{"id":27304092,"url":"https://github.com/ispyhumanfly/scribe","last_synced_at":"2025-04-12T03:14:52.285Z","repository":{"id":33225038,"uuid":"136612523","full_name":"ispyhumanfly/scribe","owner":"ispyhumanfly","description":"Scribe is a data modeling service built around a component-based architecture paradigm, designed to bring structure, reusability, and modularity to backend data systems in the same way that modern frontend frameworks like React or Vue do for UI.","archived":false,"fork":false,"pushed_at":"2025-03-30T01:56:53.000Z","size":1074,"stargazers_count":2,"open_issues_count":8,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-12T03:14:45.904Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ispyhumanfly.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-08T11:52:28.000Z","updated_at":"2025-03-30T01:57:02.000Z","dependencies_parsed_at":"2024-03-06T02:47:44.086Z","dependency_job_id":"566b6bb7-5203-42b4-9033-a7bc9748c99e","html_url":"https://github.com/ispyhumanfly/scribe","commit_stats":{"total_commits":278,"total_committers":4,"mean_commits":69.5,"dds":0.08273381294964033,"last_synced_commit":"0e401841e37e068a83e7ede765b40dc53b16b346"},"previous_names":["ispyhumanfly/scribe","flypapertech/scribe"],"tags_count":72,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ispyhumanfly%2Fscribe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ispyhumanfly%2Fscribe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ispyhumanfly%2Fscribe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ispyhumanfly%2Fscribe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ispyhumanfly","download_url":"https://codeload.github.com/ispyhumanfly/scribe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248510001,"owners_count":21116130,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-12T03:14:51.500Z","updated_at":"2025-04-12T03:14:52.275Z","avatar_url":"https://github.com/ispyhumanfly.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CircleCI](https://circleci.com/gh/ispyhumanfly/scribe/tree/master.svg?style=svg)](https://circleci.com/gh/ispyhumanfly/scribe/tree/master)\n[![Known Vulnerabilities](https://snyk.io/test/github/ispyhumanfly/scribe/badge.svg)](https://snyk.io/test/github/ispyhumanfly/scribe)\n[![npm (scoped)](https://img.shields.io/npm/v/@spytech/scribe.svg)](https://www.npmjs.com/package/@spytech/scribe)\n[![NpmLicense](https://img.shields.io/npm/l/@spytech/scribe.svg)](https://github.com/ispyhumanfly/scribe/blob/master/LICENSE)\n\n# Scribe\n\nScribe is a data modeling service built around a component-based architecture paradigm, designed to bring structure, reusability, and modularity to backend data systems in the same way that modern frontend frameworks like React or Vue do for UI. While it exposes a RESTful API interface, Scribe is more than just an API server—it’s a flexible framework for modeling domain logic as discrete components and subcomponents, each defined by JSON Schema. These components encapsulate their own validation rules, version history, and relational structure, enabling composable data models that can scale with application complexity. With built-in support for PostgreSQL, Redis caching, dynamic querying, time-based data introspection, and SQL passthroughs, Scribe offers a powerful foundation for building maintainable, schema-driven backends that are easy to reason about and evolve over time.\n\n## Project Structure\n\nScribe assumes your application follows a component-based directory structure:\n\n```\nmy-app/\n├── components/           # Application components\n│   ├── Users/          # User component directory\n│   │   ├── Users.schema.json    # Component schema definition\n│   │   └── Users.backend.ts     # Backend routes and logic\n│   ├── Products/       # Product component directory\n│   │   ├── Products.schema.json # Component schema definition\n│   │   └── Products.backend.ts  # Backend routes and logic\n│   └── Orders/         # Order component directory\n│       ├── Orders.schema.json   # Component schema definition\n│       └── Orders.backend.ts    # Backend routes and logic\n├── assets/             # Static assets\n└── dist/              # Production build output\n```\n\n## How Data Components Work\n\nIn Scribe, a component represents a distinct data model with its own schema, validation rules, and history tracking. Components are defined using JSON Schema definitions, providing a powerful and flexible way to validate and structure your data on a per component basis.\n\n### Component Schema Definition\n\nEach component in Scribe is defined by a JSON Schema definition that specifies:\n\n-   Required fields\n-   Data types\n-   Validation rules\n-   Default values\n-   Custom formats\n\nFor example, a Users component and its Profile subcomponent might be defined as:\n\n```json\n// components/Users/Users.schema.json\n{\n    \"$schema\": \"http://json-schema.org/draft-07/schema\",\n    \"type\": \"object\",\n    \"required\": [\"name\", \"email\", \"status\"],\n    \"properties\": {\n        \"name\": {\n            \"type\": \"string\",\n            \"minLength\": 2,\n            \"maxLength\": 100\n        },\n        \"email\": {\n            \"type\": \"string\",\n            \"format\": \"email\"\n        },\n        \"status\": {\n            \"type\": \"string\",\n            \"enum\": [\"active\", \"inactive\", \"suspended\"]\n        },\n        \"role\": {\n            \"type\": \"string\",\n            \"enum\": [\"admin\", \"user\", \"guest\"]\n        },\n        \"last_login\": {\n            \"type\": \"string\",\n            \"format\": \"date-time\"\n        }\n    }\n}\n\n// components/Users/Profile/Profile.schema.json\n{\n    \"$schema\": \"http://json-schema.org/draft-07/schema\",\n    \"type\": \"object\",\n    \"required\": [\"user_id\", \"avatar_url\"],\n    \"properties\": {\n        \"user_id\": {\n            \"type\": \"integer\",\n            \"description\": \"Reference to the parent Users component\"\n        },\n        \"avatar_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n        },\n        \"bio\": {\n            \"type\": \"string\",\n            \"maxLength\": 500\n        },\n        \"location\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"city\": {\n                    \"type\": \"string\"\n                },\n                \"country\": {\n                    \"type\": \"string\"\n                },\n                \"timezone\": {\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"preferences\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"theme\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"light\", \"dark\", \"system\"]\n                },\n                \"notifications\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"email\": {\n                            \"type\": \"boolean\"\n                        },\n                        \"push\": {\n                            \"type\": \"boolean\"\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\nWhen creating records, the data is automatically wrapped in the default schema structure:\n\n```typescript\n// Creating a user\nPOST /users\n{\n    \"data\": {\n        \"name\": \"John Doe\",\n        \"email\": \"john@example.com\",\n        \"status\": \"active\",\n        \"role\": \"user\"\n    }\n}\n\n// Creating a user profile\nPOST /users/profile\n{\n    \"data\": {\n        \"user_id\": 1,\n        \"avatar_url\": \"https://example.com/avatars/john.jpg\",\n        \"bio\": \"Software engineer and coffee enthusiast\",\n        \"location\": {\n            \"city\": \"San Francisco\",\n            \"country\": \"USA\",\n            \"timezone\": \"America/Los_Angeles\"\n        },\n        \"preferences\": {\n            \"theme\": \"dark\",\n            \"notifications\": {\n                \"email\": true,\n                \"push\": false\n            }\n        }\n    }\n}\n```\n\nComponents can be:\n\n-   **Base Components**: Like `Users` or `Products`\n-   **Subcomponents**: Extensions of base components like `Users/Profile` or `Products/Inventory`\n-   **Related**: Through parent-child relationships or references\n\nFor example, an e-commerce system might be modeled as:\n\n```json\n// components/Products/Products.schema.json\n{\n    \"$schema\": \"http://json-schema.org/draft-07/schema\",\n    \"type\": \"object\",\n    \"required\": [\"name\", \"price\", \"category\"],\n    \"properties\": {\n        \"name\": {\n            \"type\": \"string\",\n            \"minLength\": 3,\n            \"maxLength\": 100\n        },\n        \"price\": {\n            \"type\": \"number\",\n            \"minimum\": 0\n        },\n        \"category\": {\n            \"type\": \"string\",\n            \"enum\": [\"electronics\", \"clothing\", \"books\"]\n        },\n        \"description\": {\n            \"type\": \"string\",\n            \"maxLength\": 1000\n        },\n        \"tags\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"string\"\n            }\n        }\n    }\n}\n\n// components/Products/Inventory/Inventory.schema.json\n{\n    \"$schema\": \"http://json-schema.org/draft-07/schema\",\n    \"type\": \"object\",\n    \"required\": [\"product_id\", \"sku\", \"stock_level\"],\n    \"properties\": {\n        \"product_id\": {\n            \"type\": \"integer\",\n            \"description\": \"Reference to the parent Product component\"\n        },\n        \"sku\": {\n            \"type\": \"string\",\n            \"pattern\": \"^[A-Z0-9-]+$\"\n        },\n        \"stock_level\": {\n            \"type\": \"integer\",\n            \"minimum\": 0\n        },\n        \"warehouse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"location\": {\n                    \"type\": \"string\"\n                },\n                \"section\": {\n                    \"type\": \"string\"\n                },\n                \"bin\": {\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"reorder_point\": {\n            \"type\": \"integer\",\n            \"minimum\": 0\n        }\n    }\n}\n```\n\nEach component and subcomponent automatically gets:\n\n-   Schema validation\n-   Version history tracking\n-   Relationship querying\n-   Time machine capabilities\n\nThis component-based approach makes it natural to:\n\n-   Organize complex data models\n-   Maintain data integrity\n-   Track changes over time\n-   Scale your data architecture\n\n### Default Schema Fields\n\nEvery component in Scribe automatically includes these base fields:\n\n-   `data`: The main component data object\n-   `date_created`: Timestamp of creation\n-   `date_modified`: Timestamp of last modification\n-   `created_by`: ID of the user who created the record\n-   `modified_by`: ID of the user who last modified the record\n\n## Features\n\n-   **Schema Validation**: Automatic validation of data against JSON schemas\n-   **History Tracking**: Built-in version history for all records\n-   **Redis Caching**: Schema caching for improved performance\n-   **PostgreSQL Storage**: Reliable and scalable data storage\n-   **Complex Queries**: Support for filtering, grouping, and relationships\n-   **Time Machine**: Ability to view data as it existed at any point in time\n-   **Multi-language Support**: Easy to use from any programming language\n-   **Flexible SQL Queries**: Support for complex SQL operations including joins, aggregations, and subqueries\n-   **Query Parameter Support**: Easy filtering, sorting, and pagination through URL parameters\n-   **Transaction Support**: Atomic operations for data integrity\n-   **Dynamic Query Building**: API for constructing complex queries programmatically\n-   **Raw SQL Access**: Direct SQL execution for advanced use cases\n\n## Installation\n\n### Prerequisites\n\n-   Node.js \u003e= 12\n-   PostgreSQL \u003e= 9.6.10\n-   Redis (optional, for schema caching)\n\n```bash\nnpm install @spytech/scribe\n```\n\n## Configuration\n\nScribe can be configured through environment variables or command line arguments:\n\n```bash\n# Required\nSCRIBE_APP_DB_HOST=localhost\nSCRIBE_APP_DB_USER=your_user\nSCRIBE_APP_DB_PASS=your_password\nSCRIBE_APP_DB_NAME=your_database\n\n# Optional\nSCRIBE_APP_PORT=1337\nSCRIBE_APP_MODE=development\nSCRIBE_APP_SCHEMA_BASE_URL=http://your-schema-server\n```\n\n## Usage Examples\n\n### TypeScript with Axios\n\n```typescript\nimport axios from \"axios\"\n\nconst scribeClient = axios.create({\n    baseURL: \"http://localhost:1337\"\n})\n\n// Create a new record\nconst createUser = async () =\u003e {\n    const response = await scribeClient.post(\"/users\", {\n        name: \"John Doe\",\n        email: \"john@example.com\",\n        age: 30\n    })\n    return response.data\n}\n\n// Get a record by ID\nconst getUser = async (id: string) =\u003e {\n    const response = await scribeClient.get(`/users/${id}`)\n    return response.data\n}\n\n// Get all records with filtering\nconst getUsers = async () =\u003e {\n    const response = await scribeClient.get(\"/users/all\", {\n        params: {\n            filter: JSON.stringify({\n                age: [25, 30, 35]\n            })\n        }\n    })\n    return response.data\n}\n\n// Update a record\nconst updateUser = async (id: string, data: any) =\u003e {\n    const response = await scribeClient.put(`/users/${id}`, data)\n    return response.data\n}\n\n// Get history of a record\nconst getUserHistory = async (id: string) =\u003e {\n    const response = await scribeClient.get(`/users/${id}/history`)\n    return response.data\n}\n```\n\n### Go with net/http\n\n```go\npackage main\n\nimport (\n    \"bytes\"\n    \"encoding/json\"\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n)\n\ntype User struct {\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n    Age   int    `json:\"age\"`\n}\n\nfunc createUser(user User) (*User, error) {\n    data, err := json.Marshal(user)\n    if err != nil {\n        return nil, err\n    }\n\n    resp, err := http.Post(\"http://localhost:1337/users\", \"application/json\", bytes.NewBuffer(data))\n    if err != nil {\n        return nil, err\n    }\n    defer resp.Body.Close()\n\n    var result User\n    if err := json.NewDecoder(resp.Body).Decode(\u0026result); err != nil {\n        return nil, err\n    }\n\n    return \u0026result, nil\n}\n\nfunc getUser(id string) (*User, error) {\n    resp, err := http.Get(fmt.Sprintf(\"http://localhost:1337/users/%s\", id))\n    if err != nil {\n        return nil, err\n    }\n    defer resp.Body.Close()\n\n    var result []User\n    if err := json.NewDecoder(resp.Body).Decode(\u0026result); err != nil {\n        return nil, err\n    }\n\n    if len(result) == 0 {\n        return nil, fmt.Errorf(\"user not found\")\n    }\n\n    return \u0026result[0], nil\n}\n\nfunc getUsers() ([]User, error) {\n    filter := map[string][]int{\"age\": {25, 30, 35}}\n    filterJSON, err := json.Marshal(filter)\n    if err != nil {\n        return nil, err\n    }\n\n    resp, err := http.Get(fmt.Sprintf(\"http://localhost:1337/users/all?filter=%s\", string(filterJSON)))\n    if err != nil {\n        return nil, err\n    }\n    defer resp.Body.Close()\n\n    var result []User\n    if err := json.NewDecoder(resp.Body).Decode(\u0026result); err != nil {\n        return nil, err\n    }\n\n    return result, nil\n}\n```\n\n### Python with requests\n\n```python\nimport requests\nimport json\n\nclass ScribeClient:\n    def __init__(self, base_url=\"http://localhost:1337\"):\n        self.base_url = base_url\n\n    def create_user(self, user_data):\n        response = requests.post(\n            f\"{self.base_url}/users\",\n            json=user_data\n        )\n        return response.json()\n\n    def get_user(self, user_id):\n        response = requests.get(f\"{self.base_url}/users/{user_id}\")\n        return response.json()\n\n    def get_users(self, filter_data=None):\n        params = {}\n        if filter_data:\n            params['filter'] = json.dumps(filter_data)\n\n        response = requests.get(\n            f\"{self.base_url}/users/all\",\n            params=params\n        )\n        return response.json()\n\n    def update_user(self, user_id, user_data):\n        response = requests.put(\n            f\"{self.base_url}/users/{user_id}\",\n            json=user_data\n        )\n        return response.json()\n\n    def get_user_history(self, user_id):\n        response = requests.get(f\"{self.base_url}/users/{user_id}/history\")\n        return response.json()\n\n# Usage example\nclient = ScribeClient()\n\n# Create a user\nuser = client.create_user({\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\",\n    \"age\": 30\n})\n\n# Get user by ID\nuser_data = client.get_user(user[0][\"id\"])\n\n# Get users with filter\nusers = client.get_users({\"age\": [25, 30, 35]})\n\n# Update user\nupdated_user = client.update_user(user[0][\"id\"], {\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\",\n    \"age\": 31\n})\n\n# Get user history\nhistory = client.get_user_history(user[0][\"id\"])\n```\n\n## Advanced Features\n\n### Time Machine\n\nScribe supports viewing data as it existed at any point in time:\n\n```typescript\n// Get data as it existed at a specific timestamp\nconst getHistoricalData = async (id: string, timestamp: string) =\u003e {\n    const response = await scribeClient.get(`/users/${id}`, {\n        params: {\n            timeMachine: JSON.stringify({\n                key: \"updatedAt\",\n                timestamp: timestamp\n            })\n        }\n    })\n    return response.data\n}\n```\n\n### Relationships\n\nScribe supports querying related data:\n\n```typescript\n// Get parent records\nconst getParentRecords = async (id: string) =\u003e {\n    const response = await scribeClient.get(`/users/${id}`, {\n        params: {\n            parents: \"parentId\"\n        }\n    })\n    return response.data\n}\n\n// Get child records\nconst getChildRecords = async (id: string) =\u003e {\n    const response = await scribeClient.get(`/users/${id}`, {\n        params: {\n            children: \"parentId\"\n        }\n    })\n    return response.data\n}\n```\n\n### SQL Queries\n\nScribe provides a direct SQL endpoint for advanced querying capabilities. All queries are executed against PostgreSQL 9.x and above:\n\n```typescript\n// Execute a custom SQL query\nconst executeSqlQuery = async (query: string) =\u003e {\n    const response = await scribeClient.post(\"/sql\", {\n        query: query\n    })\n    return response.data\n}\n\n// Example: Basic JOIN query (compatible with all PostgreSQL versions)\nconst getUsersWithOrders = async () =\u003e {\n    const query = `\n        SELECT u.*, o.*\n        FROM users u\n        LEFT JOIN orders o ON u.id = o.user_id\n        WHERE o.status = 'completed'\n        ORDER BY u.created_at DESC\n    `\n    return executeSqlQuery(query)\n}\n\n// Example: Aggregation with GROUP BY (compatible with all PostgreSQL versions)\nconst getOrderStats = async () =\u003e {\n    const query = `\n        SELECT \n            user_id,\n            COUNT(*) as total_orders,\n            SUM(amount) as total_amount,\n            AVG(amount) as average_order_value\n        FROM orders\n        WHERE status = 'completed'\n        GROUP BY user_id\n        HAVING COUNT(*) \u003e 5\n        ORDER BY total_amount DESC\n    `\n    return executeSqlQuery(query)\n}\n\n// Example: Window functions (available in PostgreSQL 9.0+)\nconst getTopCustomers = async () =\u003e {\n    const query = `\n        SELECT \n            u.name,\n            u.email,\n            COUNT(o.id) as order_count,\n            SUM(o.amount) as total_spent,\n            ROW_NUMBER() OVER (ORDER BY SUM(o.amount) DESC) as rank\n        FROM users u\n        JOIN orders o ON u.id = o.user_id\n        WHERE o.status = 'completed'\n        GROUP BY u.id, u.name, u.email\n        ORDER BY total_spent DESC\n        LIMIT 10\n    `\n    return executeSqlQuery(query)\n}\n\n// Example: Common Table Expression (CTE) with recursive query (available in PostgreSQL 8.4+)\nconst getOrderHierarchy = async (orderId: string) =\u003e {\n    const query = `\n        WITH RECURSIVE order_tree AS (\n            -- Base case: get the initial order\n            SELECT \n                id,\n                parent_order_id,\n                amount,\n                1 as level\n            FROM orders\n            WHERE id = $1\n            \n            UNION ALL\n            \n            -- Recursive case: get child orders\n            SELECT \n                o.id,\n                o.parent_order_id,\n                o.amount,\n                ot.level + 1\n            FROM orders o\n            JOIN order_tree ot ON o.parent_order_id = ot.id\n        )\n        SELECT * FROM order_tree\n        ORDER BY level, id\n    `\n    return executeSqlQuery(query)\n}\n\n// Example: JSON operations (available in PostgreSQL 9.2+)\nconst getProductDetails = async () =\u003e {\n    const query = `\n        SELECT \n            p.id,\n            p.name,\n            p.price,\n            p.metadata-\u003e\u003e'category' as category,\n            p.metadata-\u003e\u003e'brand' as brand,\n            (p.metadata-\u003e\u003e'rating')::float as rating\n        FROM products p\n        WHERE p.metadata-\u003e\u003e'category' = 'electronics'\n        AND (p.metadata-\u003e\u003e'rating')::float \u003e= 4.5\n        ORDER BY rating DESC\n    `\n    return executeSqlQuery(query)\n}\n\n// Example: Date/Time operations (compatible with all PostgreSQL versions)\nconst getRecentActivity = async () =\u003e {\n    const query = `\n        SELECT \n            u.name,\n            o.id as order_id,\n            o.created_at,\n            EXTRACT(EPOCH FROM (o.created_at - NOW())) / 3600 as hours_ago\n        FROM users u\n        JOIN orders o ON u.id = o.user_id\n        WHERE o.created_at \u003e= NOW() - INTERVAL '7 days'\n        ORDER BY o.created_at DESC\n    `\n    return executeSqlQuery(query)\n}\n```\n\n\u003e **Note**: The SQL endpoint should only be used in trusted environments as it provides direct database access. Make sure to:\n\u003e\n\u003e -   Properly validate and sanitize any user input before using it in queries\n\u003e -   Use parameterized queries to prevent SQL injection\n\u003e -   Consider query performance and add appropriate indexes\n\u003e -   Test queries against your specific PostgreSQL version\n\n## API Endpoints\n\n-   `POST /:component` - Create a new record\n-   `GET /:component/:id` - Get a record by ID\n-   `GET /:component/all` - Get all records\n-   `PUT /:component/:id` - Update a record\n-   `DELETE /:component/:id` - Delete a record\n-   `GET /:component/:id/history` - Get record history\n-   `DELETE /:component/all` - Delete all records\n-   `DELETE /:component` - Drop the component table\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fispyhumanfly%2Fscribe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fispyhumanfly%2Fscribe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fispyhumanfly%2Fscribe/lists"}