{"id":48161590,"url":"https://github.com/seandearnaley/brainstrike-typescript-starter","last_synced_at":"2026-04-04T17:25:35.973Z","repository":{"id":37175706,"uuid":"225090788","full_name":"seandearnaley/brainstrike-typescript-starter","owner":"seandearnaley","description":"Full stack starter kit for TypeScript(Node.js + React.js) + Apollo GraphQL w/ Apollo Client 3 + TypeORM","archived":false,"fork":false,"pushed_at":"2025-06-06T14:27:05.000Z","size":9697,"stargazers_count":45,"open_issues_count":1,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-06T15:29:57.161Z","etag":null,"topics":["apollo-client","apollo-server","graphql","react","typeorm","typescript"],"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/seandearnaley.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null}},"created_at":"2019-12-01T01:18:08.000Z","updated_at":"2025-06-06T14:27:06.000Z","dependencies_parsed_at":"2025-02-24T23:29:31.580Z","dependency_job_id":null,"html_url":"https://github.com/seandearnaley/brainstrike-typescript-starter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seandearnaley/brainstrike-typescript-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seandearnaley%2Fbrainstrike-typescript-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seandearnaley%2Fbrainstrike-typescript-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seandearnaley%2Fbrainstrike-typescript-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seandearnaley%2Fbrainstrike-typescript-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seandearnaley","download_url":"https://codeload.github.com/seandearnaley/brainstrike-typescript-starter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seandearnaley%2Fbrainstrike-typescript-starter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31407644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["apollo-client","apollo-server","graphql","react","typeorm","typescript"],"created_at":"2026-04-04T17:25:35.850Z","updated_at":"2026-04-04T17:25:35.949Z","avatar_url":"https://github.com/seandearnaley.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# brainstrike-typescript-starter\n\n![Node.js Server CI](https://github.com/seandearnaley/brainstrike-typescript-starter/actions/workflows/nodejs-server-test.yml/badge.svg)\n![Node.js Client CI](https://github.com/seandearnaley/brainstrike-typescript-starter/actions/workflows/nodejs-client-test.yml/badge.svg)\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"logo.png\" alt=\"Brainstrike Logo\" width=\"400\"\u003e\n\u003c/div\u003e\n\nStarter kit for Node.js + Typescript + React.js + Apollo GraphQL + TypeORM\n\n![Brainstrike Client Screenshot](screenshot-client.png)\n\n## Features\n\n- Typescript Node + React + pnpm\n- Apollo GraphQL (apollo-client 3.0)\n- TypeORM w/ testing connections (Docker-Compose Postgres + GitHub Actions)\n- GraphQL Code Generator\n- Material UI\n- Unit/Integration/E2E tests\n- React client with Hooks\n- Prettier + ESLint configuration\n\nNOTE: VS Code settings for ESLint+Prettier (consequence of mono repo structure)\n\n\"eslint.workingDirectories\": [ \"./client\", \"./server\" ]\n\n## Prerequisites\n\nRequires Node.js 18.19 or higher, Postgres 11+ required for database. Docker-compose provided for Postgres. Should be easy to adapt examples to other databases... may update database support in future versions.\n\n## Folders\n\nThis is setup like a mono-repo with seperate folders for clients and server, each with their own package and config. You could set these up in their own repos, switch to each folder to start the respective packages.\n\n- client - Vite-based React 19 application with Material UI, Apollo Client 3, and GraphQL Code Generator for type-safe hooks plus modern tooling like Vitest.\n\n- server - Apollo Server using [GraphQL Code Generator](https://github.com/dotansimha/graphql-code-generator) for resolvers + types. Using TypeORM for database access, working examples of relay style cursor pagination, unit, integration and e2e tests.\n\n# Recent Updates (as of January 2025)\n\n**BREAKING CHANGE: Migrated from Yarn to pnpm workspaces**\n\n- **Migration to pnpm v9.6.0**: Switched from Yarn to pnpm for better performance, disk space efficiency, and modern workspace features\n- **Updated GitHub Actions**: All CI/CD workflows now use pnpm with proper workspace support and PostgreSQL services\n- **Modern TypeScript 5.8.2**: Implemented catalog dependencies across workspaces for consistent versioning\n- **Enhanced Scripts**: Added comprehensive development scripts including `dev`, `clean`, `typecheck`, and proper workspace filtering\n- **Improved Configuration**: Added modern `.npmrc` with optimal pnpm settings for monorepo development\n- **Removed Yarn Artifacts**: Cleaned up all Yarn-specific files (.yarnrc.yml, .yarn directories, lock files)\n\n## Getting Started with pnpm\n\n1. Install pnpm globally: `npm install -g pnpm` or use Corepack: `corepack enable pnpm`\n2. Install dependencies: `pnpm install`\n3. Start development: `pnpm dev` (runs both client and server)\n4. Build all packages: `pnpm build`\n5. Run tests: `pnpm test`\n\n## Workspace Commands\n\n- `pnpm dev:client` - Start only the React client\n- `pnpm dev:server` - Start only the Node.js server\n- `pnpm clean` - Clean all node_modules and build artifacts\n- `pnpm typecheck` - Run TypeScript type checking across all packages\n\n## API Documentation\n\nThis section provides details about the available GraphQL queries and mutations, based on the schema in `server/src/graphql/`.\n\n### Standard Types\n\n*   **`Node` Interface**: Represents an object with an `id`, `created`, and `updated` timestamp. Implemented by `Card` and `Category`.\n    *   `id: ID!`\n    *   `created: DateTime!`\n    *   `updated: DateTime`\n*   **`PageInfo` Type**: Provides information about pagination.\n    *   `hasNextPage: Boolean!`\n    *   `hasPreviousPage: Boolean!`\n    *   `startCursor: String`\n    *   `endCursor: String`\n    *   `totalCount: Int!`\n*   **`DirectionEnum` Enum**: Specifies the direction for ordering.\n    *   `ASC` (Ascending)\n    *   `DESC` (Descending)\n*   **Scalar Types**:\n    *   `Date`: Represents a date.\n    *   `DateTime`: Represents a date and time.\n    *   `Time`: Represents a time.\n\n### Queries\n\n#### 1. `cards`\n\n*   **Description**: Retrieves a paginated list of cards. Supports filtering by category and ordering.\n*   **Parameters**:\n    *   `first` (Int): Returns the first `n` cards.\n    *   `last` (Int): Returns the last `n` cards (requires `before`).\n    *   `after` (String): Returns cards after the specified cursor.\n    *   `before` (String): Returns cards before the specified cursor.\n    *   `orderByColumn` (String): Column to order by (e.g., \"label\", \"created\"). Defaults to \"id\" if not specified or invalid.\n    *   `orderByDirection` (DirectionEnum): Direction of ordering (`ASC` or `DESC`). Defaults to `ASC`.\n    *   `categoryId` (ID): Filters cards by a specific category ID.\n*   **Returns**: `CardConnection!`: An object containing a list of cards (`edges`) and pagination information (`pageInfo`).\n    *   `CardConnection`:\n        *   `pageInfo: PageInfo!`\n        *   `edges: [CardEdge!]!`\n            *   `CardEdge`:\n                *   `cursor: String!`\n                *   `node: Card!` (see `Card` type below)\n*   **Example**: Get the first 10 cards, ordered by label descending.\n\n```graphql\nquery GetFirstTenCardsOrdered {\n  cards(first: 10, orderByColumn: \"label\", orderByDirection: DESC) {\n    pageInfo {\n      hasNextPage\n      endCursor\n      totalCount\n    }\n    edges {\n      cursor\n      node {\n        id\n        number\n        label\n        description\n        created\n        categories {\n          id\n          name\n        }\n      }\n    }\n  }\n}\n```\n\n#### 2. `card`\n\n*   **Description**: Retrieves a single card by its unique ID.\n*   **Parameters**:\n    *   `id` (ID!): The unique identifier of the card.\n*   **Returns**: `Card!`: The card object if found.\n    *   `Card` (implements `Node`):\n        *   `id: ID!`\n        *   `number: Int`\n        *   `label: String`\n        *   `description: String`\n        *   `created: DateTime!`\n        *   `updated: DateTime`\n        *   `categories: [Category!]!` (List of categories associated with the card)\n*   **Example**:\n\n```graphql\nquery GetCardById($cardId: ID!) {\n  card(id: $cardId) {\n    id\n    number\n    label\n    description\n    created\n    updated\n    categories {\n      id\n      name\n    }\n  }\n}\n```\n\n#### 3. `categories`\n\n*   **Description**: Retrieves a list of categories. Supports filtering by card IDs and ordering.\n*   **Parameters**:\n    *   `cardIds` (String): A comma-separated string of card IDs to filter categories that are associated with these cards.\n    *   `orderByColumn` (String): Column to order by (e.g., \"name\", \"created\"). Defaults to \"id\" if not specified or invalid.\n    *   `orderByDirection` (DirectionEnum): Direction of ordering (`ASC` or `DESC`). Defaults to `ASC`.\n*   **Returns**: `[Category!]!`: A list of category objects.\n    *   `Category` (implements `Node`):\n        *   `id: ID!`\n        *   `name: String`\n        *   `created: DateTime!`\n        *   `updated: DateTime`\n        *   `cards(...)`: A connection to retrieve cards associated with this category (supports same pagination/ordering as the top-level `cards` query).\n*   **Example**: Get all categories, ordered by name.\n\n```graphql\nquery GetAllCategoriesOrdered {\n  categories(orderByColumn: \"name\", orderByDirection: ASC) {\n    id\n    name\n    created\n    # Example of fetching cards for each category (first 2)\n    cards(first: 2) {\n      edges {\n        node {\n          id\n          label\n        }\n      }\n      totalCount\n    }\n  }\n}\n```\n\n#### 4. `category`\n\n*   **Description**: Retrieves a single category by its unique ID.\n*   **Parameters**:\n    *   `id` (ID!): The unique identifier of the category.\n*   **Returns**: `Category!`: The category object if found. (See `Category` type definition above).\n*   **Example**:\n\n```graphql\nquery GetCategoryById($categoryId: ID!) {\n  category(id: $categoryId) {\n    id\n    name\n    created\n    updated\n    cards { # Fetch all cards in this category\n      edges {\n        node {\n          id\n          label\n        }\n      }\n    }\n  }\n}\n```\n\n#### 5. `node`\n\n*   **Description**: Retrieves any object that implements the `Node` interface by its global ID. This can be a `Card` or a `Category`.\n*   **Parameters**:\n    *   `id` (ID!): The global unique identifier of the node.\n*   **Returns**: `Node!`: The node object if found. You can use inline fragments to get specific fields based on the type.\n*   **Example**:\n\n```graphql\nquery GetNode($nodeId: ID!) {\n  node(id: $nodeId) {\n    id\n    created\n    updated\n    ... on Card {\n      label\n      description\n      number\n      categories {\n        id\n        name\n      }\n    }\n    ... on Category {\n      name\n      cards {\n        totalCount\n      }\n    }\n  }\n}\n```\n\n### Mutations\n\n#### Card Mutations\n\n##### 1. `addCard`\n\n*   **Description**: Adds a new card.\n*   **Parameters**:\n    *   `input` (CardInput!): The details for the new card.\n        *   `CardInput`:\n            *   `number` (Int)\n            *   `label` (String)\n            *   `description` (String)\n            *   `categoryId` (ID): Optional ID of the category to associate with this card.\n*   **Returns**: `CardsUpdatedResponse!`:\n    *   `CardsUpdatedResponse`:\n        *   `success: Boolean!`\n        *   `message: String!`\n        *   `card: Card!` (The newly created card)\n*   **Example**:\n\n```graphql\nmutation AddNewCard($newCard: CardInput!) {\n  addCard(input: $newCard) {\n    success\n    message\n    card {\n      id\n      label\n      number\n      description\n      created\n      categories {\n        id\n        name\n      }\n    }\n  }\n}\n\n# Example variables for the above mutation:\n# {\n#   \"newCard\": {\n#     \"label\": \"New Task Card\",\n#     \"description\": \"Details about the new task.\",\n#     \"number\": 101,\n#     \"categoryId\": \"some-category-id\" # Optional\n#   }\n# }\n```\n\n##### 2. `updateCard`\n\n*   **Description**: Updates an existing card by its ID.\n*   **Parameters**:\n    *   `id` (ID!): The ID of the card to update.\n    *   `input` (CardInput!): The new details for the card. Fields in `CardInput` are optional for updates.\n        *   `CardInput`: (Same as `addCard`)\n*   **Returns**: `CardsUpdatedResponse!`: (Same as `addCard`, but `card` is the updated card)\n*   **Example**:\n\n```graphql\nmutation ModifyCard($cardId: ID!, $updatedData: CardInput!) {\n  updateCard(id: $cardId, input: $updatedData) {\n    success\n    message\n    card {\n      id\n      label\n      description\n      updated\n    }\n  }\n}\n\n# Example variables for the above mutation:\n# {\n#   \"cardId\": \"existing-card-id\",\n#   \"updatedData\": {\n#     \"label\": \"Updated Task Card Label\"\n#   }\n# }\n```\n\n##### 3. `removeCard`\n\n*   **Description**: Removes a card by its ID.\n*   **Parameters**:\n    *   `id` (ID!): The ID of the card to remove.\n*   **Returns**: `CardsUpdatedResponse!`: (Same as `addCard`, `card` will be the removed card details)\n*   **Example**:\n\n```graphql\nmutation DeleteCard($cardId: ID!) {\n  removeCard(id: $cardId) {\n    success\n    message\n    card { # Details of the card that was removed\n      id\n      label\n    }\n  }\n}\n```\n\n#### Category Mutations\n\n##### 1. `addCategory`\n\n*   **Description**: Adds a new category.\n*   **Parameters**:\n    *   `input` (CategoryInput!): The details for the new category.\n        *   `CategoryInput`:\n            *   `name` (String)\n*   **Returns**: `CategoryUpdatedResponse!`:\n    *   `CategoryUpdatedResponse`:\n        *   `success: Boolean!`\n        *   `message: String!`\n        *   `category: Category!` (The newly created category)\n*   **Example**:\n\n```graphql\nmutation AddNewCategory($newCategory: CategoryInput!) {\n  addCategory(input: $newCategory) {\n    success\n    message\n    category {\n      id\n      name\n      created\n    }\n  }\n}\n\n# Example variables for the above mutation:\n# {\n#   \"newCategory\": {\n#     \"name\": \"Project Alpha\"\n#   }\n# }\n```\n\n##### 2. `updateCategory`\n\n*   **Description**: Updates an existing category by its ID.\n*   **Parameters**:\n    *   `id` (ID!): The ID of the category to update.\n    *   `input` (CategoryInput!): The new details for the category.\n        *   `CategoryInput`: (Same as `addCategory`)\n*   **Returns**: `CategoryUpdatedResponse!`: (Same as `addCategory`, but `category` is the updated category)\n*   **Example**:\n\n```graphql\nmutation ModifyCategory($categoryId: ID!, $updatedData: CategoryInput!) {\n  updateCategory(id: $categoryId, input: $updatedData) {\n    success\n    message\n    category {\n      id\n      name\n      updated\n    }\n  }\n}\n\n# Example variables for the above mutation:\n# {\n#   \"categoryId\": \"existing-category-id\",\n#   \"updatedData\": {\n#     \"name\": \"Project Beta Features\"\n#   }\n# }\n```\n\n##### 3. `removeCategory`\n\n*   **Description**: Removes a category by its ID.\n*   **Parameters**:\n    *   `id` (ID!): The ID of the category to remove.\n*   **Returns**: `CategoryUpdatedResponse!`: (Same as `addCategory`, `category` will be the removed category details)\n*   **Example**:\n\n```graphql\nmutation DeleteCategory($categoryId: ID!) {\n  removeCategory(id: $categoryId) {\n    success\n    message\n    category { # Details of the category that was removed\n      id\n      name\n    }\n  }\n}\n```\n\n## 🏗️ System Architecture\n\nThe Brainstrike server follows a modern, layered architecture designed for scalability, maintainability, and type safety:\n\n```mermaid\ngraph TB\n    %% External Layer\n    Client[\"🌐 Client Applications\u003cbr/\u003e(React/Next.js)\"]\n    Studio[\"🛠️ Apollo Studio\u003cbr/\u003e(GraphQL Playground)\"]\n\n    %% API Gateway Layer\n    subgraph \"🚀 API Layer\"\n        Express[\"⚡ Express Server\u003cbr/\u003e(Port 4000)\"]\n        CORS[\"🔒 CORS Middleware\u003cbr/\u003e(Security)\"]\n        Apollo[\"🚀 Apollo Server\u003cbr/\u003e(GraphQL Gateway)\"]\n    end\n\n    %% Business Logic Layer\n    subgraph \"🧠 Business Logic\"\n        Schema[\"📋 GraphQL Schema\u003cbr/\u003e(Type Definitions)\"]\n        Resolvers[\"⚙️ GraphQL Resolvers\u003cbr/\u003e(Query/Mutation Logic)\"]\n\n        subgraph \"📊 Data Sources\"\n            CardAPI[\"🃏 Card API\u003cbr/\u003e(Business Logic)\"]\n            CategoryAPI[\"📁 Category API\u003cbr/\u003e(Business Logic)\"]\n        end\n    end\n\n    %% Data Access Layer\n    subgraph \"💾 Data Access Layer\"\n        TypeORM[\"🔗 TypeORM\u003cbr/\u003e(ORM Framework)\"]\n\n        subgraph \"📦 Entities\"\n            CardEntity[\"🃏 Card Entity\"]\n            CategoryEntity[\"📁 Category Entity\"]\n            UserEntity[\"👤 User Entity\"]\n        end\n\n        subgraph \"🔄 Database Operations\"\n            Migrations[\"📈 Migrations\u003cbr/\u003e(Schema Evolution)\"]\n            Repositories[\"📚 Repositories\u003cbr/\u003e(Data Access)\"]\n        end\n    end\n\n    %% Database Layer\n    subgraph \"🗄️ Database Layer\"\n        PostgreSQL[\"🐘 PostgreSQL\u003cbr/\u003e(Primary Database)\"]\n        TestDB[\"🧪 Test Database\u003cbr/\u003e(brainstrike_test)\"]\n    end\n\n    %% Development Tools\n    subgraph \"🛠️ Development \u0026 Testing\"\n        Vitest[\"⚡ Vitest\u003cbr/\u003e(Unit Testing)\"]\n        Faker[\"🎭 Faker.js\u003cbr/\u003e(Test Data)\"]\n        Codegen[\"🔧 GraphQL Codegen\u003cbr/\u003e(Type Generation)\"]\n        ESLint[\"📏 ESLint + Prettier\u003cbr/\u003e(Code Quality)\"]\n    end\n\n    %% Environment \u0026 Config\n    subgraph \"⚙️ Configuration\"\n        EnvConfig[\"🌍 Environment Config\u003cbr/\u003e(.env files)\"]\n        ORMConfig[\"🔧 ORM Configuration\u003cbr/\u003e(Database Settings)\"]\n    end\n\n    %% Connections\n    Client --\u003e Express\n    Studio --\u003e Express\n\n    Express --\u003e CORS\n    CORS --\u003e Apollo\n\n    Apollo --\u003e Schema\n    Apollo --\u003e Resolvers\n\n    Resolvers --\u003e CardAPI\n    Resolvers --\u003e CategoryAPI\n\n    CardAPI --\u003e TypeORM\n    CategoryAPI --\u003e TypeORM\n\n    TypeORM --\u003e CardEntity\n    TypeORM --\u003e CategoryEntity\n    TypeORM --\u003e UserEntity\n\n    TypeORM --\u003e Repositories\n    TypeORM --\u003e Migrations\n\n    Repositories --\u003e PostgreSQL\n    Migrations --\u003e PostgreSQL\n\n    Vitest --\u003e TestDB\n    Faker --\u003e TestDB\n\n    EnvConfig --\u003e ORMConfig\n    ORMConfig --\u003e TypeORM\n\n    Codegen --\u003e Schema\n\n    %% Styling with black text\n    classDef clientLayer fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000000\n    classDef apiLayer fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000000\n    classDef businessLayer fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px,color:#000000\n    classDef dataLayer fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000000\n    classDef dbLayer fill:#fce4ec,stroke:#880e4f,stroke-width:2px,color:#000000\n    classDef devLayer fill:#f1f8e9,stroke:#33691e,stroke-width:2px,color:#000000\n    classDef configLayer fill:#e0f2f1,stroke:#004d40,stroke-width:2px,color:#000000\n\n    class Client,Studio clientLayer\n    class Express,CORS,Apollo apiLayer\n    class Schema,Resolvers,CardAPI,CategoryAPI businessLayer\n    class TypeORM,CardEntity,CategoryEntity,UserEntity,Migrations,Repositories dataLayer\n    class PostgreSQL,TestDB dbLayer\n    class Vitest,Faker,Codegen,ESLint devLayer\n    class EnvConfig,ORMConfig configLayer\n```\n\n### Architecture Highlights\n\n- **🌐 Client Layer**: React/Next.js applications and Apollo Studio for development\n- **🚀 API Layer**: Express.js with Apollo Server providing a robust GraphQL gateway\n- **🧠 Business Logic**: Clean separation with dedicated data source APIs and resolvers\n- **💾 Data Access**: TypeORM with entity models and repository patterns\n- **🗄️ Database**: PostgreSQL with separate test database for development\n- **🛠️ Development**: Comprehensive testing and code generation tools\n- **⚙️ Configuration**: Environment-based configuration management\n\nThis architecture ensures **type safety**, **scalability**, and **maintainability** while following modern best practices for GraphQL APIs.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseandearnaley%2Fbrainstrike-typescript-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseandearnaley%2Fbrainstrike-typescript-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseandearnaley%2Fbrainstrike-typescript-starter/lists"}