From deb79d11eb2740b1c85584e12694882ecfada96e Mon Sep 17 00:00:00 2001 From: raj pandey Date: Mon, 25 May 2026 00:21:07 +0530 Subject: [PATCH] feat(DX-7531): migrate contentstack-cli-tsgen v1 into cli-plugins monorepo Adds the contentstack-cli-tsgen OCLIF plugin package under packages/, wires it into CI (unit tests, integration tests, production release), and registers it in the monorepo config and README. --- .cursor/rules/README.md | 1 + .github/config/release.json | 3 +- .../workflows/release-production-plugins.yml | 8 + .github/workflows/tsgen-integration-test.yml | 52 +++ .github/workflows/unit-test.yml | 4 + .talismanrc | 8 +- README.md | 2 +- packages/contentstack-cli-tsgen/.eslintrc.js | 18 + packages/contentstack-cli-tsgen/.gitignore | 15 + packages/contentstack-cli-tsgen/AGENTS.md | 53 +++ packages/contentstack-cli-tsgen/MIGRATION.md | 124 +++++++ packages/contentstack-cli-tsgen/README.md | 135 +++++++ packages/contentstack-cli-tsgen/SECURITY.md | 27 ++ packages/contentstack-cli-tsgen/bin/dev | 5 + packages/contentstack-cli-tsgen/bin/dev.cmd | 3 + packages/contentstack-cli-tsgen/bin/dev.js | 5 + packages/contentstack-cli-tsgen/bin/run | 6 + packages/contentstack-cli-tsgen/bin/run.cmd | 3 + packages/contentstack-cli-tsgen/bin/run.js | 6 + .../contentstack-cli-tsgen/jest.config.js | 9 + packages/contentstack-cli-tsgen/package.json | 74 ++++ .../contentstack-cli-tsgen/skills/README.md | 18 + .../skills/code-review/SKILL.md | 29 ++ .../skills/dev-workflow/SKILL.md | 55 +++ .../skills/testing/SKILL.md | 32 ++ .../skills/typescript-cli-tsgen/SKILL.md | 31 ++ .../src/commands/tsgen.ts | 247 +++++++++++++ .../contentstack-cli-tsgen/src/lib/helper.ts | 91 +++++ .../contentstack-cli-tsgen/src/types/index.ts | 8 + .../integration/tsgen.integration.test.ts | 191 ++++++++++ packages/contentstack-cli-tsgen/tsconfig.json | 16 + pnpm-lock.yaml | 339 +++++++++++++++--- 32 files changed, 1569 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/tsgen-integration-test.yml create mode 100644 packages/contentstack-cli-tsgen/.eslintrc.js create mode 100644 packages/contentstack-cli-tsgen/.gitignore create mode 100644 packages/contentstack-cli-tsgen/AGENTS.md create mode 100644 packages/contentstack-cli-tsgen/MIGRATION.md create mode 100644 packages/contentstack-cli-tsgen/README.md create mode 100644 packages/contentstack-cli-tsgen/SECURITY.md create mode 100755 packages/contentstack-cli-tsgen/bin/dev create mode 100644 packages/contentstack-cli-tsgen/bin/dev.cmd create mode 100755 packages/contentstack-cli-tsgen/bin/dev.js create mode 100755 packages/contentstack-cli-tsgen/bin/run create mode 100644 packages/contentstack-cli-tsgen/bin/run.cmd create mode 100755 packages/contentstack-cli-tsgen/bin/run.js create mode 100644 packages/contentstack-cli-tsgen/jest.config.js create mode 100644 packages/contentstack-cli-tsgen/package.json create mode 100644 packages/contentstack-cli-tsgen/skills/README.md create mode 100644 packages/contentstack-cli-tsgen/skills/code-review/SKILL.md create mode 100644 packages/contentstack-cli-tsgen/skills/dev-workflow/SKILL.md create mode 100644 packages/contentstack-cli-tsgen/skills/testing/SKILL.md create mode 100644 packages/contentstack-cli-tsgen/skills/typescript-cli-tsgen/SKILL.md create mode 100644 packages/contentstack-cli-tsgen/src/commands/tsgen.ts create mode 100644 packages/contentstack-cli-tsgen/src/lib/helper.ts create mode 100644 packages/contentstack-cli-tsgen/src/types/index.ts create mode 100644 packages/contentstack-cli-tsgen/tests/integration/tsgen.integration.test.ts create mode 100644 packages/contentstack-cli-tsgen/tsconfig.json diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md index 325fa7b53..4506d95bb 100644 --- a/.cursor/rules/README.md +++ b/.cursor/rules/README.md @@ -51,6 +51,7 @@ This is a **CLI plugins** monorepo with plugin packages under `packages/`, inclu - `contentstack-seed` - Seed stacks with generated data - `contentstack-variants` - Manage content variants - `contentstack-apps-cli` - Developer Hub apps (`app:*` commands; npm `@contentstack/apps-cli`) +- `contentstack-cli-tsgen` - TypeScript typings (`csdx tsgen`; npm `contentstack-cli-tsgen`; Jest integration tests) All plugins depend on: - `@contentstack/cli-command` - Base Command class diff --git a/.github/config/release.json b/.github/config/release.json index 1ea83997c..79ff87c09 100755 --- a/.github/config/release.json +++ b/.github/config/release.json @@ -20,6 +20,7 @@ "launch": false, "branches": false, "apps-cli": false, - "core": false + "core": false, + "tsgen": false } } diff --git a/.github/workflows/release-production-plugins.yml b/.github/workflows/release-production-plugins.yml index e17f9a817..945f4a9ab 100644 --- a/.github/workflows/release-production-plugins.yml +++ b/.github/workflows/release-production-plugins.yml @@ -143,6 +143,14 @@ jobs: package: ./packages/contentstack-query-export/package.json tag: latest + # Tsgen + - name: Publishing tsgen (Production) + uses: JS-DevTools/npm-publish@v3 + with: + token: ${{ secrets.NPM_TOKEN }} + package: ./packages/contentstack-cli-tsgen/package.json + tag: latest + - name: Create Production Release id: create_release env: diff --git a/.github/workflows/tsgen-integration-test.yml b/.github/workflows/tsgen-integration-test.yml new file mode 100644 index 000000000..1d1affab1 --- /dev/null +++ b/.github/workflows/tsgen-integration-test.yml @@ -0,0 +1,52 @@ +name: Tsgen Integration Tests + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + tsgen-integration: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.28.0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build tsgen plugin + run: pnpm --filter contentstack-cli-tsgen run build + + - name: Install Contentstack CLI (v1) + run: npm i -g @contentstack/cli + + - name: Configure CLI region + run: csdx config:set:region ${{ secrets.REGION }} + + - name: Add delivery token + run: csdx auth:tokens:add -a ${{ secrets.TOKEN_ALIAS }} --delivery -k ${{ secrets.APIKEY }} --token ${{ secrets.DELIVERYKEY }} -e ${{ secrets.ENVIRONMENT }} + + - name: Link tsgen plugin + working-directory: ./packages/contentstack-cli-tsgen + run: csdx plugins:link + + - name: Run integration tests + run: pnpm --filter contentstack-cli-tsgen run test:integration + env: + TOKEN_ALIAS: ${{ secrets.TOKEN_ALIAS }} + + - name: Unlink tsgen plugin + working-directory: ./packages/contentstack-cli-tsgen + run: csdx plugins:unlink + if: always() diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 7c306cae7..895852c6d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -69,3 +69,7 @@ jobs: - name: Run tests for Contentstack Apps CLI working-directory: ./packages/contentstack-apps-cli run: npm run test:unit:report:json + + - name: Run tests for Contentstack Tsgen plugin + working-directory: ./packages/contentstack-cli-tsgen + run: npm run test diff --git a/.talismanrc b/.talismanrc index 3e8b96c8b..5a9bbf716 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,4 +1,10 @@ fileignoreconfig: - filename: pnpm-lock.yaml - checksum: 89acb731dc98c886694fa4d267c11c188000b8ecbe5ee6e4809e5bc6ec33a2f3 + checksum: a5fd5bbb109364fc980359e175fde60bd0fec3241953bb93362928de80f3f0ac + - filename: packages/contentstack-cli-tsgen/AGENTS.md + checksum: 75b4f1414b547d0bd83df5ed4fb80020acc0ed849619bed2639491b565be7a1b + - filename: packages/contentstack-cli-tsgen/src/lib/helper.ts + checksum: a7ead0030ead9d15b6b6e9623f61e7def77b00325e3988f0e3d73a145180dedc + - filename: packages/contentstack-cli-tsgen/src/commands/tsgen.ts + checksum: 054ea78f765edca62c785714cf8962df4fb91529c0851439d1ed61e963467408 version: '1.0' diff --git a/README.md b/README.md index b3660d6ba..25b340db2 100644 --- a/README.md +++ b/README.md @@ -55,4 +55,4 @@ To get a more detailed documentation for every command, visit the [CLI section]( ## Useful Plugins -- [Generate TypeScript typings from a Stack](https://github.com/Contentstack-Solutions/contentstack-cli-tsgen) +- [Generate TypeScript typings from a Stack](https://github.com/contentstack/cli-plugins/tree/v1-dev/packages/contentstack-cli-tsgen) (`contentstack-cli-tsgen`) diff --git a/packages/contentstack-cli-tsgen/.eslintrc.js b/packages/contentstack-cli-tsgen/.eslintrc.js new file mode 100644 index 000000000..63bf82762 --- /dev/null +++ b/packages/contentstack-cli-tsgen/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + rules: { + "unicorn/prefer-module": "off", + "unicorn/no-abusive-eslint-disable": "off", + "@typescript-eslint/no-use-before-define": "off", + "node/no-missing-import": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-require-imports": "off", + "no-useless-escape": "off", + }, +}; diff --git a/packages/contentstack-cli-tsgen/.gitignore b/packages/contentstack-cli-tsgen/.gitignore new file mode 100644 index 000000000..d3add85c8 --- /dev/null +++ b/packages/contentstack-cli-tsgen/.gitignore @@ -0,0 +1,15 @@ +*-debug.log +*-error.log +/.nyc_output +/dist +/lib +/tmp +node_modules +.DS_Store +coverage +.env +oclif.manifest.json +talisman_output.log +snyk_output.log +tsconfig.tsbuildinfo +tests/integration/generated.d.ts diff --git a/packages/contentstack-cli-tsgen/AGENTS.md b/packages/contentstack-cli-tsgen/AGENTS.md new file mode 100644 index 000000000..e783a3553 --- /dev/null +++ b/packages/contentstack-cli-tsgen/AGENTS.md @@ -0,0 +1,53 @@ +# contentstack-cli-tsgen – Agent guide + +**Universal entry point** for contributors and AI agents. Detailed conventions live in **`skills/*/SKILL.md`**. + +## What this repo is + +| Field | Detail | +| --- | --- | +| **Name:** | `contentstack-cli-tsgen` ([contentstack/cli-plugins](https://github.com/contentstack/cli-plugins) → `packages/contentstack-cli-tsgen`) | +| **Purpose:** | OCLIF plugin that adds **`csdx tsgen`** to generate TypeScript typings from a stack. Generation is delegated to **`@contentstack/types-generator`** (`generateTS` / `graphqlTS`); this package owns flags, auth alias, file output, and CLI error formatting. | +| **Out of scope (if any):** | Core type-generation logic belongs in **`@contentstack/types-generator`** ([npm](https://www.npmjs.com/package/@contentstack/types-generator)), not reimplemented here. | + +## Tech stack (at a glance) + +| Area | Details | +| --- | --- | +| **Language** | TypeScript **5.9** (`strict` in [tsconfig.json](tsconfig.json)) | +| **Build** | `pnpm run build` → `tsc -b` → `lib/`; OCLIF manifest + readme | +| **Tests** | **Jest** + **ts-jest** ([jest.config.js](jest.config.js)); integration tests under `tests/integration/` | +| **Lint / coverage** | ESLint via `lint` and `posttest` | +| **Other** | OCLIF v4, Node **>= 14**; **v1** `@contentstack/cli-command` ~1.8.2 / `@contentstack/cli-utilities` ~1.18.3 | + +**Main dependencies:** `@contentstack/cli-command`, `@contentstack/cli-utilities`, `@contentstack/types-generator`. + +## Commands (quick reference) + +| Command type | Command | +| --- | --- | +| **Build** | `pnpm run build` (from repo root: `pnpm --filter contentstack-cli-tsgen run build`) | +| **Test** | `pnpm test` (then **`posttest`** → ESLint) | +| **Integration** | `pnpm run test:integration` | +| **Lint** | `pnpm run lint` | + +CI: [tsgen-integration-test.yml](../../.github/workflows/tsgen-integration-test.yml) (live stack); tests in [unit-test.yml](../../.github/workflows/unit-test.yml); release via [release-production-plugins.yml](../../.github/workflows/release-production-plugins.yml) on **`main`** (`latest` tag). + +## Credentials and integration tests + +Integration tests spawn **`csdx tsgen`** and require a **delivery token alias**. Set **`TOKEN_ALIAS`** (e.g. **`.env`** at package root; see [tests/integration/tsgen.integration.test.ts](tests/integration/tsgen.integration.test.ts)). CI uses secrets **`REGION`**, **`TOKEN_ALIAS`**, **`APIKEY`**, **`DELIVERYKEY`**, **`ENVIRONMENT`**. + +## Where the documentation lives: skills + +| Skill | Path | What it covers | +| --- | --- | --- | +| Development workflow | [skills/dev-workflow/SKILL.md](skills/dev-workflow/SKILL.md) | pnpm, CI, PRs, releases | +| TypeScript CLI tsgen | [skills/typescript-cli-tsgen/SKILL.md](skills/typescript-cli-tsgen/SKILL.md) | OCLIF command, flags, delegation to the library | +| Testing | [skills/testing/SKILL.md](skills/testing/SKILL.md) | Jest, integration env, CI | +| Code review | [skills/code-review/SKILL.md](skills/code-review/SKILL.md) | PR checklist, terminology, semver | + +An index with “when to use” hints is in [skills/README.md](skills/README.md). + +## Migration from standalone repo + +See [TSGEN-MIGRATION.md](../../TSGEN-MIGRATION.md) at the monorepo root. diff --git a/packages/contentstack-cli-tsgen/MIGRATION.md b/packages/contentstack-cli-tsgen/MIGRATION.md new file mode 100644 index 000000000..f2c11e2cd --- /dev/null +++ b/packages/contentstack-cli-tsgen/MIGRATION.md @@ -0,0 +1,124 @@ +## Migrating to 4.10.0 (cli-plugins monorepo, CLI 1.x) + +The plugin source moved to [contentstack/cli-plugins](https://github.com/contentstack/cli-plugins) at `packages/contentstack-cli-tsgen`. See [TSGEN-MIGRATION.md](../../TSGEN-MIGRATION.md). + +| Change | Notes | +| --- | --- | +| Version | **4.10.0** — first release from monorepo on the **1.x** line | +| Install | `csdx plugins:install contentstack-cli-tsgen` | +| Dependencies | `@contentstack/cli-command` ~1.8.2, `@contentstack/cli-utilities` ~1.18.3 | +| Command | `csdx tsgen` unchanged | + +For **CLI 2.x beta**, use the `v2-beta` branch and `contentstack-cli-tsgen@beta` (**5.0.0-beta.0**+). + +--- + +## Migrating from v3 to v4 + +This changelog documents a breaking change to the `ISystemFields` interface, specifically related to the `publish_details` field. + +## What Changed + +The `publish_details` field is no longer an array of objects. It is now represented as a single `IPublishDetails` object. + +This update aligns the generated types with the actual [Contentstack API](https://www.contentstack.com/docs/developers/apis/content-delivery-api) response. + +## Before + +```typescript +export interface ISystemFields { + uid?: string; + created_at?: string; + updated_at?: string; + created_by?: string; + updated_by?: string; + _content_type_uid?: string; + tags?: string[]; + ACL?: any[]; + _version?: number; + _in_progress?: boolean; + locale?: string; + publish_details?: IPublishDetails[]; // Incorrect: Array of IPublishDetails + title?: string; +} +``` + +## After + +```typescript +export interface ISystemFields { + uid?: string; + created_at?: string; + updated_at?: string; + created_by?: string; + updated_by?: string; + _content_type_uid?: string; + tags?: string[]; + ACL?: any[]; + _version?: number; + _in_progress?: boolean; + locale?: string; + publish_details?: IPublishDetails; // Corrected: Single IPublishDetails object + title?: string; +} +``` + +--- + +## Migrating from v2 to v3 + +This document outlines the necessary changes to separate nested modular blocks into distinct interfaces. This update will affect how modular blocks are structured and used throughout the codebase. + +## Before + +```typescript +export interface Test { + /** Version */ + _version?: 2; + /** Title */ + title: string; + /** Modular Blocks */ + modular_blocks?: { + test: { + /** Multi Line Textbox */ + multi_line?: string; + /** Rich Text Editor */ + rich_text_editor?: string; + /** Modular Blocks1 */ + modular_blocks1?: { + test1: { + /** Multi Line Textbox */ + multi_line?: string; + }; + }[]; + }; + }[]; +} +``` + +## After + +```typescript +export interface Test { + /** Version */ + _version: 2; + /** Title */ + title: string; + /** Modular Blocks */ + modular_blocks?: ModularBlocks[]; +} + +export interface ModularBlocks { + /** Multi Line Textbox */ + multi_line?: string; + /** Rich Text Editor */ + rich_text_editor?: string; + /** Modular Blocks1 */ + modular_blocks1?: ModularBlocks1[]; +} + +export interface ModularBlocks1 { + /** Multi Line Textbox */ + multi_line?: string; +} +``` diff --git a/packages/contentstack-cli-tsgen/README.md b/packages/contentstack-cli-tsgen/README.md new file mode 100644 index 000000000..0522459e6 --- /dev/null +++ b/packages/contentstack-cli-tsgen/README.md @@ -0,0 +1,135 @@ +![npm](https://img.shields.io/npm/v/contentstack-cli-tsgen) + +## Description + +This is a plugin for [Contentstack's](https://www.contentstack.com/) CLI. +This plugin generates TypeScript typings from Content Types. Interfaces and fields are optionally annotated with JSDoc comments. + +Source lives in [contentstack/cli-plugins](https://github.com/contentstack/cli-plugins) at `packages/contentstack-cli-tsgen` (**CLI 1.x** line). + +## How to install this plugin + +```shell +$ csdx plugins:install contentstack-cli-tsgen +``` + +## Migration + +- **Monorepo move (4.10.0):** See [TSGEN-MIGRATION.md](../../TSGEN-MIGRATION.md) and [MIGRATION.md](./MIGRATION.md). +- **Older plugin versions:** See [MIGRATION.md](./MIGRATION.md) for v3→v4 schema changes. + +## How to use this plugin + +`$ csdx tsgen` + +generate TypeScript typings from a Stack + +``` +USAGE + $ csdx tsgen + +OPTIONS + -a, --token-alias=token-alias (required) delivery token alias + -d, --[no-]doc include documentation comments + -o, --output=output (required) full path to output + -p, --prefix=prefix interface prefix, e.g. "I" + +EXAMPLES + $ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" + $ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" -p "I" + $ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" --no-doc +``` + +_See code: [src/commands/tsgen.ts](https://github.com/contentstack/cli-plugins/blob/v1-dev/packages/contentstack-cli-tsgen/src/commands/tsgen.ts)_ + + +## Supported Fields +* Number +* Text +* IsoDate +* Boolean +* Single Select w/ String and Number Types +* Multiple Select w/ String and Number Types +* Modular Block +* Global Field +* Group +* Link +* File +* References + +## Supported Field Options +* Mandatory +* Multiple +* Multiple Max Limit +* Description (used in JSDoc comment) + +## Example Output +```typescript +/** This is a description. */ +interface BuiltinExample { + /** Title */ + title: string; + /** URL */ + url: string; + /** Group1 */ + group1?: { + /** Group2 */ + group2?: { + /** Group3 */ + group3?: { + /** Number */ + number?: number; + }; + }; + }; + /** SEO */ + seo?: Seo; + /** Single line textbox */ + single_line?: string; + /** Multi line textbox */ + multi_line?: string; + /** Rich text editor */ + rich_text_editor?: string; + /** Multiple Single Line Textbox */ + multiple_single_line_textbox?: string[]; + /** Markdown */ + markdown?: string; + /** Multiple Choice */ + multiple_choice?: ("Choice 1" | "Choice 2" | "Choice 3")[]; + /** Single Choice */ + single_choice: "Choice 1" | "Choice 2" | "Choice 3"; + /** Modular Blocks */ + modular_blocks?:ModularBlocks[]; + /** Number */ + number?: number; + /** Link */ + link?: Link; + /** File */ + file?: File; + /** Boolean */ + boolean?: boolean; + /** Date */ + date?: string; +} + +interface ModularBlocks { + block_1: { + /** Number */ + number?: number; + /** Single line textbox */ + single_line?: string; + }; + block_2: { + /** Boolean */ + boolean?: boolean; + /** Date */ + date?: string; + }; + seo_gf: { + /** Keywords */ + keywords?: string; + /** Description */ + description?: string; + }; +} +``` diff --git a/packages/contentstack-cli-tsgen/SECURITY.md b/packages/contentstack-cli-tsgen/SECURITY.md new file mode 100644 index 000000000..1f44e3424 --- /dev/null +++ b/packages/contentstack-cli-tsgen/SECURITY.md @@ -0,0 +1,27 @@ +## Security + +Contentstack takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations. + +If you believe you have found a security vulnerability in any Contentstack-owned repository, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Send email to [security@contentstack.com](mailto:security@contentstack.com). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + +- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +- Full paths of source file(s) related to the manifestation of the issue +- The location of the affected source code (tag/branch/commit or direct URL) +- Any special configuration required to reproduce the issue +- Step-by-step instructions to reproduce the issue +- Proof-of-concept or exploit code (if possible) +- Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +[https://www.contentstack.com/trust/](https://www.contentstack.com/trust/) diff --git a/packages/contentstack-cli-tsgen/bin/dev b/packages/contentstack-cli-tsgen/bin/dev new file mode 100755 index 000000000..5ba3954a0 --- /dev/null +++ b/packages/contentstack-cli-tsgen/bin/dev @@ -0,0 +1,5 @@ +#!/usr/bin/env node +(async () => { + const { execute } = require("@contentstack/cli-utilities"); + await execute({ type: "cjs", development: true, dir: __dirname }); +})(); diff --git a/packages/contentstack-cli-tsgen/bin/dev.cmd b/packages/contentstack-cli-tsgen/bin/dev.cmd new file mode 100644 index 000000000..077b57ae7 --- /dev/null +++ b/packages/contentstack-cli-tsgen/bin/dev.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\dev" %* \ No newline at end of file diff --git a/packages/contentstack-cli-tsgen/bin/dev.js b/packages/contentstack-cli-tsgen/bin/dev.js new file mode 100755 index 000000000..5ba3954a0 --- /dev/null +++ b/packages/contentstack-cli-tsgen/bin/dev.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node +(async () => { + const { execute } = require("@contentstack/cli-utilities"); + await execute({ type: "cjs", development: true, dir: __dirname }); +})(); diff --git a/packages/contentstack-cli-tsgen/bin/run b/packages/contentstack-cli-tsgen/bin/run new file mode 100755 index 000000000..d06447d35 --- /dev/null +++ b/packages/contentstack-cli-tsgen/bin/run @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +(async () => { + const {execute} = require('@contentstack/cli-utilities') + await execute({ type: 'cjs', dir: __dirname }) +})() \ No newline at end of file diff --git a/packages/contentstack-cli-tsgen/bin/run.cmd b/packages/contentstack-cli-tsgen/bin/run.cmd new file mode 100644 index 000000000..968fc3075 --- /dev/null +++ b/packages/contentstack-cli-tsgen/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/contentstack-cli-tsgen/bin/run.js b/packages/contentstack-cli-tsgen/bin/run.js new file mode 100755 index 000000000..d06447d35 --- /dev/null +++ b/packages/contentstack-cli-tsgen/bin/run.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +(async () => { + const {execute} = require('@contentstack/cli-utilities') + await execute({ type: 'cjs', dir: __dirname }) +})() \ No newline at end of file diff --git a/packages/contentstack-cli-tsgen/jest.config.js b/packages/contentstack-cli-tsgen/jest.config.js new file mode 100644 index 000000000..3ccbce97f --- /dev/null +++ b/packages/contentstack-cli-tsgen/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + roots: [""], + testMatch: ["**/tests/**/*.+(ts|tsx)", "**/?(*.)+(spec|test).+(ts|tsx)"], + transform: { + "^.+\\.(ts|tsx)$": "ts-jest", + }, +}; diff --git a/packages/contentstack-cli-tsgen/package.json b/packages/contentstack-cli-tsgen/package.json new file mode 100644 index 000000000..66ed3fab3 --- /dev/null +++ b/packages/contentstack-cli-tsgen/package.json @@ -0,0 +1,74 @@ +{ + "name": "contentstack-cli-tsgen", + "description": "Generate TypeScript typings from a Stack.", + "version": "4.10.0", + "author": "Contentstack", + "bugs": "https://github.com/contentstack/cli-plugins/issues", + "dependencies": { + "@contentstack/cli-command": "~1.8.2", + "@contentstack/cli-utilities": "~1.18.3", + "@contentstack/types-generator": "^3.10.0" + }, + "devDependencies": { + "@oclif/plugin-help": "^6.2.49", + "@oclif/test": "^4.1.18", + "@types/jest": "^29.5.14", + "@types/node": "^22.19.19", + "@typescript-eslint/eslint-plugin": "^8.59.3", + "@typescript-eslint/parser": "^8.59.3", + "dotenv": "^16.6.1", + "eslint": "^8.57.1", + "eslint-config-oclif": "^6.0.165", + "eslint-config-oclif-typescript": "^3.1.14", + "jest": "^29.7.0", + "oclif": "^4.23.7", + "ts-jest": "^29.4.9", + "typescript": "^5.9.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "files": [ + "/lib", + "/npm-shrinkwrap.json", + "/oclif.manifest.json" + ], + "homepage": "https://github.com/contentstack/cli-plugins/tree/v1-dev/packages/contentstack-cli-tsgen", + "keywords": [ + "contentstack", + "cli", + "plugin", + "typescript" + ], + "license": "MIT", + "oclif": { + "commands": "./lib/commands", + "bin": "csdx", + "devPlugins": [ + "@oclif/plugin-help" + ], + "repositoryPrefix": "<%- repo %>/blob/main/packages/contentstack-cli-tsgen/<%- commandPath %>" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/contentstack/cli-plugins.git", + "directory": "packages/contentstack-cli-tsgen" + }, + "scripts": { + "build": "pnpm compile && oclif manifest && oclif readme", + "clean": "rm -rf ./lib ./node_modules tsconfig.tsbuildinfo", + "compile": "tsc -b tsconfig.json", + "lint": "eslint . --ext .ts --config .eslintrc.js", + "postpack": "rm -f oclif.manifest.json", + "posttest": "eslint . --ext .ts --config .eslintrc.js --fix", + "prepack": "pnpm compile && oclif manifest && oclif readme", + "test": "jest --testPathPattern=tests", + "test:integration": "jest --testPathPattern=tests/integration", + "version": "oclif readme && git add README.md" + }, + "csdxConfig": { + "shortCommandName": { + "tsgen": "TSGEN" + } + } +} diff --git a/packages/contentstack-cli-tsgen/skills/README.md b/packages/contentstack-cli-tsgen/skills/README.md new file mode 100644 index 000000000..1c724cba3 --- /dev/null +++ b/packages/contentstack-cli-tsgen/skills/README.md @@ -0,0 +1,18 @@ +# Skills – contentstack-cli-tsgen + +Source of truth for detailed guidance. Read [AGENTS.md](../AGENTS.md) first, then open the skill that matches your task. + +Parent repository: **[contentstack/cli-plugins](https://github.com/contentstack/cli-plugins)** (`packages/contentstack-cli-tsgen`, **v1 line**). + +## When to use which skill + +| Skill folder | Use when | +| --- | --- | +| [dev-workflow](dev-workflow/SKILL.md) | pnpm commands, monorepo CI, PR and production release | +| [typescript-cli-tsgen](typescript-cli-tsgen/SKILL.md) | OCLIF `tsgen` command, flags, helpers vs library behavior | +| [testing](testing/SKILL.md) | Jest, posttest ESLint, integration tests, `TOKEN_ALIAS`, CI secrets | +| [code-review](code-review/SKILL.md) | PR checklist: CLI UX, errors, Delivery vs CMA wording, types-generator dependency | + +HTTP and generation internals live in **`@contentstack/types-generator`** ([npm](https://www.npmjs.com/package/@contentstack/types-generator)); do not assume a sibling checkout. + +Each folder contains `SKILL.md` with YAML frontmatter (`name`, `description`). diff --git a/packages/contentstack-cli-tsgen/skills/code-review/SKILL.md b/packages/contentstack-cli-tsgen/skills/code-review/SKILL.md new file mode 100644 index 000000000..60ecaa3a4 --- /dev/null +++ b/packages/contentstack-cli-tsgen/skills/code-review/SKILL.md @@ -0,0 +1,29 @@ +--- +name: code-review +description: PR review checklist for contentstack-cli-tsgen (v1 line). +--- + +# Code review skill (`contentstack-cli-tsgen`) + +## CLI and docs + +- Help text, **`static examples`**, and README/oclif docs stay in sync when flags change. +- Short command **`tsgen`** / **`TSGEN`** from **`csdxConfig`** in [package.json](../../package.json). + +## Product language + +- Prefer **Delivery** token flows; **GraphQL** requires delivery token. + +## Errors + +- **`printFormattedError`** in [`src/lib/helper.ts`](../../src/lib/helper.ts)—extend **`error_code`** switches carefully. + +## Dependency on the library + +- **`generateTS`** / **`graphqlTS`** logic belongs in **`@contentstack/types-generator`**. + +## Tests and CI + +- Integration tests need **`csdx`** and **`TOKEN_ALIAS`**. +- **v1 semver:** **`4.10.0`**+ on this branch; do not land v2 **`5.0.0-beta.0`** pins here. +- SCA: monorepo [`.github/workflows/sca-scan.yml`](../../../.github/workflows/sca-scan.yml). diff --git a/packages/contentstack-cli-tsgen/skills/dev-workflow/SKILL.md b/packages/contentstack-cli-tsgen/skills/dev-workflow/SKILL.md new file mode 100644 index 000000000..8c2846c49 --- /dev/null +++ b/packages/contentstack-cli-tsgen/skills/dev-workflow/SKILL.md @@ -0,0 +1,55 @@ +--- +name: dev-workflow +description: pnpm, monorepo CI, PR and production release workflow for contentstack-cli-tsgen (v1 line). +--- + +# Development workflow – contentstack-cli-tsgen (v1) + +## When to use + +- Running builds or tests before a PR +- Understanding which GitHub Actions run for this package +- Husky / pre-commit expectations in the monorepo + +## Commands (from repo root) + +| Command | Purpose | +| --- | --- | +| `pnpm --filter contentstack-cli-tsgen run build` | `tsc -b` → `lib/`, OCLIF manifest + readme | +| `pnpm --filter contentstack-cli-tsgen test` | Jest; then **`posttest`** → ESLint | +| `pnpm --filter contentstack-cli-tsgen run test:integration` | Jest integration tests only | +| `pnpm --filter contentstack-cli-tsgen run lint` | ESLint | +| `pnpm --filter contentstack-cli-tsgen run clean` | Remove `lib/`, `node_modules`, build info | + +## Local plugin link + +```bash +cd packages/contentstack-cli-tsgen +pnpm run build +npm i -g @contentstack/cli +csdx plugins:link +csdx tsgen --help +csdx plugins:unlink +``` + +## Branches and CI + +- Development: **`feat/migrate-external-cli-plugins-v1`** → merge to **`v1-dev`** / **`main`**. +- Workflows under [`.github/workflows/`](../../../.github/workflows/): + - **`tsgen-integration-test.yml`** — live `csdx tsgen` tests (delivery token secrets; global **`@contentstack/cli`**) + - **`unit-test.yml`** — workspace build + `npm run test` for this package + - **`release-production-plugins.yml`** — npm publish with **`latest`** tag on push to **`main`** + - **`sca-scan.yml`**, **`policy-scan.yml`**, **`codeql-analysis.yml`** — monorepo-wide + +## Git hooks + +- Root **`prepare`** runs Husky; hooks under [`.husky/`](../../../.husky/) when present. + +## Pull requests + +- Run **`pnpm --filter contentstack-cli-tsgen run build`** before opening a PR. +- Integration tests run in **`tsgen-integration-test.yml`**; local runs need **`TOKEN_ALIAS`** in `.env`. + +## Releases + +- Version **`4.10.0`**+ in [package.json](../../package.json); published from **`main`** via **`release-production-plugins.yml`**. diff --git a/packages/contentstack-cli-tsgen/skills/testing/SKILL.md b/packages/contentstack-cli-tsgen/skills/testing/SKILL.md new file mode 100644 index 000000000..cf5412c3e --- /dev/null +++ b/packages/contentstack-cli-tsgen/skills/testing/SKILL.md @@ -0,0 +1,32 @@ +--- +name: testing +description: Jest and integration tests for contentstack-cli-tsgen (v1 line). +--- + +# Testing skill (`contentstack-cli-tsgen`) + +This package is the **only** cli-plugins package that uses **Jest** (other plugins use Mocha). + +## Commands + +| Command | What it does | +| --- | --- | +| `pnpm test` | Jest with `--testPathPattern=tests`; then **`posttest`** runs ESLint | +| `pnpm run test:integration` | Jest only for `tests/integration` | +| `pnpm run build` | Build `lib/` (required before `csdx plugins:link`) | + +From repo root: `pnpm --filter contentstack-cli-tsgen …` + +## Config + +- [`jest.config.js`](../../jest.config.js): **ts-jest**, **`testEnvironment: node`**. + +## Integration tests + +- **[tests/integration/tsgen.integration.test.ts](../../tests/integration/tsgen.integration.test.ts)** spawns **`csdx tsgen`** with **`TOKEN_ALIAS`**. +- Loads **`.env`** from package root via **`dotenv`**. **`TOKEN_ALIAS`** must be defined or the suite throws at load time. + +## CI + +- [`.github/workflows/tsgen-integration-test.yml`](../../../.github/workflows/tsgen-integration-test.yml): `pnpm install`, build, global **`@contentstack/cli`**, token setup, **`csdx plugins:link`**, **`test:integration`** with secrets. +- [`.github/workflows/unit-test.yml`](../../../.github/workflows/unit-test.yml) → `npm run test` in this package. diff --git a/packages/contentstack-cli-tsgen/skills/typescript-cli-tsgen/SKILL.md b/packages/contentstack-cli-tsgen/skills/typescript-cli-tsgen/SKILL.md new file mode 100644 index 000000000..3dfe9e7bd --- /dev/null +++ b/packages/contentstack-cli-tsgen/skills/typescript-cli-tsgen/SKILL.md @@ -0,0 +1,31 @@ +--- +name: typescript-cli-tsgen +description: Mental model for the contentstack-cli-tsgen OCLIF plugin and tsgen command (CLI 1.x line). +--- + +# TypeScript CLI tsgen skill + +## Role of this package + +- **Plugin** for **`csdx`**: implements **`TypeScriptCodeGeneratorCommand`** in [`src/commands/tsgen.ts`](../../src/commands/tsgen.ts). +- **Generation** is delegated to **`@contentstack/types-generator`** ([npm](https://www.npmjs.com/package/@contentstack/types-generator))—version **^3.10.0** in [package.json](../../package.json). +- Uses **v1** `@contentstack/cli-command` **~1.8.2** and `@contentstack/cli-utilities` **~1.18.3**. + +## Change here vs change in the library + +| Concern | Where | +| --- | --- | +| New flags, output path, `csdx` UX, **`printFormattedError`** | This package (`src/commands/`, `src/lib/`) | +| Schema mapping, Delivery SDK calls, GraphQL introspection | **`@contentstack/types-generator`** | + +## Helpers + +- [`src/lib/helper.ts`](../../src/lib/helper.ts): **`sanitizePath`**, **`printFormattedError`**. +- [`src/types/index.ts`](../../src/types/index.ts): **`StackConnectionConfig`**. + +## Command shape (`tsgen`) + +- Extends **`Command`** from **`@contentstack/cli-command`**. +- **Flags:** `token-alias` (`-a`, required), `output` (`-o`), `prefix`, `doc`, `branch`, `include-system-fields`, `include-editable-tags`, `include-referenced-entry`, `api-type` (`rest` \| `graphql`), `namespace` (GraphQL). +- **`this.getToken(flags["token-alias"])`**; warn if not a delivery token. +- REST → **`generateTS`**; GraphQL → **`graphqlTS`**; write with **`fs.writeFileSync`**. diff --git a/packages/contentstack-cli-tsgen/src/commands/tsgen.ts b/packages/contentstack-cli-tsgen/src/commands/tsgen.ts new file mode 100644 index 000000000..fc14f9016 --- /dev/null +++ b/packages/contentstack-cli-tsgen/src/commands/tsgen.ts @@ -0,0 +1,247 @@ +import { Command } from "@contentstack/cli-command"; +import { flags, FlagInput, log } from "@contentstack/cli-utilities"; +import * as path from "path"; +import * as fs from "fs"; +import { cliux } from "@contentstack/cli-utilities"; +import { generateTS, graphqlTS } from "@contentstack/types-generator"; +import { sanitizePath, printFormattedError } from "../lib/helper"; +import { StackConnectionConfig } from "../types"; + +function createOutputPath(outputFile: string) { + const outputPath = path.resolve( + sanitizePath(process.cwd()), + sanitizePath(outputFile), + ); + const dirName = path.dirname(outputPath); + + fs.mkdirSync(dirName, { recursive: true }); + + return outputPath; +} + +export default class TypeScriptCodeGeneratorCommand extends Command { + static description = "Generate TypeScript typings from a Stack"; + + static examples = [ + '$ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts"', + '$ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" -p "I"', + '$ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" --no-doc', + '$ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" --include-referenced-entry', + '$ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" --api-type graphql', + '$ csdx tsgen -a "delivery token alias" -o "contentstack/generated.d.ts" --api-type graphql --namespace "GraphQL" ', + ]; + + // Check if a region is a default Contentstack region + private isDefaultRegion(region: string): boolean { + const defaultRegions = [ + "US", + "EU", + "AU", + "AZURE_NA", + "AZURE_EU", + "GCP_NA", + "GCP_EU", + ]; + return defaultRegions.includes(region.toUpperCase()); + } + + static flags: FlagInput = { + "token-alias": flags.string({ + char: "a", + description: "delivery token alias", + hidden: false, + multiple: false, + required: true, + }), + + output: flags.string({ + char: "o", + description: "full path to output", + hidden: false, + multiple: false, + required: true, + }), + + prefix: flags.string({ + char: "p", + description: 'interface prefix, e.g. "I"', + hidden: false, + multiple: false, + default: "", + required: false, + }), + + doc: flags.boolean({ + char: "d", + description: "include documentation comments", + default: true, + allowNo: true, + }), + + branch: flags.string({ + description: "branch", + hidden: false, + multiple: false, + }), + + "include-system-fields": flags.boolean({ + description: "include system fields in generated types", + default: false, + }), + + "include-editable-tags": flags.boolean({ + description: "include editable tags in generated types", + default: false, + }), + + "include-referenced-entry": flags.boolean({ + description: + "Includes the ReferencedEntry interface in generated types. Use this option to add a generic interface for handling referenced entries when the exact content type is unknown or when you need a flexible reference type", + default: false, + }), + + "api-type": flags.string({ + default: "rest", + multiple: false, + options: ["rest", "graphql"], + description: + "[Optional] Please enter an API type to generate the type definitions.", + }), + + namespace: flags.string({ + description: + "[Optional]Please enter a namespace for the GraphQL API type to organize the generated types.", + }), + }; + + async run() { + try { + const { flags } = await this.parse(TypeScriptCodeGeneratorCommand); + + const token = this.getToken(flags["token-alias"]); + const prefix = flags.prefix; + const includeDocumentation = flags.doc; + const filePath = flags.output; + const branch = flags.branch; + const includeSystemFields = flags["include-system-fields"]; + const includeEditableTags = flags["include-editable-tags"]; + const includeReferencedEntry = flags["include-referenced-entry"]; + const namespace = flags.namespace; + + const outputPath = createOutputPath(filePath); + + if (token.type !== "delivery") { + this.warn( + "You might be using a management token. Connection may fail. Use a delivery token instead.", + ); + } + + if (!outputPath || !outputPath.trim()) { + this.error("Output path is required.", { exit: 2 }); + } + + const config: StackConnectionConfig = { + apiKey: token.apiKey, + token: token.token, + region: + this.region.name === "NA" ? "us" : this.region.name.toLowerCase(), + environment: token.environment || "", + branch: branch || undefined, + host: this.cdaHost, + }; + + // Generate the GraphQL schema TypeScript definitions + if (flags["api-type"] === "graphql") { + try { + if (config.region === "us") { + config.region = "US"; + } + + // Check if token has delivery type (required for GraphQL) + if (token.type !== "delivery") { + throw new Error( + "GraphQL API requires a delivery token. Management tokens aren't supported.", + ); + } + + // Prepare GraphQL config - only include host for custom regions + const graphqlConfig: any = { + apiKey: config.apiKey, + token: config.token, + environment: config.environment, + namespace: namespace, + logger: log, + }; + + // Add region or host based on whether it's a custom region + if (config.host && !this.isDefaultRegion(config.region)) { + // Custom region - include both region and host + graphqlConfig.region = config.region; + graphqlConfig.host = config.host; + } else { + // Default region - only include region + graphqlConfig.region = config.region; + } + + const result = await graphqlTS(graphqlConfig); + + if (!result) { + throw new Error("No result returned by the GraphQL API."); + } + + fs.writeFileSync(outputPath, result); + this.log( + `Successfully added the GraphQL schema type definitions to '${outputPath}'.`, + ); + } catch (error: any) { + printFormattedError(error, error?.context || "graphql"); + process.exit(1); + } + } else { + // Generate the Content Types TypeScript definitions + try { + const result = await generateTS({ + ...config, + tokenType: "delivery", + includeDocumentation: includeDocumentation, + prefix, + systemFields: includeSystemFields, + isEditableTags: includeEditableTags, + includeReferencedEntry, + logger: log, + }); + + fs.writeFileSync(outputPath, result || ""); + + // Check if we have any skipped content types and show summary + if ( + result && + typeof result === "string" && + result.includes( + "Generation completed successfully with partial schema", + ) + ) { + cliux.print(""); + log.success("Type generation completed successfully with partial schema!"); + log.warn( + "Some content types were skipped due to validation issues, but types were generated for valid content types." + ); + log.info( + "Check the output above for details on what was skipped and suggestions for fixing issues." + ); + } else { + log.success(`Successfully added the Content Types to '${outputPath}'.`); + } + + // this.log(`Wrote ${outputPath} Content Types to '${result.outputPath}'.`) + } catch (error: any) { + printFormattedError(error, error?.context || "tsgen"); + process.exit(1); + } + } + } catch (error: any) { + printFormattedError(error, error?.context || "tsgen"); + process.exit(1); + } + } +} diff --git a/packages/contentstack-cli-tsgen/src/lib/helper.ts b/packages/contentstack-cli-tsgen/src/lib/helper.ts new file mode 100644 index 000000000..5ea7a7adb --- /dev/null +++ b/packages/contentstack-cli-tsgen/src/lib/helper.ts @@ -0,0 +1,91 @@ +import { log } from "@contentstack/cli-utilities"; + +export const sanitizePath = (str: string) => { + return str + ?.replace(/^([\/\\]){2,}/, "./") // Normalize leading slashes/backslashes to '' + .replace(/[\/\\]+/g, "/") // Replace multiple slashes/backslashes with a single '/' + .replace(/(\.\.(\/|\\|$))+/g, ""); // Remove directory traversal (../ or ..\) +}; + +/** + * Error object interface for consistent error handling + */ +export interface FormattedError { + error_code?: string; + error_message?: string; + message?: string; + context?: string; + timestamp?: string; +} + +/** + * Prints formatted error messages with consistent styling and helpful hints + * @param error - The error object containing error details + * @param context - The context where the error occurred (e.g., "tsgen", "graphql") + */ +export const printFormattedError = (error: FormattedError, context: string) => { + const errorCode = error?.error_code || "UNKNOWN_ERROR"; + // Special handling for our numeric identifier validation errors + if ( + errorCode === "VALIDATION_ERROR" && + error?.error_message && + error.error_message.includes("numeric identifiers") + ) { + // Just print our detailed message as-is, no extra formatting + log.error(error.error_message); + return; + } + + let errorMessage = "An unexpected error occurred. Try again."; + let hint = ""; + + switch (errorCode) { + case "AUTHENTICATION_FAILED": + errorMessage = "Authentication failed. Check your credentials and try again."; + hint = "Please check your API key, token, and region."; + break; + case "INVALID_CREDENTIALS": + errorMessage = "Invalid credentials. Please verify and re-enter your login details."; + hint = "Please verify your API key, token, and region."; + break; + case "INVALID_INTERFACE_NAME": + case "INVALID_CONTENT_TYPE_UID": + errorMessage = "Generated types contain a TypeScript syntax error."; + hint = + "Use a prefix to ensure all interface names are valid TypeScript identifiers."; + break; + case "INVALID_GLOBAL_FIELD_REFERENCE": + errorMessage = "Generated types contain a TypeScript syntax error."; + hint = + "Use a prefix to ensure all interface names are valid TypeScript identifiers."; + break; + case "VALIDATION_ERROR": + errorMessage = "Type generation failed due to a validation error."; + hint = + error?.error_message || + "Type generation failed due to a validation error."; + break; + case "TYPE_GENERATION_FAILED": + errorMessage = "Type generation failed due to a system error. Try again."; + hint = + error?.error_message || + "Unexpected error during type generation. Try again."; + break; + default: + errorMessage = + error?.error_message || + error?.message || + "An unexpected error occurred. Try again."; + hint = "Check the error details and try again."; + } + + // Print formatted error output + log.error(`Type generation failed: ${errorMessage}`); + + if (hint) { + log.warn(`Tip: ${hint}`); + } + + log.info(`Error context: ${context}`); + log.info(`Timestamp: ${error?.timestamp || new Date().toISOString()}`); +}; diff --git a/packages/contentstack-cli-tsgen/src/types/index.ts b/packages/contentstack-cli-tsgen/src/types/index.ts new file mode 100644 index 000000000..6d869b933 --- /dev/null +++ b/packages/contentstack-cli-tsgen/src/types/index.ts @@ -0,0 +1,8 @@ +export type StackConnectionConfig = { + apiKey: string; + token: string; + region: any; + environment: string; + branch?: string | undefined; + host?: string; +}; diff --git a/packages/contentstack-cli-tsgen/tests/integration/tsgen.integration.test.ts b/packages/contentstack-cli-tsgen/tests/integration/tsgen.integration.test.ts new file mode 100644 index 000000000..f3860ff57 --- /dev/null +++ b/packages/contentstack-cli-tsgen/tests/integration/tsgen.integration.test.ts @@ -0,0 +1,191 @@ +import { spawnSync } from "child_process"; +import * as path from "path"; +import * as dotenv from "dotenv"; +import * as fs from "fs"; + +// Load environment variables from .env file +dotenv.config({ path: path.resolve(__dirname, "../../.env") }); + +const outputFilePath = path.resolve(__dirname, "generated.d.ts"); // Define the path to store the generated TypeScript file +const tokenAlias = process.env.TOKEN_ALIAS; + +describe("Integration Test for tsgen command", () => { + beforeEach(() => { + if (fs.existsSync(outputFilePath)) { + fs.unlinkSync(outputFilePath); + } + }); + + // Check if tokenAlias is defined before running tests + if (!tokenAlias) { + throw new Error("TOKEN_ALIAS environment variable is not set"); + } + + // Test case 1: Generate TypeScript types with default flags + it("should generate TypeScript types with the default flags", () => { + const cmd = "csdx"; + const args = ["tsgen", "-a", tokenAlias!, "-o", outputFilePath]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).toBe(0); // Command should exit successfully + expect(fs.existsSync(outputFilePath)).toBeTruthy(); + + const generatedContent = fs.readFileSync(outputFilePath, "utf8"); + expect(generatedContent).toContain("interface"); // Verify TypeScript interface presence + expect(generatedContent).toMatch(/(?:\/\*\*.*?\*\/\s*)?export/); // Multi-line comment block check + }); + + // Test case 2: Generate TypeScript types with a prefix applied + it("should generate TypeScript types with the prefix", () => { + const prefix = "I"; + const cmd = "csdx"; + const args = [ + "tsgen", + "-a", + tokenAlias!, + "-o", + outputFilePath, + "-p", + prefix, + ]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).toBe(0); + const generatedContent = fs.readFileSync(outputFilePath, "utf8"); + + expect(generatedContent).toContain("interface"); + const allInterfacesWithPrefix = + generatedContent.match(/export interface \w+/g); + if (allInterfacesWithPrefix) { + allInterfacesWithPrefix.forEach((interfaceDecl) => { + expect( + interfaceDecl.startsWith(`export interface ${prefix}`), + ).toBeTruthy(); + }); + } + + expect(generatedContent).toMatch(/(?:\/\*\*.*?\*\/\s*)?export/); // Multi-line comment block check + }); + + // Test case 3: Generate TypeScript types without documentation comments + it("should generate TypeScript types without documentation", () => { + const cmd = "csdx"; + const args = ["tsgen", "-a", tokenAlias!, "-o", outputFilePath, "--no-doc"]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).toBe(0); + expect(fs.existsSync(outputFilePath)).toBeTruthy(); + + const generatedContent = fs.readFileSync(outputFilePath, "utf8"); + expect(generatedContent).toMatch(/(?:\/\*\*.*?\*\/\s*)?export/); // Ensure no multi-line comments + }); + + // Test case 4: Generate TypeScript types with system fields + it("should generate TypeScript types with the system fields", () => { + const cmd = "csdx"; + const args = [ + "tsgen", + "-a", + tokenAlias!, + "-o", + outputFilePath, + "--include-system-fields", + ]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).toBe(0); + expect(fs.existsSync(outputFilePath)).toBeTruthy(); + + const generatedContent = fs.readFileSync(outputFilePath, "utf8"); + expect(generatedContent).toContain("export interface SystemFields"); + expect(generatedContent).toContain("extends SystemFields"); + }); + + // Test case 5: Handling of invalid token alias + it("should fail with an invalid token alias", () => { + const cmd = "csdx"; + const args = ["tsgen", "-a", "invalid_alias", "-o", outputFilePath]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).not.toBe(0); // Command should fail + + expect(result.stderr + result.stdout).toContain("No token found"); + }); + + // Test case 6: Generate TypeScript types for GraphQL API + it("should generate correct TypeScript for basic GraphQL response", () => { + const cmd = "csdx"; + const args = [ + "tsgen", + "-a", + tokenAlias!, + "-o", + outputFilePath, + "--api-type", + "graphql", + ]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + expect(result.status).toBe(0); + expect(fs.existsSync(outputFilePath)).toBeTruthy(); + + const generatedContent = fs.readFileSync(outputFilePath, "utf-8"); + expect(generatedContent).toContain("interface IGraphQLResponseRoot"); + expect(generatedContent).toContain("interface IGraphQLResponseError"); + }); + + // Test case 7: Generate TypeScript types for GraphQL API with a custom namespace + it("should generate correct TypeScript for GraphQL API with a custom namespace", () => { + const namespace = "GraphQL"; + const cmd = "csdx"; + const args = [ + "tsgen", + "-a", + tokenAlias!, + "-o", + outputFilePath, + "--api-type", + "graphql", + "--namespace", + namespace, + ]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).toBe(0); + expect(fs.existsSync(outputFilePath)).toBeTruthy(); + + const generatedContent = fs.readFileSync(outputFilePath, "utf-8"); + expect(generatedContent).toContain(`declare namespace ${namespace}`); + }); + + // Test case 8: Handle errors for GraphQL API + it("should fail with an invalid token alias for GraphQL API", () => { + const cmd = "csdx"; + const args = [ + "tsgen", + "-a", + "invalid_alias", + "-o", + outputFilePath, + "--api-type", + "graphql", + ]; + + const result = spawnSync(cmd, args, { encoding: "utf-8" }); + + expect(result.status).not.toBe(0); + expect(result.stderr + result.stdout).toContain("No token found"); + }); + + afterEach(() => { + if (fs.existsSync(outputFilePath)) { + fs.unlinkSync(outputFilePath); + } + }); +}); diff --git a/packages/contentstack-cli-tsgen/tsconfig.json b/packages/contentstack-cli-tsgen/tsconfig.json new file mode 100644 index 000000000..d0682513c --- /dev/null +++ b/packages/contentstack-cli-tsgen/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "declaration": true, + "importHelpers": true, + "module": "commonjs", + "outDir": "lib", + "rootDir": "src", + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "resolveJsonModule": true, + }, + "include": [ + "src/**/*" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3824fa6d..6f71a6e04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -372,6 +372,61 @@ importers: specifier: ^4.17.46 version: 4.23.7(@types/node@22.19.19) + packages/contentstack-cli-tsgen: + dependencies: + '@contentstack/cli-command': + specifier: ~1.8.2 + version: 1.8.2(@types/node@22.19.19) + '@contentstack/cli-utilities': + specifier: ~1.18.3 + version: 1.18.3(@types/node@22.19.19) + '@contentstack/types-generator': + specifier: ^3.10.0 + version: 3.10.0(graphql@16.14.0) + devDependencies: + '@oclif/plugin-help': + specifier: ^6.2.49 + version: 6.2.49 + '@oclif/test': + specifier: ^4.1.18 + version: 4.1.18(@oclif/core@4.11.3) + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/node': + specifier: ^22.19.19 + version: 22.19.19 + '@typescript-eslint/eslint-plugin': + specifier: ^8.59.3 + version: 8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.59.3 + version: 8.59.4(eslint@8.57.1)(typescript@5.9.3) + dotenv: + specifier: ^16.6.1 + version: 16.6.1 + eslint: + specifier: ^8.57.1 + version: 8.57.1 + eslint-config-oclif: + specifier: ^6.0.165 + version: 6.0.165(eslint@8.57.1)(typescript@5.9.3) + eslint-config-oclif-typescript: + specifier: ^3.1.14 + version: 3.1.14(eslint@8.57.1)(typescript@5.9.3) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.19) + oclif: + specifier: ^4.23.7 + version: 4.23.7(@types/node@22.19.19) + ts-jest: + specifier: ^29.4.9 + version: 29.4.10(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.19))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + packages/contentstack-clone: dependencies: '@colors/colors': @@ -1452,6 +1507,13 @@ packages: '@contentstack/cli-utilities@1.18.3': resolution: {integrity: sha512-qrkfODXP+SSVNQyoGe2OQ73VmORUcSjqhk2MafothAZiac7vNsyZ4E/rMvskDmJjH6azjjQI99/Ejao78YrCyw==} + '@contentstack/core@1.3.14': + resolution: {integrity: sha512-wQsBcVF94448UKX2Simfnkx8nC+OMMNEhBoXIBeB51CXxIStP44I3HMk/d/m2PvrGqMKdcbGjVBBI/trK1qANQ==} + + '@contentstack/delivery-sdk@5.2.0': + resolution: {integrity: sha512-QaOg120n20yEljKN4OzcDpd4Bnn5U4TTc3UlaY92fkyFc70zE27XPciTQ5EerZNl83PKNNSwjaCpwPSe7ZAdQQ==} + engines: {node: '>=18'} + '@contentstack/management@1.30.2': resolution: {integrity: sha512-DPr/4N35dbclU/PAugB3PGom5MSxYOqicIgP7TPnOlO6TY7r76VIPiycu8yXck71RH+Dmeni1+cMwMlIfzrYOQ==} engines: {node: '>=8.0.0'} @@ -1459,6 +1521,9 @@ packages: '@contentstack/marketplace-sdk@1.5.2': resolution: {integrity: sha512-BAjHLuAkKw+tcF/nE6UrD5QzIm+xFQrk/2vnWajJF3XJ9W/Ovg/5H9BLMpD+AfkwoRaWh8vAqLdt4nVQyr5e+g==} + '@contentstack/types-generator@3.10.0': + resolution: {integrity: sha512-KgyaH6kOxpRoyAc/2E8V6Bc3rzTESIoeEmiEnfWZaLgb7O1U4EUBTUmIzc2MspcRyinchRHFRmWagcDGdMq0fA==} + '@contentstack/utils@1.9.1': resolution: {integrity: sha512-THZM0rNuq0uOSKkKnvzp8lsPDvvdKIvJIcMa9JBv4foL9rC8RWkWffa2yMyb+9m/5HZrdAmpEWdubkGwARa8WQ==} @@ -1567,6 +1632,19 @@ packages: '@fast-csv/parse@4.3.6': resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@gql2ts/from-schema@2.0.0-4': + resolution: {integrity: sha512-dx9OCgL5A6IiDecIFxKyfbpzCsEI6dbyCLlYwQU0dzr+L3IN0YkEIpNtPmO83W1MRZAi34G1AiKZBgbRcT0S9g==} + peerDependencies: + graphql: '>= 0.10 <15' + + '@gql2ts/language-typescript@2.0.0-0': + resolution: {integrity: sha512-7jxNsQutjQ5rUfOxNZuovUXFldIA5hhIjQh95aJLQIRYKBVDCIceM6QpCCbf1iuhwx4P23jx1kc/4uLsAJHnug==} + + '@gql2ts/util@2.0.0-0': + resolution: {integrity: sha512-lMUYZZtoo31klzenNvJ4cMYzFbs3T4peT7fSlCAq1EEthbIuXFtrY8aFIa2Y5BdrnhoHFYDgdlYbr3xJ0j1teA==} + peerDependencies: + graphql: '>= 0.10 <15' + '@graphql-typed-document-node/core@3.2.0': resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -2307,6 +2385,9 @@ packages: '@types/jest@26.0.24': resolution: {integrity: sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==} + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2969,6 +3050,14 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axios-mock-adapter@2.1.0: + resolution: {integrity: sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==} + peerDependencies: + axios: '>= 0.17.0' + + axios@1.16.0: + resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} + axios@1.16.1: resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} @@ -3478,6 +3567,9 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + dedent@0.7.0: + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + dedent@1.7.2: resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: @@ -4431,6 +4523,9 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + humps@2.0.1: + resolution: {integrity: sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g==} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -4551,6 +4646,10 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -5706,6 +5805,11 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + pretty-format@26.6.2: resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} engines: {node: '>= 10'} @@ -5767,6 +5871,10 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + qs@6.15.2: resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} engines: {node: '>=0.6'} @@ -7652,6 +7760,27 @@ snapshots: - debug - supports-color + '@contentstack/core@1.3.14': + dependencies: + axios: 1.16.1(debug@4.4.3) + axios-mock-adapter: 2.1.0(axios@1.16.1) + lodash: 4.18.1 + qs: 6.15.1 + tslib: 2.8.1 + transitivePeerDependencies: + - debug + - supports-color + + '@contentstack/delivery-sdk@5.2.0': + dependencies: + '@contentstack/core': 1.3.14 + '@contentstack/utils': 1.9.1 + axios: 1.16.1(debug@4.4.3) + humps: 2.0.1 + transitivePeerDependencies: + - debug + - supports-color + '@contentstack/management@1.30.2(debug@4.4.3)': dependencies: '@contentstack/utils': 1.9.1 @@ -7676,6 +7805,19 @@ snapshots: - debug - supports-color + '@contentstack/types-generator@3.10.0(graphql@16.14.0)': + dependencies: + '@contentstack/delivery-sdk': 5.2.0 + '@gql2ts/from-schema': 2.0.0-4(graphql@16.14.0) + async: 3.2.6 + axios: 1.16.0 + lodash: 4.18.1 + prettier: 3.8.3 + transitivePeerDependencies: + - debug + - graphql + - supports-color + '@contentstack/utils@1.9.1': {} '@cspotcode/source-map-support@0.8.1': @@ -7841,6 +7983,24 @@ snapshots: lodash.isundefined: 3.0.1 lodash.uniq: 4.5.0 + '@gql2ts/from-schema@2.0.0-4(graphql@16.14.0)': + dependencies: + '@gql2ts/language-typescript': 2.0.0-0(graphql@16.14.0) + '@gql2ts/util': 2.0.0-0(graphql@16.14.0) + dedent: 0.7.0 + graphql: 16.14.0 + + '@gql2ts/language-typescript@2.0.0-0(graphql@16.14.0)': + dependencies: + '@gql2ts/util': 2.0.0-0(graphql@16.14.0) + humps: 2.0.1 + transitivePeerDependencies: + - graphql + + '@gql2ts/util@2.0.0-0(graphql@16.14.0)': + dependencies: + graphql: 16.14.0 + '@graphql-typed-document-node/core@3.2.0(graphql@16.14.0)': dependencies: graphql: 16.14.0 @@ -9019,6 +9179,11 @@ snapshots: jest-diff: 26.6.2 pretty-format: 26.6.2 + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -10075,6 +10240,20 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axios-mock-adapter@2.1.0(axios@1.16.1): + dependencies: + axios: 1.16.1(debug@4.4.3) + fast-deep-equal: 3.1.3 + is-buffer: 2.0.5 + + axios@1.16.0: + dependencies: + follow-redirects: 1.16.0(debug@4.4.3) + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + axios@1.16.1(debug@4.4.3): dependencies: follow-redirects: 1.16.0(debug@4.4.3) @@ -10601,6 +10780,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(@types/node@22.19.19): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.19.19) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + create-require@1.1.1: {} cross-fetch@4.1.0: @@ -10686,6 +10880,8 @@ snapshots: dependencies: mimic-response: 3.1.0 + dedent@0.7.0: {} + dedent@1.7.2: {} deep-eql@4.1.4: @@ -10941,7 +11137,7 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) eslint-config-xo-space: 0.35.0(eslint@8.57.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-mocha: 10.5.0(eslint@8.57.1) eslint-plugin-n: 15.7.0(eslint@8.57.1) eslint-plugin-perfectionist: 2.11.0(eslint@8.57.1)(typescript@5.9.3) @@ -10983,7 +11179,7 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@9.39.4)(typescript@5.9.3) eslint-config-xo-space: 0.35.0(eslint@9.39.4) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4))(eslint@9.39.4) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4) eslint-plugin-mocha: 10.5.0(eslint@9.39.4) eslint-plugin-n: 15.7.0(eslint@9.39.4) eslint-plugin-perfectionist: 2.11.0(eslint@9.39.4)(typescript@5.9.3) @@ -11192,18 +11388,7 @@ snapshots: tinyglobby: 0.2.16 unrs-resolver: 1.12.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4))(eslint@9.39.4) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) - eslint: 8.57.1 - eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.4(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4) transitivePeerDependencies: - supports-color @@ -11218,7 +11403,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4))(eslint@9.39.4): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4): dependencies: debug: 3.2.7 optionalDependencies: @@ -11299,35 +11484,6 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.9 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.1 - eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1) - hasown: 2.0.3 - is-core-module: 2.16.2 - is-glob: 4.0.3 - minimatch: 3.1.5 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4): dependencies: '@rtsao/scc': 1.1.0 @@ -11357,7 +11513,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4))(eslint@9.39.4): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -11368,7 +11524,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4 eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4))(eslint@9.39.4) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4) hasown: 2.0.3 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -12492,6 +12648,8 @@ snapshots: human-signals@2.1.0: {} + humps@2.0.1: {} + husky@9.1.7: {} iconv-lite@0.4.24: @@ -12684,6 +12842,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-buffer@2.0.5: {} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 @@ -12960,6 +13120,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@22.19.19): + dependencies: + '@jest/core': 29.7.0(ts-node@8.10.2(typescript@4.9.5)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.19.19) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.19.19) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-config@29.7.0(@types/node@14.18.63)(ts-node@8.10.2(typescript@4.9.5)): dependencies: '@babel/core': 7.29.0 @@ -13022,6 +13201,36 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@22.19.19): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.19.19 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@26.6.2: dependencies: chalk: 4.1.2 @@ -13258,6 +13467,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@22.19.19): + dependencies: + '@jest/core': 29.7.0(ts-node@8.10.2(typescript@4.9.5)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.19.19) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -14143,6 +14364,8 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.8.3: {} + pretty-format@26.6.2: dependencies: '@jest/types': 26.6.2 @@ -14210,6 +14433,10 @@ snapshots: pure-rand@6.1.0: {} + qs@6.15.1: + dependencies: + side-channel: 1.1.0 + qs@6.15.2: dependencies: side-channel: 1.1.0 @@ -15019,6 +15246,26 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.29.0) jest-util: 29.7.0 + ts-jest@29.4.10(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.19))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.9 + jest: 29.7.0(@types/node@22.19.19) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.8.0 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + jest-util: 29.7.0 + ts-node@10.9.2(@types/node@14.18.63)(typescript@4.9.5): dependencies: '@cspotcode/source-map-support': 0.8.1