From d719964a4a18a442d551edeaf596e09e4882a583 Mon Sep 17 00:00:00 2001 From: raj pandey Date: Mon, 6 Apr 2026 23:32:55 +0530 Subject: [PATCH] update: added unit tests and skills --- .cursor/rules/README.md | 15 + .cursor/rules/content-type-plugin.mdc | 22 + .cursor/rules/dev-workflow.mdc | 18 + .cursor/rules/eslint-config.mdc | 12 + .cursor/rules/oclif-docs.mdc | 15 + .cursor/rules/review.mdc | 18 + .cursor/rules/testing.mdc | 16 + .cursor/rules/typescript-build.mdc | 14 + .cursor/skills/README.md | 11 + .../contentstack-cli-content-type/SKILL.md | 69 + .../references/architecture.md | 58 + .../references/commands.md | 48 + .cursor/skills/review/SKILL.md | 24 + .cursor/skills/review/references/checklist.md | 52 + .cursor/skills/testing/SKILL.md | 36 + .../skills/testing/references/conventions.md | 26 + .../skills/testing/references/jest-mocking.md | 37 + .vscode/settings.json | 4 + AGENTS.md | 42 + jest.config.js | 30 +- package-lock.json | 1183 ++++++++--------- package.json | 17 +- tests/audit.test.ts | 5 - tests/commands/content-type/audit.test.ts | 101 ++ .../content-type/compare-remote.test.ts | 131 ++ tests/commands/content-type/compare.test.ts | 159 +++ tests/commands/content-type/details.test.ts | 97 ++ tests/commands/content-type/diagram.test.ts | 125 ++ tests/commands/content-type/list.test.ts | 82 ++ tests/core/command.test.ts | 183 +++ tests/core/content-type/audit.test.ts | 38 + tests/core/content-type/compare.test.ts | 56 + tests/core/content-type/details.test.ts | 136 ++ tests/core/content-type/diagram.test.ts | 165 +++ tests/core/content-type/formatting.test.ts | 102 ++ tests/core/content-type/list.test.ts | 49 + tests/core/contentstack/client.test.ts | 138 ++ tests/core/contentstack/error.test.ts | 16 + tests/utils/index.test.ts | 292 ++++ 39 files changed, 3022 insertions(+), 620 deletions(-) create mode 100644 .cursor/rules/README.md create mode 100644 .cursor/rules/content-type-plugin.mdc create mode 100644 .cursor/rules/dev-workflow.mdc create mode 100644 .cursor/rules/eslint-config.mdc create mode 100644 .cursor/rules/oclif-docs.mdc create mode 100644 .cursor/rules/review.mdc create mode 100644 .cursor/rules/testing.mdc create mode 100644 .cursor/rules/typescript-build.mdc create mode 100644 .cursor/skills/README.md create mode 100644 .cursor/skills/contentstack-cli-content-type/SKILL.md create mode 100644 .cursor/skills/contentstack-cli-content-type/references/architecture.md create mode 100644 .cursor/skills/contentstack-cli-content-type/references/commands.md create mode 100644 .cursor/skills/review/SKILL.md create mode 100644 .cursor/skills/review/references/checklist.md create mode 100644 .cursor/skills/testing/SKILL.md create mode 100644 .cursor/skills/testing/references/conventions.md create mode 100644 .cursor/skills/testing/references/jest-mocking.md create mode 100644 .vscode/settings.json create mode 100644 AGENTS.md delete mode 100644 tests/audit.test.ts create mode 100644 tests/commands/content-type/audit.test.ts create mode 100644 tests/commands/content-type/compare-remote.test.ts create mode 100644 tests/commands/content-type/compare.test.ts create mode 100644 tests/commands/content-type/details.test.ts create mode 100644 tests/commands/content-type/diagram.test.ts create mode 100644 tests/commands/content-type/list.test.ts create mode 100644 tests/core/command.test.ts create mode 100644 tests/core/content-type/audit.test.ts create mode 100644 tests/core/content-type/compare.test.ts create mode 100644 tests/core/content-type/details.test.ts create mode 100644 tests/core/content-type/diagram.test.ts create mode 100644 tests/core/content-type/formatting.test.ts create mode 100644 tests/core/content-type/list.test.ts create mode 100644 tests/core/contentstack/client.test.ts create mode 100644 tests/core/contentstack/error.test.ts create mode 100644 tests/utils/index.test.ts diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md new file mode 100644 index 0000000..d14f770 --- /dev/null +++ b/.cursor/rules/README.md @@ -0,0 +1,15 @@ +# Cursor rules (contentstack-cli-content-type) + +Rules are `.mdc` files under `.cursor/rules/`. For each rule here, **`alwaysApply` is `false`**: Cursor applies it when files matching **`globs`** are in context (open or relevant to the task). + +| Rule file | Globs | Purpose | Related skill | +|-----------|--------|---------|----------------| +| [dev-workflow.mdc](dev-workflow.mdc) | `src/**/*.ts`, `tests/**/*.ts`, `package.json`, `jest.config.js` | Run `npm test` / ESLint / `test:coverage` before PR; oclif docs when commands change | [AGENTS.md](../../AGENTS.md), [.cursor/skills/testing/SKILL.md](../skills/testing/SKILL.md) | +| [content-type-plugin.mdc](content-type-plugin.mdc) | `src/commands/**`, `src/core/**`, `src/utils/**`, `src/types/**`, `src/config/**` | OCLIF commands, `ContentTypeCommand`, utils/types/config, Management SDK + axios client, no secret logging, `oclif readme` when commands change | [.cursor/skills/contentstack-cli-content-type/SKILL.md](../skills/contentstack-cli-content-type/SKILL.md) | +| [testing.mdc](testing.mdc) | `tests/**/*.ts`, `jest.config.js` | Jest + ts-jest, mock boundaries, `npm test` / posttest ESLint | [.cursor/skills/testing/SKILL.md](../skills/testing/SKILL.md) | +| [review.mdc](review.mdc) | `compare.ts`, `diagram.ts`, `src/core/contentstack/**`, `package.json` | Security and PR review highlights for high-risk paths and dependency changes | [.cursor/skills/review/SKILL.md](../skills/review/SKILL.md) | +| [typescript-build.mdc](typescript-build.mdc) | `tsconfig.json` | `strict`, `rootDir`/`outDir`, `tsc -b` / prepack alignment | [.cursor/skills/contentstack-cli-content-type/SKILL.md](../skills/contentstack-cli-content-type/SKILL.md) | +| [eslint-config.mdc](eslint-config.mdc) | `.eslintrc` | oclif-typescript style, posttest eslint expectations | — | +| [oclif-docs.mdc](oclif-docs.mdc) | `README.md`, `oclif.manifest.json` | Regenerate command docs/manifest via `oclif readme` / `oclif manifest` after command changes | [.cursor/skills/contentstack-cli-content-type/SKILL.md](../skills/contentstack-cli-content-type/SKILL.md) | + +See also [.cursor/skills/README.md](../skills/README.md) for the full skill index. diff --git a/.cursor/rules/content-type-plugin.mdc b/.cursor/rules/content-type-plugin.mdc new file mode 100644 index 0000000..38b0d89 --- /dev/null +++ b/.cursor/rules/content-type-plugin.mdc @@ -0,0 +1,22 @@ +--- +description: OCLIF content-type plugin — commands, core, utils, types, config (ContentTypeCommand, SDK, CMA client) +globs: + - src/commands/**/*.ts + - src/core/**/*.ts + - src/utils/**/*.ts + - src/types/**/*.ts + - src/config/**/*.ts +alwaysApply: false +--- + +# content-type plugin + +- Commands live under `src/commands/content-type/` and extend **`ContentTypeCommand`** from `src/core/command.ts`. Parse flags, call **`setup(flags)`** (await where the command awaits it), then delegate to **`src/core/content-type/`** and **`src/utils/index.ts`**. +- **utils**: Management SDK helpers (`getStack`, `getContentTypes`, pagination via `src/config/index.ts`); keep **`handleErrorMsg`** / `process.exit` patterns consistent with existing code. +- **types / config**: Shared types and query limits affect commands and core—align changes with consumers under `src/core` and `src/commands`. +- Build **`managementSDKClient`** when using stack/content-type fetches; use **`ContentstackClient`** from `src/core/contentstack/client.ts` for audit logs and references REST paths. +- **Never log** tokens, `authtoken`, `authorization` headers, or raw management credentials. +- User-facing errors for REST failures should follow **`ContentstackError`** / `buildError` patterns in `client.ts`. +- After changing command IDs, flags, or descriptions, regenerate docs with **`oclif readme`** (see `package.json` `prepack` / `version`). + +Full detail: `.cursor/skills/contentstack-cli-content-type/SKILL.md` and `references/` there. diff --git a/.cursor/rules/dev-workflow.mdc b/.cursor/rules/dev-workflow.mdc new file mode 100644 index 0000000..4082175 --- /dev/null +++ b/.cursor/rules/dev-workflow.mdc @@ -0,0 +1,18 @@ +--- +description: Local validation before PR — tests, ESLint, coverage, oclif docs +globs: + - src/**/*.ts + - tests/**/*.ts + - package.json + - jest.config.js +alwaysApply: true +--- + +# Development workflow + +- Run **`npm test`** before opening a PR; fix failures before review. +- **`npm run posttest`** runs ESLint on `.ts` files (see [package.json](package.json)); keep lint clean when shipping changes. +- When changing behavior in **`src/core/`** or **`src/utils/`**, run **`npm run test:coverage`** and ensure coverage does not drop below [jest.config.js](jest.config.js) thresholds. +- After changing command IDs, flags, or descriptions, regenerate CLI docs with **`npm run prepack`** or the **`version`** script so `README.md` and `oclif.manifest.json` stay aligned. + +Full context: [AGENTS.md](../../AGENTS.md) at the repository root. diff --git a/.cursor/rules/eslint-config.mdc b/.cursor/rules/eslint-config.mdc new file mode 100644 index 0000000..ea9e337 --- /dev/null +++ b/.cursor/rules/eslint-config.mdc @@ -0,0 +1,12 @@ +--- +description: ESLint configuration and style expectations for contentstack-cli-content-type +globs: + - .eslintrc +alwaysApply: false +--- + +# ESLint + +- Config extends **oclif-typescript** and **`@typescript-eslint/recommended`** (see [`.eslintrc`](.eslintrc)). +- Notable conventions: **single quotes**, **`eqeqeq`** smart, **`@typescript-eslint/no-unused-vars`** (args none), **`no-var`**. New rule overrides should not break **`npm run posttest`** / **`eslint . --ext .ts --config .eslintrc`**. +- Prefer fixing lint in **`src/`** and **`tests/`** rather than disabling rules project-wide without team agreement. diff --git a/.cursor/rules/oclif-docs.mdc b/.cursor/rules/oclif-docs.mdc new file mode 100644 index 0000000..e6b5bfc --- /dev/null +++ b/.cursor/rules/oclif-docs.mdc @@ -0,0 +1,15 @@ +--- +description: Oclif-generated README command docs and CLI manifest for contentstack-cli-content-type +globs: + - README.md + - oclif.manifest.json +alwaysApply: false +--- + +# Oclif docs and manifest + +- **README.md** command sections and **oclif.manifest.json** are generated by **`oclif readme`** and **`oclif manifest`** (see [`package.json`](package.json) `prepack`, `version`). +- Prefer changing **`src/commands/**/*.ts`** (descriptions, flags, examples), then run **`npm run prepack`** or the relevant **`oclif`** script so README and manifest stay consistent—avoid hand-editing generated command blocks unless you will regenerate immediately after. +- If you only need doc tweaks without a full prepack, follow the same workflow the team uses for releases so CI and published packages stay aligned. + +Full workflow: `.cursor/skills/contentstack-cli-content-type/SKILL.md`. diff --git a/.cursor/rules/review.mdc b/.cursor/rules/review.mdc new file mode 100644 index 0000000..38d5f9c --- /dev/null +++ b/.cursor/rules/review.mdc @@ -0,0 +1,18 @@ +--- +description: PR and security review context for contentstack-cli-content-type (compare, diagram, client, dependencies) +globs: + - src/core/content-type/compare.ts + - src/core/content-type/diagram.ts + - src/core/contentstack/**/*.ts + - package.json +alwaysApply: false +--- + +# Review + +- **Never log** tokens, `authtoken`, `authorization` headers, or raw management credentials. Stack API keys in user-facing errors should follow existing patterns in `client.ts` / `ContentstackError`. +- **Compare** (`compare.ts`): temp HTML, browser open, and diff pipeline—review for accidental data exposure and path handling. +- **Diagram** (`diagram.ts`): Graphviz / file output paths and large-model behavior when changing layout or IO. +- **Dependencies** (`package.json`): version bumps on axios, diff2html, git-diff, node-graphviz, tmp, cli-ux—check changelog and security advisories. + +Full checklist: `.cursor/skills/review/SKILL.md` and `references/checklist.md`. diff --git a/.cursor/rules/testing.mdc b/.cursor/rules/testing.mdc new file mode 100644 index 0000000..38c8915 --- /dev/null +++ b/.cursor/rules/testing.mdc @@ -0,0 +1,16 @@ +--- +description: Jest tests and config for contentstack-cli-content-type +globs: + - tests/**/*.ts + - jest.config.js +alwaysApply: false +--- + +# Testing + +- Use **Jest** with **ts-jest** per `jest.config.js`. Tests live under **`tests/`** (and may match `*.test.ts` at repo root per config). +- **Mock HTTP and SDK boundaries** — mock `ContentstackClient`, axios, or fake `managementSDKClient` chains. **No live Contentstack API** calls in unit tests. +- Run **`npm test`**; **`npm run test:coverage`** for coverage against [jest.config.js](jest.config.js) thresholds; **`posttest`** runs ESLint on `.ts` files — keep both green when shipping changes. +- Add or update tests for behavioral changes in `src/core/` and `src/utils/` when practical. + +Full detail: `.cursor/skills/testing/SKILL.md`; `references/conventions.md` and `references/jest-mocking.md`. diff --git a/.cursor/rules/typescript-build.mdc b/.cursor/rules/typescript-build.mdc new file mode 100644 index 0000000..78584e6 --- /dev/null +++ b/.cursor/rules/typescript-build.mdc @@ -0,0 +1,14 @@ +--- +description: TypeScript compiler options and build layout for contentstack-cli-content-type +globs: + - tsconfig.json +alwaysApply: false +--- + +# TypeScript build + +- Preserve [`tsconfig.json`](tsconfig.json) intent: **`strict: true`**, **`rootDir: src`**, **`outDir: lib`**, **`module: commonjs`**, **`target: es2017`**. +- Changes to **`include`**, **`compilerOptions.paths`**, or **`rootDir`/`outDir`** affect the whole package and published `lib/` output—validate with **`tsc -b`** (as in `prepack`). +- Source of truth for app code is **`src/`**; compiled JS lives under **`lib/`** (do not treat `lib/` as hand-edited). + +See `.cursor/skills/contentstack-cli-content-type/SKILL.md` for release/prepack workflow. diff --git a/.cursor/skills/README.md b/.cursor/skills/README.md new file mode 100644 index 0000000..5c7b577 --- /dev/null +++ b/.cursor/skills/README.md @@ -0,0 +1,11 @@ +# Project skills (contentstack-cli-content-type) + +Cursor loads **project skills** from `.cursor/skills//SKILL.md`. Each folder below is one skill; open `SKILL.md` for instructions and follow links into `references/` for detail. + +| Skill folder | Purpose | When to load | +|--------------|---------|----------------| +| [contentstack-cli-content-type](contentstack-cli-content-type/SKILL.md) | Plugin architecture, `ContentTypeCommand`, CMA client, commands, build/readme workflow | Editing `src/commands`, `src/core`, `src/utils`, auth, compare/diagram behavior, oclif | +| [testing](testing/SKILL.md) | Jest, `tests/`, mocking, `npm test` / `test:coverage` / ESLint posttest; see `references/conventions.md` | Adding or changing tests, Jest config, mocks | +| [review](review/SKILL.md) | PR review checklist and risk areas | Reviewing PRs, dependency bumps, security-sensitive edits | + +Use the skill whose **description** frontmatter best matches the task; combine **testing** with **contentstack-cli-content-type** when implementing features that need tests. diff --git a/.cursor/skills/contentstack-cli-content-type/SKILL.md b/.cursor/skills/contentstack-cli-content-type/SKILL.md new file mode 100644 index 0000000..6fb8784 --- /dev/null +++ b/.cursor/skills/contentstack-cli-content-type/SKILL.md @@ -0,0 +1,69 @@ +--- +name: contentstack-cli-content-type +description: >- + Develops and maintains the contentstack-cli-content-type csdx plugin (content-type list, + details, audit, compare, compare-remote, diagram). Use when editing this repository, + ContentTypeCommand setup and flags, CMA/Management SDK usage, axios audit/references calls, + diff/compare HTML output, Graphviz diagrams, or oclif readme/manifest workflows. +--- + +# contentstack-cli-content-type + +## Repository role + +npm package `contentstack-cli-content-type`: a **Contentstack CLI** (`csdx`) plugin that reads Content Type metadata from a stack—list, field details, audit log lines, version or cross-stack comparison, and stack content-model diagrams. + +## Code layout + +| Area | Path | +|------|------| +| Command classes (oclif) | `src/commands/content-type/*.ts` | +| Shared base | `src/core/command.ts` — `ContentTypeCommand` extends `@contentstack/cli-command` `Command` | +| Core output / logic | `src/core/content-type/*.ts` | +| HTTP client (axios CMA) | `src/core/contentstack/client.ts`, `src/core/contentstack/error.ts` | +| Stack / CT fetch helpers | `src/utils/index.ts` (uses Management SDK from `@contentstack/cli-utilities`) | +| Types | `src/types/index.ts` | +| Config (pagination limits) | `src/config/index.ts` | + +Commands **parse flags**, call **`setup(flags)`** (see below), build **`managementSDKClient`**, then call utils + core builders. See [references/architecture.md](references/architecture.md) and [references/commands.md](references/commands.md). + +## Authentication and stack identity + +1. `authenticationHandler.getAuthDetails()`; must have **access token** or command exits with `auth:login` message (`exit: 2`). +2. User must pass **either** a **management token alias** (`-a` / `--alias` or `--token-alias`) **or** **stack API key** (`-k` / `--stack-api-key`) or deprecated `--stack` (maps to stack key). If neither: error and `process.exit(1)` (message references “token alias or stack UID”). +3. Token alias: `getToken(alias)` → `apiKey` from token; warns if token type is not `management`. +4. `ContentTypeCommand` constructs **`ContentstackClient(this.cmaHost, authToken)`** for REST calls that use `api_key` in headers. + +**Do not log** tokens, `authorization` / `authtoken` headers, or full CLI credentials. + +## Two ways to call APIs + +- **Axios `ContentstackClient`**: `GET https://{cmaHost}/v3/...` with default headers `authorization` (if Bearer) or `authtoken`, plus per-request `headers: { api_key }`. Used for audit logs and references. Errors → `ContentstackError` via `buildError`. +- **Management SDK** (`managementSDKClient({ host, 'X-CS-CLI': ... })`): stack fetch, content types, global fields, content type by version—see `src/utils/index.ts`. + +## Build and CLI metadata + +From `package.json`: + +- **`prepack`**: `rm -rf lib && tsc -b && oclif manifest && oclif readme` — publishable `lib/`, manifest, and README command docs. +- **`version`**: `oclif readme && git add README.md`. + +After changing commands, flags, or descriptions, run the appropriate script so **README** and **oclif.manifest.json** stay in sync. + +## Short command names (csdx) + +`package.json` → `csdxConfig.shortCommandName`: + +| Command id | Short name | +|------------|------------| +| `content-type:audit` | CTAUDIT | +| `content-type:compare` | CTCMP | +| `content-type:compare-remote` | CTCMP-R | +| `content-type:details` | CTDET | +| `content-type:diagram` | CTDIAG | +| `content-type:list` | CTLS | + +## Further reading + +- [references/architecture.md](references/architecture.md) — command → core mapping, auth flow, CMA shape. +- [references/commands.md](references/commands.md) — flags, UX notes, files to edit per command. diff --git a/.cursor/skills/contentstack-cli-content-type/references/architecture.md b/.cursor/skills/contentstack-cli-content-type/references/architecture.md new file mode 100644 index 0000000..647a3d5 --- /dev/null +++ b/.cursor/skills/contentstack-cli-content-type/references/architecture.md @@ -0,0 +1,58 @@ +# Architecture + +## Command → core modules + +| Command file | Core / utilities | Notes | +|--------------|------------------|--------| +| `src/commands/content-type/audit.ts` | `core/content-type/audit.ts`, `utils` (`getStack`, `getUsers`, `getContentType`), `client.getContentTypeAuditLogs` | Audit + users for display | +| `src/commands/content-type/compare.ts` | `core/content-type/compare.ts`, `utils` | Same-stack two versions; optional `--left` / `--right` | +| `src/commands/content-type/compare-remote.ts` | `core/content-type/compare.ts` (same `buildOutput`), `utils` | Two stacks; `setup` uses origin stack key only | +| `src/commands/content-type/details.ts` | `core/content-type/details.ts`, `utils`, `client.getContentTypeReferences` | `--path` / `--no-path` | +| `src/commands/content-type/diagram.ts` | `core/content-type/diagram.ts`, `utils` (`getStack`, `getContentTypes`, `getGlobalFields`) | Writes file via Graphviz | +| `src/commands/content-type/list.ts` | `core/content-type/list.ts`, `utils` | `--order title|modified` | + +Formatting helpers live under `src/core/content-type/formatting.ts` where imported by core modules. + +## Auth flow (high level) + +```mermaid +flowchart LR + subgraph setup [ContentTypeCommand.setup] + A[getAuthDetails] + B{accessToken?} + C[error auth:login] + D{alias or stack key?} + E[exit 1 missing stack] + F[getToken or use stack API key] + G[ContentstackClient] + end + A --> B + B -->|no| C + B -->|yes| D + D -->|neither| E + D -->|ok| F + F --> G +``` + +- **`compare-remote`**: `setup` is called with `{ alias: undefined, stack: flags["origin-stack"] }` so `apiKey` is the **origin** stack API key; remote stack is passed only in `getStack` / `getContentType` calls. + +## CMA request shape (ContentstackClient) + +- **Base URL**: `https://{cmaHost}/v3/` (`cmaHost` from command context). +- **Default axios headers**: `authorization: ` if token string includes `Bearer`, else `authtoken: `. +- **Per-request**: `headers: { api_key: }` for stack-scoped routes. + +| Method | HTTP | Path / params | +|--------|------|----------------| +| `getContentTypeAuditLogs` | GET | `/audit-logs` — `params.query.$and`: `module: content_type`, `metadata.uid` | +| `getContentTypeReferences` | GET | `/content_types/{uid}/references` — `include_global_fields: true` | + +Errors: response `data.errors` → `ContentstackError`; optional suffix with stack API key when `data.errors.api_key` and context `api_key` are set. + +## Compare output pipeline + +`core/content-type/compare.ts` builds a unified diff from two JSON snapshots (`git-diff`), parses with **diff2html**, writes a **temporary HTML** file, opens it in the browser (`cli-ux` / `cli.open`). Not a terminal table. + +## Diagram pipeline + +`core/content-type/diagram.ts` builds a DOT graph, runs **node-graphviz** (`graphviz` binary must be available on the system for SVG rendering). Output path is sanitized where utilities apply. diff --git a/.cursor/skills/contentstack-cli-content-type/references/commands.md b/.cursor/skills/contentstack-cli-content-type/references/commands.md new file mode 100644 index 0000000..33820fc --- /dev/null +++ b/.cursor/skills/contentstack-cli-content-type/references/commands.md @@ -0,0 +1,48 @@ +# Commands reference + +Primary sources: `README.md` and `src/commands/content-type/*.ts`. + +## `content-type:list` + +- **Flags**: `--stack-api-key` (`-k`), `--stack` (deprecated → use stack key), `--token-alias` / `--alias` (`-a`), `--order` (`-o`) `title` | `modified` (default `title`). +- **Files**: `src/commands/content-type/list.ts`, `src/core/content-type/list.ts`. +- **Behavior**: Lists Content Types for the stack; table output via core builder. + +## `content-type:details` + +- **Flags**: stack identity flags as above; `--content-type` (`-c`) required; `--path` / `--no-path` (`-p`) — default shows path column; use `--no-path` on narrow terminals (README). +- **Files**: `src/commands/content-type/details.ts`, `src/core/content-type/details.ts`. +- **Behavior**: Fetches CT + **references** via `ContentstackClient.getContentTypeReferences`. + +## `content-type:audit` + +- **Flags**: stack identity + `--content-type` (`-c`) required. +- **Files**: `src/commands/content-type/audit.ts`, `src/core/content-type/audit.ts`. +- **Behavior**: Audit logs via `getContentTypeAuditLogs`; README notes **audit log retention** (e.g. 90 days) per Contentstack docs. + +## `content-type:compare` + +- **Flags**: stack identity + `--content-type` (`-c`); optional `--left` (`-l`) / `--right` (`-r`) **integers** (both required if either set). If omitted, command infers latest version vs previous from discovery fetch. +- **Files**: `src/commands/content-type/compare.ts`, `src/core/content-type/compare.ts`. +- **Behavior**: Side-by-side diff in **HTML** in a browser; not stdout-only. Warns if left === right. + +## `content-type:compare-remote` + +- **Flags**: `--origin-stack` (`-o`) and `--remote-stack` (`-r`) **required** (stack API keys); `--content-type` (`-c`) required. No token-alias flow for two stacks—setup uses **origin** stack key for session. +- **Files**: `src/commands/content-type/compare-remote.ts`, same `core/content-type/compare.ts` as same-stack compare. +- **Behavior**: Same HTML diff pipeline; compares CT JSON from two stacks. Warns if origin === remote API key. + +## `content-type:diagram` + +- **Flags**: stack identity; `--output` (`-o`) **required** (full path); `--direction` (`-d`) `portrait` | `landscape` (required in schema, default portrait); `--type` (`-t`) `svg` | `dot` (default svg). +- **Files**: `src/commands/content-type/diagram.ts`, `src/core/content-type/diagram.ts`. +- **Behavior**: Loads all content types + global fields; renders graph. **Graphviz** must be installed for typical SVG generation; DOT export available. README documents `-t dot` for raw DOT language. + +## Editing checklist + +| Change | Touch first | +|--------|-------------| +| New flag / description | Command file under `src/commands/content-type/`, then `oclif readme` | +| Output format / table | `src/core/content-type/*.ts`, `formatting.ts` | +| REST audit/references | `src/core/contentstack/client.ts`, `error.ts` | +| SDK pagination / fetch | `src/utils/index.ts`, `src/config/index.ts` | diff --git a/.cursor/skills/review/SKILL.md b/.cursor/skills/review/SKILL.md new file mode 100644 index 0000000..02bbbe5 --- /dev/null +++ b/.cursor/skills/review/SKILL.md @@ -0,0 +1,24 @@ +--- +name: review +description: >- + Reviews pull requests and risky changes for the contentstack-cli-content-type plugin. + Use when reviewing diffs, security-sensitive edits, dependency upgrades, or changes to + compare/diagram/temp-file behavior, ESLint, and tests. +--- + +# review + +## Purpose + +Provide consistent **security**, **correctness**, and **maintainability** review for this repository. The plugin handles stack API keys in error messages, opens **HTML** diffs in a browser, and writes **diagram** files via Graphviz. + +## Highlights + +- **Secrets**: Never approve logging of tokens, `authtoken` / `authorization` values, or raw management tokens. +- **Compare / diagram**: Changes to [src/core/content-type/compare.ts](../../../src/core/content-type/compare.ts) or [diagram.ts](../../../src/core/content-type/diagram.ts) deserve extra scrutiny (temp files, browser open, paths, binary dependency). +- **Dependencies**: axios, diff2html, git-diff, node-graphviz, tmp, cli-ux—review changelog and supply-chain for version bumps. +- **Quality**: TypeScript and **eslint-config-oclif-typescript** ([.eslintrc](../../../.eslintrc)); behavioral changes should include or update **Jest** tests where appropriate. + +## Full checklist + +Use [references/checklist.md](references/checklist.md) for the printable severity-labeled checklist. diff --git a/.cursor/skills/review/references/checklist.md b/.cursor/skills/review/references/checklist.md new file mode 100644 index 0000000..790a585 --- /dev/null +++ b/.cursor/skills/review/references/checklist.md @@ -0,0 +1,52 @@ +# PR review checklist + +Use **Critical** / **Important** / **Suggestion** when leaving feedback. + +## Security and privacy + +| Severity | Item | +|----------|------| +| Critical | No logging or serializing of **access tokens**, **management tokens**, or **Bearer** strings. | +| Critical | No new `console.log` of full API responses that may contain secrets. | +| Important | Stack API keys appear in user-facing errors only in line with [src/core/contentstack/client.ts](../../../../src/core/contentstack/client.ts) (`buildError` + optional key suffix). | + +## Correctness + +| Severity | Item | +|----------|------| +| Critical | Command flags and `setup(flags)` behavior remain consistent; **compare-remote** still resolves origin vs remote stacks correctly. | +| Important | **Compare**: left/right version logic and warning when versions are equal; HTML output path and browser open behavior unchanged unless intentionally redesigned. | +| Important | **Diagram**: output path validation; Graphviz / DOT paths; orientation and file type flags. | +| Suggestion | Edge cases for empty audit logs, missing references, or single-version content types. | + +## Compare and diagram (touching core) + +| Severity | Item | +|----------|------| +| Critical | [compare.ts](../../../../src/core/content-type/compare.ts): temp HTML creation does not write sensitive data beyond the diff; file handling is safe on failure paths. | +| Important | [diagram.ts](../../../../src/core/content-type/diagram.ts): `sanitizePath` / path usage; large stack models do not cause unbounded memory without consideration. | +| Suggestion | User messaging when Graphviz is missing or SVG generation fails. | + +## Dependencies + +| Severity | Item | +|----------|------| +| Important | **axios**: security advisories; upgrade notes. | +| Important | **diff2html**, **git-diff**, **tmp**, **cli-ux**: behavior changes affecting compare UX. | +| Important | **node-graphviz**: compatibility with supported Node and system Graphviz. | +| Suggestion | **moment** (if touched): prefer minimal churn; note maintenance status of dependencies. | + +## Tests and tooling + +| Severity | Item | +|----------|------| +| Important | New behavior in `src/core/` or `src/utils/` has **Jest** coverage or a clear reason why not. | +| Important | `npm test` and **ESLint** (`posttest` / [`.eslintrc`](../../../../.eslintrc)) pass. | +| Suggestion | Tests mock HTTP/SDK boundaries; no accidental live API calls. | + +## Documentation + +| Severity | Item | +|----------|------| +| Important | If commands or flags change, **README** (generated via `oclif readme`) is updated via `prepack` / `version` workflow. | +| Suggestion | User-facing strings and examples match `src/commands/content-type/*.ts` examples. | diff --git a/.cursor/skills/testing/SKILL.md b/.cursor/skills/testing/SKILL.md new file mode 100644 index 0000000..c1c0aec --- /dev/null +++ b/.cursor/skills/testing/SKILL.md @@ -0,0 +1,36 @@ +--- +name: testing +description: >- + Runs and extends Jest tests for the contentstack-cli-content-type plugin. Use when adding + or changing tests under tests/, mocking HTTP or ContentstackClient, configuring Jest, + or running npm test and posttest ESLint. +--- + +# testing + +## Runner and config + +- **Framework**: [Jest](https://jestjs.io/) with **ts-jest** for TypeScript. +- **Config**: [jest.config.js](../../../jest.config.js) at repo root — `testMatch` includes `**/tests/**/*.+(ts|tsx)` and common `*.test.ts` / `*.spec.ts` patterns. +- **Test location**: [tests/](../../../tests/) (e.g. [audit.test.ts](../../../tests/audit.test.ts)). + +## Scripts + +| Script | Behavior | +|--------|----------| +| `npm test` | Runs Jest | +| `npm run test:coverage` | Jest with coverage (see [jest.config.js](../../../jest.config.js)) | +| `npm run posttest` | After tests (when invoked via npm lifecycle), ESLint runs per [package.json](../../../package.json): `eslint . --ext .ts --config .eslintrc` | + +When validating changes, run **`npm test`**; ensure **ESLint** still passes (posttest or `eslint` directly). Use **`npm run test:coverage`** when changing `src/core/` or `src/utils/` behavior. + +## What to test + +- **Pure functions** and **core builders** in `src/core/content-type/` with inputs/outputs mocked at the boundary. +- **ContentstackClient**: mock `get` on the axios instance or mock the whole module—**no live CMA** or real stack keys in unit tests. +- **Commands**: prefer testing **core** and **utils** first; command tests may require heavy mocking of `@contentstack/cli-utilities` (auth, cliux, management SDK). + +## Further reading + +- [references/conventions.md](references/conventions.md) — naming, what to test first, no `.only`/`.skip`, coverage goal. +- [references/jest-mocking.md](references/jest-mocking.md) — mocking boundaries and minimal patterns. diff --git a/.cursor/skills/testing/references/conventions.md b/.cursor/skills/testing/references/conventions.md new file mode 100644 index 0000000..75384e0 --- /dev/null +++ b/.cursor/skills/testing/references/conventions.md @@ -0,0 +1,26 @@ +# Testing conventions + +## Naming + +- Prefer **`it` / `test`** descriptions that state behavior: `should when ` (or close variants), e.g. `should return sorted titles when order is title`. +- Group related cases with **`describe`** blocks named after the unit under test (module, function, or command behavior). + +## What to test first + +1. **Pure helpers** and **core builders** in `src/core/content-type/` — easiest to drive with inputs and assert outputs. +2. **`src/utils/index.ts`** — mock Management SDK / stack boundaries. +3. **Command classes** — only when needed; they pull in `@contentstack/cli-utilities` (auth, cliux) and need heavier mocks. + +## Mocking boundaries + +- **No live Contentstack API** calls and no real stack keys in unit tests. +- Mock **`ContentstackClient`** (axios), **Management SDK** chains, or **`authenticationHandler`** as described in [jest-mocking.md](jest-mocking.md). + +## Commits and CI hygiene + +- Do not commit **`describe.only`**, **`it.only`**, **`test.only`**, or **`.skip`** variants. +- Run **`npm test`** before pushing; use **`npm run test:coverage`** when changing core logic to confirm coverage (see [jest.config.js](../../../../jest.config.js) thresholds and [AGENTS.md](../../../../AGENTS.md)). + +## Coverage goal + +- Long-term target: about **80%** lines, branches, and functions. Global thresholds in Jest are raised from the baseline as tests are added; see [AGENTS.md](../../../../AGENTS.md). diff --git a/.cursor/skills/testing/references/jest-mocking.md b/.cursor/skills/testing/references/jest-mocking.md new file mode 100644 index 0000000..96fccb9 --- /dev/null +++ b/.cursor/skills/testing/references/jest-mocking.md @@ -0,0 +1,37 @@ +# Jest mocking (this repo) + +## Principles + +1. **No live API calls** — Do not hit Contentstack Management API or real stacks in unit tests. +2. **Mock at the boundary** — Prefer mocking `ContentstackClient` methods, axios, or `@contentstack/cli-utilities` pieces (e.g. `managementSDKClient`, `authenticationHandler`) when testing command flows. +3. **Behavioral changes** — New or changed behavior in `src/core/` or `src/utils/` should come with tests where practical; this repo does not define a CI coverage percentage in the skill—follow whatever the project adds later. + +## Mocking ContentstackClient + +Example pattern: spy or replace methods that perform HTTP: + +```typescript +import ContentstackClient from '../src/core/contentstack/client' + +jest.mock('../src/core/contentstack/client', () => { + return jest.fn().mockImplementation(() => ({ + getContentTypeAuditLogs: jest.fn().mockResolvedValue({ logs: [] }), + getContentTypeReferences: jest.fn().mockResolvedValue({}), + })) +}) +``` + +Adjust import paths to match the file under test. For tests that import the class from the same path as production, use `jest.mock` with the factory before importing the subject. + +## Mocking axios + +If testing code that constructs axios directly, use `jest.mock('axios')` and mock `axios.create` to return an instance whose `get`/`post` resolve with fixture data. Align with [src/core/contentstack/client.ts](../../../../src/core/contentstack/client.ts). + +## Management SDK helpers + +[src/utils/index.ts](../../../../src/utils/index.ts) uses the stack SDK from `managementSDKClient`. In integration-style tests, pass a **fake** `managementSdk` object with `stack().contentType()...` chains that return Promises with fixture data instead of calling real APIs. + +## Style + +- Use `describe` / `it` or `test` with clear names: what behavior, under what condition. +- Keep tests **deterministic** — no real network, no reliance on local `csdx` auth state unless explicitly using an e2e harness (not assumed here). diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a03aa18 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "snyk.advanced.autoSelectOrganization": true, + "snyk.advanced.organization": "18cb8ddb-8261-46fc-85fd-8b7025684b29" +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1a55567 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,42 @@ +# contentstack-cli-content-type + +TypeScript npm package: a **Contentstack CLI** (`csdx`) plugin that reads Content Type metadata from a stack—list, field details, audit log lines, same-stack or cross-stack comparison, and stack content-model diagrams. It does not perform bulk mutations on entries or assets. + +## Stack + +- **Language**: TypeScript (`strict` in [tsconfig.json](tsconfig.json)) +- **CLI**: oclif; commands under `src/commands/content-type/` +- **Tests**: Jest + ts-jest; tests under `tests/` (see [jest.config.js](jest.config.js)) +- **Core logic**: `src/core/content-type/`, shared command base in `src/core/command.ts`, HTTP in `src/core/contentstack/` + +## Scripts + +| Script | Purpose | +|--------|---------| +| `npm test` | Run Jest | +| `npm run posttest` | ESLint on `.ts` files (see [package.json](package.json)) | +| `npm run test:coverage` | Jest with coverage; terminal summary plus **HTML** report at `coverage/lcov-report/index.html` (see [jest.config.js](jest.config.js)) | +| `npm run prepack` | `tsc -b`, `oclif manifest`, `oclif readme` — run when commands, flags, or descriptions change | + +## Workflow + +- Prefer adding or updating tests for behavioral changes in `src/core/` and `src/utils/`. +- Do not commit `test.only` / `test.skip` (or `describe.only` / `it.only`). +- After changing command IDs, flags, or help text, regenerate CLI docs so `README.md` and `oclif.manifest.json` stay aligned (see `prepack` / `version` in [package.json](package.json)). + +## Coverage + +- **Target**: **80%** minimum on statements, branches, functions, and lines. +- **Enforcement**: [jest.config.js](jest.config.js) sets **global** `coverageThreshold` at **80%** for all four metrics. Run `npm run test:coverage` so thresholds apply. +- **HTML report**: after `npm run test:coverage`, open `coverage/lcov-report/index.html` in a browser. The `coverage/` directory is gitignored. + +## Security + +See [SECURITY.md](SECURITY.md) for reporting issues. + +## Cursor: rules and skills + +- **Rules index**: [.cursor/rules/README.md](.cursor/rules/README.md) — context-specific `.mdc` rules. +- **Skills index**: [.cursor/skills/README.md](.cursor/skills/README.md) — `ContentTypeCommand`, CMA client, testing, PR review. + +For detailed plugin architecture and commands, start with [.cursor/skills/contentstack-cli-content-type/SKILL.md](.cursor/skills/contentstack-cli-content-type/SKILL.md). diff --git a/jest.config.js b/jest.config.js index b7ac236..93a1b2b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,12 +1,26 @@ module.exports = { - "roots": [ - "" + roots: [ + '', ], - "testMatch": [ - "**/tests/**/*.+(ts|tsx)", - "**/?(*.)+(spec|test).+(ts|tsx)" + testMatch: [ + '**/tests/**/*.+(ts|tsx)', + '**/?(*.)+(spec|test).+(ts|tsx)', ], - "transform": { - "^.+\\.(ts|tsx)$": "ts-jest" + transform: { + '^.+\\.(ts|tsx)$': 'ts-jest', }, -} \ No newline at end of file + // Coverage: raise thresholds toward ~80% lines/branches/functions as the suite grows (see AGENTS.md). + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/**/*.d.ts', + ], + coverageReporters: ['text', 'text-summary', 'lcov', 'html'], + coverageThreshold: { + global: { + branches: 80, + functions: 80, + lines: 80, + statements: 80, + }, + }, +} diff --git a/package-lock.json b/package-lock.json index 982093c..3dffa12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "contentstack-cli-content-type", - "version": "1.4.2", + "version": "1.4.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "contentstack-cli-content-type", - "version": "1.4.2", + "version": "1.4.3", "license": "MIT", "dependencies": { - "@contentstack/cli-command": "^1.7.2", - "@contentstack/cli-utilities": "^1.17.4", + "@contentstack/cli-command": "^1.8.0", + "@contentstack/cli-utilities": "^1.18.1", "@types/diff2html": "^3.0.3", "@types/git-diff": "^2.0.7", "@types/hogan.js": "^3.0.5", @@ -28,16 +28,16 @@ "url-join": "^4.0.1" }, "devDependencies": { - "@oclif/plugin-help": "^6.2.37", + "@oclif/plugin-help": "^6.2.43", "@types/jest": "^29.5.14", - "@types/node": "^22.19.15", + "@types/node": "^22.19.17", "eslint": "^8.57.1", - "eslint-config-oclif": "^6.0.148", + "eslint-config-oclif": "^6.0.156", "eslint-config-oclif-typescript": "^3.1.14", "globby": "^10.0.2", "jest": "^29.7.0", - "oclif": "^4.22.87", - "ts-jest": "^29.4.6", + "oclif": "^4.22.96", + "ts-jest": "^29.4.9", "ts-node": "^10.9.2", "typescript": "^4.9.5" }, @@ -264,52 +264,52 @@ } }, "node_modules/@aws-sdk/client-cloudfront": { - "version": "3.1004.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.1004.0.tgz", - "integrity": "sha512-CIGnyfPRa4gkY7I32MBiMJTsQxJFEaMXTedf7uU7TiUemLz4IHrGXx+3BLDbdxwL6dVfBfvC2POSFzrbgA2t4g==", + "version": "3.1009.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.1009.0.tgz", + "integrity": "sha512-KRac+gkuj3u49IyWkrudHRlP/q/faTto+1xRS7Aj6cDGewMIzgdQArrdZEJoVntbaVZHLM5s/NVmWORzBWNcSw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.18", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.19", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.4", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-stream": "^4.5.17", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-stream": "^4.5.19", "@smithy/util-utf8": "^4.2.2", - "@smithy/util-waiter": "^4.2.11", + "@smithy/util-waiter": "^4.2.13", "tslib": "^2.6.2" }, "engines": { @@ -317,66 +317,66 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.1004.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1004.0.tgz", - "integrity": "sha512-m0zNfpsona9jQdX1cHtHArOiuvSGZPsgp/KRZS2YjJhKah96G2UN3UNGZQ6aVjXIQjCY6UanCJo0uW9Xf2U41w==", + "version": "3.1014.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1014.0.tgz", + "integrity": "sha512-0XLrOT4Cm3NEhhiME7l/8LbTXS4KdsbR4dSrY207KNKTcHLLTZ9EXt4ZpgnTfLvWQF3pGP2us4Zi1fYLo0N+Ow==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.18", - "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", - "@aws-sdk/middleware-expect-continue": "^3.972.7", - "@aws-sdk/middleware-flexible-checksums": "^3.973.4", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-location-constraint": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-sdk-s3": "^3.972.18", - "@aws-sdk/middleware-ssec": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.19", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/signature-v4-multi-region": "^3.996.6", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.4", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/eventstream-serde-browser": "^4.2.11", - "@smithy/eventstream-serde-config-resolver": "^4.3.11", - "@smithy/eventstream-serde-node": "^4.2.11", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-blob-browser": "^4.2.12", - "@smithy/hash-node": "^4.2.11", - "@smithy/hash-stream-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/md5-js": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", + "@aws-sdk/core": "^3.973.23", + "@aws-sdk/credential-provider-node": "^3.972.24", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.8", + "@aws-sdk/middleware-expect-continue": "^3.972.8", + "@aws-sdk/middleware-flexible-checksums": "^3.974.3", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-location-constraint": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-sdk-s3": "^3.972.23", + "@aws-sdk/middleware-ssec": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.24", + "@aws-sdk/region-config-resolver": "^3.972.9", + "@aws-sdk/signature-v4-multi-region": "^3.996.11", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.10", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/eventstream-serde-config-resolver": "^4.3.12", + "@smithy/eventstream-serde-node": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-blob-browser": "^4.2.13", + "@smithy/hash-node": "^4.2.12", + "@smithy/hash-stream-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/md5-js": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.27", + "@smithy/middleware-retry": "^4.4.44", + "@smithy/middleware-serde": "^4.2.15", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-stream": "^4.5.17", + "@smithy/util-defaults-mode-browser": "^4.3.43", + "@smithy/util-defaults-mode-node": "^4.2.47", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", - "@smithy/util-waiter": "^4.2.11", + "@smithy/util-waiter": "^4.2.13", "tslib": "^2.6.2" }, "engines": { @@ -384,23 +384,23 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.19.tgz", - "integrity": "sha512-56KePyOcZnKTWCd89oJS1G6j3HZ9Kc+bh/8+EbvtaCCXdP6T7O7NzCiPuHRhFLWnzXIaXX3CxAz0nI5My9spHQ==", + "version": "3.973.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.26.tgz", + "integrity": "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/xml-builder": "^3.972.10", - "@smithy/core": "^3.23.9", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/signature-v4": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/xml-builder": "^3.972.16", + "@smithy/core": "^3.23.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", - "@smithy/util-middleware": "^4.2.11", + "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -409,13 +409,13 @@ } }, "node_modules/@aws-sdk/crc64-nvme": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.4.tgz", - "integrity": "sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw==", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.5.tgz", + "integrity": "sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -423,16 +423,16 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.17.tgz", - "integrity": "sha512-MBAMW6YELzE1SdkOniqr51mrjapQUv8JXSGxtwRjQV0mwVDutVsn22OPAUt4RcLRvdiHQmNBDEFP9iTeSVCOlA==", + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.24.tgz", + "integrity": "sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -440,21 +440,21 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.19.tgz", - "integrity": "sha512-9EJROO8LXll5a7eUFqu48k6BChrtokbmgeMWmsH7lBb6lVbtjslUYz/ShLi+SHkYzTomiGBhmzTW7y+H4BxsnA==", + "version": "3.972.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.26.tgz", + "integrity": "sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/property-provider": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", - "@smithy/util-stream": "^4.5.17", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.21", "tslib": "^2.6.2" }, "engines": { @@ -462,25 +462,25 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.19.tgz", - "integrity": "sha512-pVJVjWqVrPqjpFq7o0mCmeZu1Y0c94OCHSYgivdCD2wfmYVtBbwQErakruhgOD8pcMcx9SCqRw1pzHKR7OGBcA==", + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.28.tgz", + "integrity": "sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/credential-provider-env": "^3.972.17", - "@aws-sdk/credential-provider-http": "^3.972.19", - "@aws-sdk/credential-provider-login": "^3.972.19", - "@aws-sdk/credential-provider-process": "^3.972.17", - "@aws-sdk/credential-provider-sso": "^3.972.19", - "@aws-sdk/credential-provider-web-identity": "^3.972.19", - "@aws-sdk/nested-clients": "^3.996.9", - "@aws-sdk/types": "^3.973.5", - "@smithy/credential-provider-imds": "^4.2.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/credential-provider-env": "^3.972.24", + "@aws-sdk/credential-provider-http": "^3.972.26", + "@aws-sdk/credential-provider-login": "^3.972.28", + "@aws-sdk/credential-provider-process": "^3.972.24", + "@aws-sdk/credential-provider-sso": "^3.972.28", + "@aws-sdk/credential-provider-web-identity": "^3.972.28", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -488,19 +488,19 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.19.tgz", - "integrity": "sha512-jOXdZ1o+CywQKr6gyxgxuUmnGwTTnY2Kxs1PM7fI6AYtDWDnmW/yKXayNqkF8KjP1unflqMWKVbVt5VgmE3L0g==", + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.28.tgz", + "integrity": "sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.9", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -508,23 +508,23 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.20.tgz", - "integrity": "sha512-0xHca2BnPY0kzjDYPH7vk8YbfdBPpWVS67rtqQMalYDQUCBYS37cZ55K6TuFxCoIyNZgSCFrVKr9PXC5BVvQQw==", + "version": "3.972.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.29.tgz", + "integrity": "sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.17", - "@aws-sdk/credential-provider-http": "^3.972.19", - "@aws-sdk/credential-provider-ini": "^3.972.19", - "@aws-sdk/credential-provider-process": "^3.972.17", - "@aws-sdk/credential-provider-sso": "^3.972.19", - "@aws-sdk/credential-provider-web-identity": "^3.972.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/credential-provider-imds": "^4.2.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/credential-provider-env": "^3.972.24", + "@aws-sdk/credential-provider-http": "^3.972.26", + "@aws-sdk/credential-provider-ini": "^3.972.28", + "@aws-sdk/credential-provider-process": "^3.972.24", + "@aws-sdk/credential-provider-sso": "^3.972.28", + "@aws-sdk/credential-provider-web-identity": "^3.972.28", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -532,17 +532,17 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.17.tgz", - "integrity": "sha512-c8G8wT1axpJDgaP3xzcy+q8Y1fTi9A2eIQJvyhQ9xuXrUZhlCfXbC0vM9bM1CUXiZppFQ1p7g0tuUMvil/gCPg==", + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.24.tgz", + "integrity": "sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -550,19 +550,19 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.19.tgz", - "integrity": "sha512-kVjQsEU3b///q7EZGrUzol9wzwJFKbEzqJKSq82A9ShrUTEO7FNylTtby3sPV19ndADZh1H3FB3+5ZrvKtEEeg==", + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.28.tgz", + "integrity": "sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.9", - "@aws-sdk/token-providers": "3.1008.0", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/token-providers": "3.1021.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -570,18 +570,18 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.19.tgz", - "integrity": "sha512-BV1BlTFdG4w4tAihxN7iXDBoNcNewXD4q8uZlNQiUrnqxwGWUhKHODIQVSPlQGxXClEj+63m+cqZskw+ESmeZg==", + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.28.tgz", + "integrity": "sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.9", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -589,17 +589,17 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.7.tgz", - "integrity": "sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.8.tgz", + "integrity": "sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", + "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-arn-parser": "^3.972.3", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, @@ -608,15 +608,15 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.7.tgz", - "integrity": "sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.8.tgz", + "integrity": "sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -624,24 +624,24 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.973.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.973.5.tgz", - "integrity": "sha512-Dp3hqE5W6hG8HQ3Uh+AINx9wjjqYmFHbxede54sGj3akx/haIQrkp85lNdTdC+ouNUcSYNiuGkzmyDREfHX1Gg==", + "version": "3.974.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.6.tgz", + "integrity": "sha512-YckB8k1ejbyCg/g36gUMFLNzE4W5cERIa4MtsdO+wpTmJEP0+TB7okWIt7d8TDOvnb7SwvxJ21E4TGOBxFpSWQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/crc64-nvme": "^3.972.4", - "@aws-sdk/types": "^3.973.5", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/crc64-nvme": "^3.972.5", + "@aws-sdk/types": "^3.973.6", "@smithy/is-array-buffer": "^4.2.2", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-stream": "^4.5.17", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.21", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -650,15 +650,15 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", - "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", + "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -666,14 +666,14 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.7.tgz", - "integrity": "sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.8.tgz", + "integrity": "sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -681,14 +681,14 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", - "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", + "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -696,16 +696,16 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", - "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.9.tgz", + "integrity": "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", + "@aws-sdk/types": "^3.973.6", "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -713,24 +713,24 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.19.tgz", - "integrity": "sha512-/CtOHHVFg4ZuN6CnLnYkrqWgVEnbOBC4kNiKa+4fldJ9cioDt3dD/f5vpq0cWLOXwmGL2zgVrVxNhjxWpxNMkg==", + "version": "3.972.27", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.27.tgz", + "integrity": "sha512-gomO6DZwx+1D/9mbCpcqO5tPBqYBK7DtdgjTIjZ4yvfh/S7ETwAPS0XbJgP2JD8Ycr5CwVrEkV1sFtu3ShXeOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-arn-parser": "^3.972.3", - "@smithy/core": "^3.23.9", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/signature-v4": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", + "@smithy/core": "^3.23.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-stream": "^4.5.17", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.21", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -739,14 +739,14 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.7.tgz", - "integrity": "sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.8.tgz", + "integrity": "sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -754,19 +754,19 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.20.tgz", - "integrity": "sha512-3kNTLtpUdeahxtnJRnj/oIdLAUdzTfr9N40KtxNhtdrq+Q1RPMdCJINRXq37m4t5+r3H70wgC3opW46OzFcZYA==", + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.28.tgz", + "integrity": "sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@smithy/core": "^3.23.9", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", - "@smithy/util-retry": "^4.2.11", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@smithy/core": "^3.23.13", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-retry": "^4.2.13", "tslib": "^2.6.2" }, "engines": { @@ -774,48 +774,48 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.996.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.9.tgz", - "integrity": "sha512-+RpVtpmQbbtzFOKhMlsRcXM/3f1Z49qTOHaA8gEpHOYruERmog6f2AUtf/oTRLCWjR9H2b3roqryV/hI7QMW8w==", + "version": "3.996.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.18.tgz", + "integrity": "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.20", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.6", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.9", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.23", - "@smithy/middleware-retry": "^4.4.40", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/region-config-resolver": "^3.972.10", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.14", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.13", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-retry": "^4.4.46", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.39", - "@smithy/util-defaults-mode-node": "^4.2.42", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", + "@smithy/util-defaults-mode-browser": "^4.3.44", + "@smithy/util-defaults-mode-node": "^4.2.48", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -824,16 +824,16 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", - "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.10.tgz", + "integrity": "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/config-resolver": "^4.4.10", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/config-resolver": "^4.4.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -841,17 +841,17 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.7.tgz", - "integrity": "sha512-mYhh7FY+7OOqjkYkd6+6GgJOsXK1xBWmuR+c5mxJPj2kr5TBNeZq+nUvE9kANWAux5UxDVrNOSiEM/wlHzC3Lg==", + "version": "3.996.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.15.tgz", + "integrity": "sha512-Ukw2RpqvaL96CjfH/FgfBmy/ZosHBqoHBCFsN61qGg99F33vpntIVii8aNeh65XuOja73arSduskoa4OJea9RQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/protocol-http": "^5.3.11", - "@smithy/signature-v4": "^5.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/middleware-sdk-s3": "^3.972.27", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -859,18 +859,18 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1008.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1008.0.tgz", - "integrity": "sha512-TulwlHQBWcJs668kNUDMZHN51DeLrDsYT59Ux4a/nbvr025gM6HjKJJ3LvnZccam7OS/ZKUVkWomCneRQKJbBg==", + "version": "3.1021.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1021.0.tgz", + "integrity": "sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.9", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -878,13 +878,13 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.973.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", - "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", + "version": "3.973.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", + "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -905,16 +905,16 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", - "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-endpoints": "^3.3.2", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", "tslib": "^2.6.2" }, "engines": { @@ -935,29 +935,29 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", - "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", + "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.6.tgz", - "integrity": "sha512-iF7G0prk7AvmOK64FcLvc/fW+Ty1H+vttajL7PvJFReU8urMxfYmynTTuFKDTA76Wgpq3FzTPKwabMQIXQHiXQ==", + "version": "3.973.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.14.tgz", + "integrity": "sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.20", - "@aws-sdk/types": "^3.973.5", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/types": "^3.973.6", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, @@ -974,14 +974,14 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", - "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.16.tgz", + "integrity": "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", - "fast-xml-parser": "5.4.1", + "@smithy/types": "^4.13.1", + "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" }, "engines": { @@ -1524,13 +1524,13 @@ } }, "node_modules/@contentstack/cli-command": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@contentstack/cli-command/-/cli-command-1.7.2.tgz", - "integrity": "sha512-dtXc3gIcnivfLegADy5/PZb+1x/esZ65H2E1CjO/pg50UC8Vy1U+U0ozS0hJZTFoaVjeG+1VJRoxf5MrtUGnNA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@contentstack/cli-command/-/cli-command-1.8.0.tgz", + "integrity": "sha512-JsOVaz7jBUMeul04DZagSlS74tsIyz/f0NmsHPsr9WV+u3fRO90ilRUG1SKrreUGa7x31gIU0CB5riQeu+TXYg==", "license": "MIT", "dependencies": { - "@contentstack/cli-utilities": "~1.17.0", - "@oclif/core": "^4.3.0", + "@contentstack/cli-utilities": "~1.18.0", + "@oclif/core": "^4.8.3", "@oclif/plugin-help": "^6.2.28", "contentstack": "^3.25.3" }, @@ -1539,14 +1539,14 @@ } }, "node_modules/@contentstack/cli-utilities": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.17.4.tgz", - "integrity": "sha512-45Ujy0lNtQiU0FhZrtfGEfte4kjy3tlOnlVz6REH+cW/y1Dgg1nMh+YVgygbOh+6b8PkvTYVlEvb15UxRarNiA==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.18.1.tgz", + "integrity": "sha512-1ymPu5HbOXFdDJHJFiwtT1yVNpmDOgMH8qqCeP3kjS7ED1+rz7Q3cWPnJC9FlUfvFeOAyJaJPPQCiYd0lgujtw==", "license": "MIT", "dependencies": { "@contentstack/management": "~1.27.5", "@contentstack/marketplace-sdk": "^1.5.0", - "@oclif/core": "^4.3.0", + "@oclif/core": "^4.8.3", "axios": "^1.13.5", "chalk": "^4.1.2", "cli-cursor": "^3.1.0", @@ -1560,7 +1560,7 @@ "inquirer-search-list": "^1.2.6", "js-yaml": "^4.1.1", "klona": "^2.0.6", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "mkdirp": "^1.0.4", "open": "^8.4.2", "ora": "^5.4.1", @@ -3949,9 +3949,9 @@ "license": "ISC" }, "node_modules/@oclif/plugin-help": { - "version": "6.2.37", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.37.tgz", - "integrity": "sha512-5N/X/FzlJaYfpaHwDC0YHzOzKDWa41s9t+4FpCDu4f9OMReds4JeNBaaWk9rlIzdKjh2M6AC5Q18ORfECRkHGA==", + "version": "6.2.43", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.43.tgz", + "integrity": "sha512-aef92VxQECLFDjI4CpgCL+jDuAsc3jzq5gBTLwNzj60mmrh8eDd7B0ABIgWXphb6gdARSRil+/FPtcdiSSupRA==", "license": "MIT", "dependencies": { "@oclif/core": "^4" @@ -3961,14 +3961,14 @@ } }, "node_modules/@oclif/plugin-not-found": { - "version": "3.2.74", - "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.74.tgz", - "integrity": "sha512-6RD/EuIUGxAYR45nMQg+nw+PqwCXUxkR6Eyn+1fvbVjtb9d+60OPwB77LCRUI4zKNI+n0LOFaMniEdSpb+A7kQ==", + "version": "3.2.80", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.80.tgz", + "integrity": "sha512-yTLjWvR1r/Rd/cO2LxHdMCDoL5sQhBYRUcOMCmxZtWVWhx4rAZ8KVUPDVsb+SvjJDV5ADTDBgt1H52fFx7YWqg==", "dev": true, "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.10.1", - "@oclif/core": "^4.8.0", + "@oclif/core": "^4.10.5", "ansis": "^3.17.0", "fast-levenshtein": "^3.0.0" }, @@ -3976,10 +3976,40 @@ "node": ">=18.0.0" } }, + "node_modules/@oclif/plugin-not-found/node_modules/@oclif/core": { + "version": "4.10.5", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.5.tgz", + "integrity": "sha512-qcdCF7NrdWPfme6Kr34wwljRCXbCVpL1WVxiNy0Ep6vbWKjxAjFQwuhqkoyL0yjI+KdwtLcOCGn5z2yzdijc8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.7.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.14", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@oclif/plugin-warn-if-update-available": { - "version": "3.1.55", - "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.55.tgz", - "integrity": "sha512-VIEBoaoMOCjl3y+w/kdfZMODi0mVMnDuM0vkBf3nqeidhRXVXq87hBqYDdRwN1XoD+eDfE8tBbOP7qtSOONztQ==", + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.60.tgz", + "integrity": "sha512-cRKBZm14IuA6G8W84dfd3iXj3BTAoxQ5o3pUE8DKEQ4n/tVha20t5nkVeD+ISC68e0Fuw5koTMvRwXb1lJSnzg==", "dev": true, "license": "MIT", "dependencies": { @@ -3987,7 +4017,7 @@ "ansis": "^3.17.0", "debug": "^4.4.3", "http-call": "^5.2.2", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "registry-auth-token": "^5.1.1" }, "engines": { @@ -4158,20 +4188,6 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@smithy/abort-controller": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.12.tgz", - "integrity": "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@smithy/chunked-blob-reader": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", @@ -4200,9 +4216,9 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.11.tgz", - "integrity": "sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", + "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4218,9 +4234,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.23.11", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.11.tgz", - "integrity": "sha512-952rGf7hBRnhUIaeLp6q4MptKW8sPFe5VvkoZ5qIzFAtx6c/QZ/54FS3yootsyUSf9gJX/NBqEBNdNR7jMIlpQ==", + "version": "3.23.13", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.13.tgz", + "integrity": "sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4230,7 +4246,7 @@ "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.21", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" @@ -4453,14 +4469,14 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.25", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.25.tgz", - "integrity": "sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A==", + "version": "4.4.28", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.28.tgz", + "integrity": "sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.11", - "@smithy/middleware-serde": "^4.2.14", + "@smithy/core": "^3.23.13", + "@smithy/middleware-serde": "^4.2.16", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", @@ -4473,19 +4489,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.42", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.42.tgz", - "integrity": "sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg==", + "version": "4.4.46", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.46.tgz", + "integrity": "sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", - "@smithy/util-retry": "^4.2.12", + "@smithy/util-retry": "^4.2.13", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, @@ -4494,13 +4510,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.14.tgz", - "integrity": "sha512-+CcaLoLa5apzSRtloOyG7lQvkUw2ZDml3hRh4QiG9WyEPfW5Ke/3tPOPiPjUneuT59Tpn8+c3RVaUvvkkwqZwg==", + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.16.tgz", + "integrity": "sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.11", + "@smithy/core": "^3.23.13", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" @@ -4540,13 +4556,12 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.16", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.16.tgz", - "integrity": "sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.1.tgz", + "integrity": "sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", @@ -4661,18 +4676,18 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.12.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.5.tgz", - "integrity": "sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w==", + "version": "4.12.8", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.8.tgz", + "integrity": "sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.11", - "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/core": "^3.23.13", + "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.21", "tslib": "^2.6.2" }, "engines": { @@ -4776,14 +4791,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.41", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.41.tgz", - "integrity": "sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g==", + "version": "4.3.44", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.44.tgz", + "integrity": "sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.2.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -4792,17 +4807,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.44", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.44.tgz", - "integrity": "sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ==", + "version": "4.2.48", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.48.tgz", + "integrity": "sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.11", + "@smithy/config-resolver": "^4.4.13", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -4853,9 +4868,9 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.12.tgz", - "integrity": "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==", + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.13.tgz", + "integrity": "sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4868,14 +4883,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.19", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.19.tgz", - "integrity": "sha512-v4sa+3xTweL1CLO2UP0p7tvIMH/Rq1X4KKOxd568mpe6LSLMQCnDHs4uv7m3ukpl3HvcN2JH6jiCS0SNRXKP/w==", + "version": "4.5.21", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.21.tgz", + "integrity": "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", - "@smithy/node-http-handler": "^4.4.16", + "@smithy/node-http-handler": "^4.5.1", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", @@ -4915,13 +4930,12 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.2.13", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.13.tgz", - "integrity": "sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.14.tgz", + "integrity": "sha512-2zqq5o/oizvMaFUlNiTyZ7dbgYv1a893aGut2uaxtbzTx/VYYnRxWzDHuD/ftgcw94ffenua+ZNLrbqwUYE+Bg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -5227,9 +5241,9 @@ } }, "node_modules/@types/node": { - "version": "22.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", - "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", "devOptional": true, "license": "MIT", "dependencies": { @@ -5304,20 +5318,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", - "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/type-utils": "8.57.0", - "@typescript-eslint/utils": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5327,9 +5341,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.0", + "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -5343,16 +5357,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", - "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "engines": { @@ -5364,18 +5378,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", - "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.0", - "@typescript-eslint/types": "^8.57.0", + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "engines": { @@ -5386,18 +5400,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", - "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5408,9 +5422,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", - "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", "dev": true, "license": "MIT", "engines": { @@ -5421,21 +5435,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", - "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5446,13 +5460,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", - "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", "dev": true, "license": "MIT", "engines": { @@ -5464,21 +5478,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", - "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.0", - "@typescript-eslint/tsconfig-utils": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5488,20 +5502,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", - "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0" + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5512,17 +5526,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", - "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -6437,9 +6451,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -8099,9 +8113,9 @@ } }, "node_modules/eslint-config-oclif": { - "version": "6.0.148", - "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-6.0.148.tgz", - "integrity": "sha512-WhunT0kumapHtxh+I/LgSIavl5pu3s1ZfmtOmq+LahsffsX8ufyDFuuwIunxD6QcALuFxusANRn3r6MNeAivjQ==", + "version": "6.0.156", + "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-6.0.156.tgz", + "integrity": "sha512-CA/h54WSdpGFU829gC6FRN0rcOZYe/+k/gRGZW3W1RZW8Jbb1mMZOXJCzMh0M2vyH7Bz60poRt6hsugiIAtwHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8121,7 +8135,7 @@ "eslint-plugin-n": "^17.24.0", "eslint-plugin-perfectionist": "^4", "eslint-plugin-unicorn": "^56.0.1", - "typescript-eslint": "^8.56.1" + "typescript-eslint": "^8.58.0" }, "engines": { "node": ">=18.18.0" @@ -9869,9 +9883,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.3.tgz", - "integrity": "sha512-1o60KoFw2+LWKQu3IdcfcFlGTW4dpqEWmjhYec6H82AYZU2TVBXep6tMl8Z1Y+wM+ZrzCwe3BZ9Vyd9N2rIvmg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", "dev": true, "funding": [ { @@ -9885,9 +9899,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", - "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", + "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==", "dev": true, "funding": [ { @@ -9897,8 +9911,9 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.0.0", - "strnum": "^2.1.2" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -10673,9 +10688,9 @@ "license": "MIT" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13053,9 +13068,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.memoize": { @@ -13301,12 +13316,12 @@ } }, "node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -13630,21 +13645,21 @@ } }, "node_modules/oclif": { - "version": "4.22.87", - "resolved": "https://registry.npmjs.org/oclif/-/oclif-4.22.87.tgz", - "integrity": "sha512-Qm+z93+fn4HSuQnbMZj6Vimje2U+LGJ4YDKLyaWL7JYq4DX17s2DAEPhbgQyC+baLvf9cC4KYXKWbVe4hnNyQA==", + "version": "4.22.96", + "resolved": "https://registry.npmjs.org/oclif/-/oclif-4.22.96.tgz", + "integrity": "sha512-aWM9cMb7Q3KW09qi5Mkw0Hq9sIM7DjVlyMAUl8Q2FP3+e5afBlUU9vmL3EJazVPhqcbg5u18E3z+6kCMk72KYw==", "dev": true, "license": "MIT", "dependencies": { - "@aws-sdk/client-cloudfront": "3.1004.0", - "@aws-sdk/client-s3": "3.1004.0", + "@aws-sdk/client-cloudfront": "3.1009.0", + "@aws-sdk/client-s3": "3.1014.0", "@inquirer/confirm": "^3.1.22", "@inquirer/input": "^2.2.4", "@inquirer/select": "^2.5.0", - "@oclif/core": "4.8.3", - "@oclif/plugin-help": "^6.2.37", - "@oclif/plugin-not-found": "^3.2.74", - "@oclif/plugin-warn-if-update-available": "^3.1.55", + "@oclif/core": "4.9.0", + "@oclif/plugin-help": "^6.2.38", + "@oclif/plugin-not-found": "^3.2.76", + "@oclif/plugin-warn-if-update-available": "^3.1.57", "ansis": "^3.16.0", "async-retry": "^1.3.3", "change-case": "^4", @@ -13668,36 +13683,6 @@ "node": ">=18.0.0" } }, - "node_modules/oclif/node_modules/@oclif/core": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.8.3.tgz", - "integrity": "sha512-f7Rc1JBZO0wNMyDmNzP5IFOv5eM97S9pO4JUFdu2OLyk73YeBI9wog1Yyf666NOQvyptkbG1xh8inzMDQLNTyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.2", - "ansis": "^3.17.0", - "clean-stack": "^3.0.1", - "cli-spinners": "^2.9.2", - "debug": "^4.4.3", - "ejs": "^3.1.10", - "get-package-type": "^0.1.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "lilconfig": "^3.1.3", - "minimatch": "^10.2.4", - "semver": "^7.7.3", - "string-width": "^4.2.3", - "supports-color": "^8", - "tinyglobby": "^0.2.14", - "widest-line": "^3.1.0", - "wordwrap": "^1.0.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -14001,9 +13986,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", - "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.1.tgz", + "integrity": "sha512-d7gQQmLvAKXKXE2GeP9apIGbMYKz88zWdsn/BN2HRWVQsDFdUY36WSLTY0Jvd4HWi7Fb30gQ62oAOzdgJA6fZw==", "dev": true, "funding": [ { @@ -15777,9 +15762,9 @@ } }, "node_modules/strnum": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", - "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", "dev": true, "funding": [ { @@ -16022,9 +16007,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -16058,19 +16043,19 @@ } }, "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "version": "29.4.9", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", + "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", + "handlebars": "^4.7.9", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.3", + "semver": "^7.7.4", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -16087,7 +16072,7 @@ "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" + "typescript": ">=4.3 <7" }, "peerDependenciesMeta": { "@babel/core": { @@ -16408,16 +16393,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", - "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.0", - "@typescript-eslint/parser": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/utils": "8.57.0" + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -16428,7 +16413,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/uglify-js": { diff --git a/package.json b/package.json index 56e7341..f2b7d6f 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "contentstack-cli-content-type", "description": "Retrieve information about Content Types in a Stack.", - "version": "1.4.2", + "version": "1.4.3", "author": "Contentstack Developer", "bugs": "https://github.com/contentstack/contentstack-cli-content-type/issues", "dependencies": { - "@contentstack/cli-command": "^1.7.2", - "@contentstack/cli-utilities": "^1.17.4", + "@contentstack/cli-command": "^1.8.0", + "@contentstack/cli-utilities": "^1.18.1", "@types/diff2html": "^3.0.3", "@types/git-diff": "^2.0.7", "@types/hogan.js": "^3.0.5", @@ -24,16 +24,16 @@ "url-join": "^4.0.1" }, "devDependencies": { - "@oclif/plugin-help": "^6.2.37", + "@oclif/plugin-help": "^6.2.43", "@types/jest": "^29.5.14", - "@types/node": "^22.19.15", + "@types/node": "^22.19.17", "eslint": "^8.57.1", - "eslint-config-oclif": "^6.0.148", + "eslint-config-oclif": "^6.0.156", "eslint-config-oclif-typescript": "^3.1.14", "globby": "^10.0.2", "jest": "^29.7.0", - "oclif": "^4.22.87", - "ts-jest": "^29.4.6", + "oclif": "^4.22.96", + "ts-jest": "^29.4.9", "ts-node": "^10.9.2", "typescript": "^4.9.5" }, @@ -66,6 +66,7 @@ "repository": "contentstack/contentstack-cli-content-type", "scripts": { "test": "jest", + "test:coverage": "jest --coverage", "postpack": "rm -f oclif.manifest.json", "posttest": "eslint . --ext .ts --config .eslintrc", "prepack": "rm -rf lib && tsc -b && oclif manifest && oclif readme", diff --git a/tests/audit.test.ts b/tests/audit.test.ts deleted file mode 100644 index b73b31c..0000000 --- a/tests/audit.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -describe('audit command', () => { - test('result', () => { - expect(true).toBe(true) - }) -}) diff --git a/tests/commands/content-type/audit.test.ts b/tests/commands/content-type/audit.test.ts new file mode 100644 index 0000000..e7f1bb0 --- /dev/null +++ b/tests/commands/content-type/audit.test.ts @@ -0,0 +1,101 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + managementSDKClient: jest.fn().mockResolvedValue({}), + cliux: { + loaderV2: jest.fn().mockReturnValue({}), + print: jest.fn(), + }, + flags: new Proxy( + {}, + { + get: (_t, prop) => + jest.fn((opts: Record) => ({ ...opts, __f: String(prop) })), + } + ), + printFlagDeprecation: jest.fn(() => () => undefined), +})) + +jest.mock('../../../src/utils', () => ({ + getContentType: jest.fn(), + getStack: jest.fn(), + getUsers: jest.fn(), +})) + +import { getContentType, getStack, getUsers } from '../../../src/utils' +import AuditCommand from '../../../src/commands/content-type/audit' + +describe('content-type:audit', () => { + it('loads audit logs and prints output', async () => { + ;(getContentType as jest.Mock).mockResolvedValueOnce({ _version: 1, uid: 'ct' }) + ;(getStack as jest.Mock).mockResolvedValue({ name: 'St' }) + ;(getUsers as jest.Mock).mockResolvedValue([ + { first_name: 'A', last_name: 'B', uid: 'u1' }, + ]) + + const cmd = new AuditCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { 'content-type': 'home', 'stack-api-key': 'k' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).client = { + getContentTypeAuditLogs: jest.fn().mockResolvedValue({ + logs: [ + { + created_at: '2020-01-01T00:00:00.000Z', + created_by: 'u1', + event_type: 'save', + metadata: { version: 1 }, + }, + ], + }), + } + ;(cmd as any).printOutput = jest.fn() + + await cmd.run() + + expect(getContentType).toHaveBeenCalled() + expect((cmd as any).client.getContentTypeAuditLogs).toHaveBeenCalled() + expect(cmd.printOutput).toHaveBeenCalledWith( + expect.objectContaining({ hasResults: true }), + 'Audit Logs', + 'home', + 'St' + ) + }) + + it('calls error when getStack fails', async () => { + ;(getContentType as jest.Mock).mockResolvedValueOnce({ uid: 'ct' }) + ;(getStack as jest.Mock).mockRejectedValue(new Error('network')) + + const cmd = new AuditCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { 'content-type': 'home', 'stack-api-key': 'k' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).client = { + getContentTypeAuditLogs: jest.fn().mockResolvedValue({ logs: [] }), + } + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith('network', { + exit: 1, + suggestions: undefined, + }) + }) +}) diff --git a/tests/commands/content-type/compare-remote.test.ts b/tests/commands/content-type/compare-remote.test.ts new file mode 100644 index 0000000..7f3bf61 --- /dev/null +++ b/tests/commands/content-type/compare-remote.test.ts @@ -0,0 +1,131 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + managementSDKClient: jest.fn().mockResolvedValue({}), + cliux: { + loaderV2: jest.fn().mockReturnValue({}), + print: jest.fn(), + }, + flags: new Proxy( + {}, + { + get: (_t, prop) => + jest.fn((opts: Record) => ({ ...opts, __f: String(prop) })), + } + ), + printFlagDeprecation: jest.fn(() => () => undefined), +})) + +jest.mock('../../../src/utils', () => ({ + getContentType: jest.fn(), + getStack: jest.fn(), +})) + +jest.mock('../../../src/core/content-type/compare', () => ({ + __esModule: true, + default: jest.fn().mockResolvedValue({ + body: 'Please check the browser output.', + footer: null, + hasResults: true, + header: null, + rows: [], + }), +})) + +import { getContentType, getStack } from '../../../src/utils' +import CompareRemoteCommand from '../../../src/commands/content-type/compare-remote' + +describe('content-type:compare-remote', () => { + it('compares content types across two stacks', async () => { + ;(getStack as jest.Mock) + .mockResolvedValueOnce({ name: 'Origin' }) + .mockResolvedValueOnce({ name: 'Remote' }) + ;(getContentType as jest.Mock) + .mockResolvedValueOnce({ uid: 'ct' }) + .mockResolvedValueOnce({ uid: 'ct' }) + + const cmd = new CompareRemoteCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + 'content-type': 'home', + 'origin-stack': 'orig-key', + 'remote-stack': 'rem-key', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'orig-key' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).printOutput = jest.fn() + ;(cmd as any).warn = jest.fn() + + await cmd.run() + + expect(getStack).toHaveBeenCalledTimes(2) + expect(cmd.printOutput).toHaveBeenCalledWith( + expect.anything(), + 'changes', + 'home', + 'Origin <-> Remote' + ) + }) + + it('warns when origin and remote stacks are identical', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'S' }) + ;(getContentType as jest.Mock).mockResolvedValue({ uid: 'ct' }) + + const cmd = new CompareRemoteCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + 'content-type': 'home', + 'origin-stack': 'same', + 'remote-stack': 'same', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'same' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).printOutput = jest.fn() + ;(cmd as any).warn = jest.fn() + + await cmd.run() + + expect(cmd.warn).toHaveBeenCalledWith( + expect.stringContaining('same stack') + ) + }) + + it('calls error when getStack fails', async () => { + ;(getStack as jest.Mock).mockRejectedValue(new Error('offline')) + + const cmd = new CompareRemoteCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + 'content-type': 'home', + 'origin-stack': 'a', + 'remote-stack': 'b', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'a' + ;(cmd as any).contentTypeManagementClient = {} + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith('offline', { + exit: 1, + suggestions: undefined, + }) + }) +}) diff --git a/tests/commands/content-type/compare.test.ts b/tests/commands/content-type/compare.test.ts new file mode 100644 index 0000000..a2f4189 --- /dev/null +++ b/tests/commands/content-type/compare.test.ts @@ -0,0 +1,159 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + managementSDKClient: jest.fn().mockResolvedValue({}), + cliux: { + loaderV2: jest.fn().mockReturnValue({}), + print: jest.fn(), + }, + flags: new Proxy( + {}, + { + get: (_t, prop) => + jest.fn((opts: Record) => ({ ...opts, __f: String(prop) })), + } + ), + printFlagDeprecation: jest.fn(() => () => undefined), +})) + +jest.mock('../../../src/utils', () => ({ + getContentType: jest.fn(), + getStack: jest.fn(), +})) + +jest.mock('../../../src/core/content-type/compare', () => ({ + __esModule: true, + default: jest.fn().mockResolvedValue({ + body: 'Please check the browser output.', + footer: null, + hasResults: true, + header: null, + rows: [], + }), +})) + +import buildOutputCore from '../../../src/core/content-type/compare' +import { getContentType, getStack } from '../../../src/utils' +import CompareCommand from '../../../src/commands/content-type/compare' + +describe('content-type:compare', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('compares two versions when left and right are set', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'St' }) + ;(getContentType as jest.Mock) + .mockResolvedValueOnce({ uid: 'ct', updated_at: '2020' }) + .mockResolvedValueOnce({ uid: 'ct', updated_at: '2020' }) + + const cmd = new CompareCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + 'content-type': 'home', + left: 2, + right: 3, + 'stack-api-key': 'k', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).printOutput = jest.fn() + + await cmd.run() + + expect(getContentType).toHaveBeenCalledTimes(2) + expect(buildOutputCore).toHaveBeenCalled() + expect(cmd.printOutput).toHaveBeenCalled() + }) + + it('discovers versions when left is omitted', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'St' }) + ;(getContentType as jest.Mock) + .mockResolvedValueOnce({ _version: 4, uid: 'home' }) + .mockResolvedValueOnce({ uid: 'home', v: 4 }) + .mockResolvedValueOnce({ uid: 'home', v: 3 }) + + const cmd = new CompareCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { 'content-type': 'home', 'stack-api-key': 'k' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).printOutput = jest.fn() + ;(cmd as any).log = jest.fn() + + await cmd.run() + + expect(getContentType).toHaveBeenCalled() + expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('Comparing versions')) + }) + + it('warns when left and right versions are equal', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'St' }) + ;(getContentType as jest.Mock) + .mockResolvedValueOnce({ uid: 'ct', updated_at: '2020' }) + .mockResolvedValueOnce({ uid: 'ct', updated_at: '2020' }) + + const cmd = new CompareCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + 'content-type': 'home', + left: 3, + right: 3, + 'stack-api-key': 'k', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).printOutput = jest.fn() + ;(cmd as any).warn = jest.fn() + + await cmd.run() + + expect(cmd.warn).toHaveBeenCalledWith( + expect.stringContaining('same version') + ) + }) + + it('calls error when getStack fails', async () => { + ;(getStack as jest.Mock).mockRejectedValue(new Error('fail')) + + const cmd = new CompareCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + 'content-type': 'home', + left: 1, + right: 2, + 'stack-api-key': 'k', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith('fail', { + exit: 1, + suggestions: undefined, + }) + }) +}) diff --git a/tests/commands/content-type/details.test.ts b/tests/commands/content-type/details.test.ts new file mode 100644 index 0000000..f0a3c94 --- /dev/null +++ b/tests/commands/content-type/details.test.ts @@ -0,0 +1,97 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + managementSDKClient: jest.fn().mockResolvedValue({}), + cliux: { + loaderV2: jest.fn().mockReturnValue({}), + print: jest.fn(), + }, + flags: new Proxy( + {}, + { + get: (_t, prop) => + jest.fn((opts: Record) => ({ ...opts, __f: String(prop) })), + } + ), + printFlagDeprecation: jest.fn(() => () => undefined), +})) + +jest.mock('../../../src/utils', () => ({ + getContentType: jest.fn(), + getStack: jest.fn(), +})) + +import { getContentType, getStack } from '../../../src/utils' +import DetailsCommand from '../../../src/commands/content-type/details' + +describe('content-type:details', () => { + it('prints details with references', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'St' }) + ;(getContentType as jest.Mock).mockResolvedValue({ + _version: 1, + created_at: '2020-01-01', + description: 'D', + options: {}, + schema: [], + title: 'T', + uid: 'ct', + updated_at: '2021-01-01', + }) + + const cmd = new DetailsCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { 'content-type': 'home', path: true, 'stack-api-key': 'k' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).client = { + getContentTypeReferences: jest.fn().mockResolvedValue({ + references: ['r1'], + }), + } + ;(cmd as any).printOutput = jest.fn() + + await cmd.run() + + expect(cmd.printOutput).toHaveBeenCalledWith( + expect.objectContaining({ hasResults: false }), + 'details', + 'home', + 'St' + ) + }) + + it('calls error when getContentType fails', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'St' }) + ;(getContentType as jest.Mock).mockRejectedValue(new Error('not found')) + + const cmd = new DetailsCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { 'content-type': 'home', path: true, 'stack-api-key': 'k' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).client = { + getContentTypeReferences: jest.fn().mockResolvedValue({ references: [] }), + } + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith('not found', { + exit: 1, + suggestions: undefined, + }) + }) +}) diff --git a/tests/commands/content-type/diagram.test.ts b/tests/commands/content-type/diagram.test.ts new file mode 100644 index 0000000..5f6f062 --- /dev/null +++ b/tests/commands/content-type/diagram.test.ts @@ -0,0 +1,125 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + managementSDKClient: jest.fn().mockResolvedValue({}), + cliux: { + loaderV2: jest.fn().mockReturnValue({}), + print: jest.fn(), + }, + flags: new Proxy( + {}, + { + get: (_t, prop) => + jest.fn((opts: Record) => ({ ...opts, __f: String(prop) })), + } + ), + printFlagDeprecation: jest.fn(() => () => undefined), +})) + +jest.mock('../../../src/utils', () => ({ + getContentTypes: jest.fn(), + getGlobalFields: jest.fn(), + getStack: jest.fn(), +})) + +jest.mock('../../../src/core/content-type/diagram', () => ({ + createDiagram: jest.fn().mockResolvedValue({ outputPath: '/tmp/out.svg' }), +})) + +import { createDiagram } from '../../../src/core/content-type/diagram' +import { getContentTypes, getGlobalFields, getStack } from '../../../src/utils' +import DiagramCommand from '../../../src/commands/content-type/diagram' + +describe('content-type:diagram', () => { + it('creates diagram file', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'StackName' }) + ;(getContentTypes as jest.Mock).mockResolvedValue([{ schema: [], title: 'A', uid: 'a' }]) + ;(getGlobalFields as jest.Mock).mockResolvedValue([]) + + const cmd = new DiagramCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + direction: 'portrait', + output: 'out/model.svg', + type: 'svg', + 'stack-api-key': 'k', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).log = jest.fn() + + await cmd.run() + + expect(createDiagram).toHaveBeenCalledWith( + expect.objectContaining({ + outputFileName: 'out/model.svg', + stackName: 'StackName', + }) + ) + expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('Created Graph')) + }) + + it('errors when output path is blank', async () => { + const cmd = new DiagramCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + direction: 'portrait', + output: ' ', + type: 'svg', + 'stack-api-key': 'k', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + ;(cmd as any).error = jest.fn() + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith( + expect.stringContaining('output path'), + expect.objectContaining({ exit: 2 }) + ) + }) + + it('calls error when createDiagram fails', async () => { + ;(createDiagram as jest.Mock).mockRejectedValueOnce(new Error('graphviz failed')) + ;(getStack as jest.Mock).mockResolvedValue({ name: 'StackName' }) + ;(getContentTypes as jest.Mock).mockResolvedValue([]) + ;(getGlobalFields as jest.Mock).mockResolvedValue([]) + + const cmd = new DiagramCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { + direction: 'portrait', + output: 'out/x.svg', + type: 'svg', + 'stack-api-key': 'k', + }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'k' + ;(cmd as any).contentTypeManagementClient = {} + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith('graphviz failed', { + exit: 1, + suggestions: undefined, + }) + }) +}) diff --git a/tests/commands/content-type/list.test.ts b/tests/commands/content-type/list.test.ts new file mode 100644 index 0000000..204c1da --- /dev/null +++ b/tests/commands/content-type/list.test.ts @@ -0,0 +1,82 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + managementSDKClient: jest.fn().mockResolvedValue({}), + cliux: { + loaderV2: jest.fn().mockReturnValue({}), + print: jest.fn(), + }, + flags: new Proxy( + {}, + { + get: (_t, prop) => + jest.fn((opts: Record) => ({ ...opts, __f: String(prop) })), + } + ), + printFlagDeprecation: jest.fn(() => () => undefined), +})) + +jest.mock('../../../src/utils', () => ({ + getContentTypes: jest.fn(), + getStack: jest.fn(), +})) + +import { getContentTypes, getStack } from '../../../src/utils' +import ListCommand from '../../../src/commands/content-type/list' + +describe('content-type:list', () => { + it('fetches stack and content types and prints output', async () => { + ;(getStack as jest.Mock).mockResolvedValue({ name: 'Stack One' }) + ;(getContentTypes as jest.Mock).mockResolvedValue([ + { _version: 1, title: 'A', uid: 'a', updated_at: '2020-01-01T00:00:00.000Z' }, + ]) + + const cmd = new ListCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { order: 'title', 'stack-api-key': 'key1' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'key1' + ;(cmd as any).printOutput = jest.fn() + + await cmd.run() + + expect(getStack).toHaveBeenCalled() + expect(getContentTypes).toHaveBeenCalled() + expect(cmd.printOutput).toHaveBeenCalledWith( + expect.objectContaining({ hasResults: true }), + 'Content Types', + null, + 'Stack One' + ) + }) + + it('calls error when a dependency rejects', async () => { + ;(getStack as jest.Mock).mockRejectedValue(new Error('stack failed')) + ;(getContentTypes as jest.Mock).mockResolvedValue([]) + + const cmd = new ListCommand([], {} as any) + ;(cmd as any).cmaHost = 'https://api.contentstack.io' + ;(cmd as any).context = { analyticsInfo: {} } + ;(cmd as any).parse = jest.fn().mockResolvedValue({ + flags: { order: 'title', 'stack-api-key': 'key1' }, + }) + ;(cmd as any).setup = jest.fn().mockResolvedValue(undefined) + ;(cmd as any).apiKey = 'key1' + + await cmd.run() + + expect(cmd.error).toHaveBeenCalledWith('stack failed', { + exit: 1, + suggestions: undefined, + }) + }) +}) diff --git a/tests/core/command.test.ts b/tests/core/command.test.ts new file mode 100644 index 0000000..b56f811 --- /dev/null +++ b/tests/core/command.test.ts @@ -0,0 +1,183 @@ +jest.mock('@contentstack/cli-command', () => ({ + Command: class Command { + log = jest.fn() + error = jest.fn() + warn = jest.fn() + getToken(_alias: string) { + return { apiKey: 'default-api', type: 'management' } + } + }, +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + authenticationHandler: { + accessToken: 'auth-token', + getAuthDetails: jest.fn(), + }, + cliux: { + print: jest.fn(), + }, +})) + +import { authenticationHandler, cliux } from '@contentstack/cli-utilities' + +import ContentTypeCommand from '../../src/core/command' +import ContentstackClient from '../../src/core/contentstack/client' + +jest.mock('../../src/core/contentstack/client', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(function ContentstackClientMock( + this: { host: string; token: string }, + host: string, + token: string + ) { + this.host = host + this.token = token + }), +})) + +describe('ContentTypeCommand', () => { + let cmd: ContentTypeCommand + + function makeCmd() { + const c = Object.create(ContentTypeCommand.prototype) as ContentTypeCommand + c.log = jest.fn() + ;(c as any).cmaHost = 'https://api.contentstack.io' + c.error = jest.fn() as any + return c + } + + beforeEach(() => { + jest.clearAllMocks() + cmd = makeCmd() + ;(authenticationHandler.getAuthDetails as jest.Mock).mockResolvedValue(undefined) + ;(authenticationHandler as any).accessToken = 'auth-token' + }) + + describe('printOutput', () => { + it('logs header, body, footer when hasResults', () => { + cmd.printOutput( + { + body: 'B', + footer: 'F', + hasResults: true, + header: 'H', + rows: [], + }, + 'Audit Logs', + 'ct-uid', + 'StackName' + ) + expect(cmd.log).toHaveBeenCalledWith( + expect.stringContaining("Requested Audit Logs for 'ct-uid' on 'StackName.'") + ) + expect(cmd.log).toHaveBeenCalledWith('H') + expect(cmd.log).toHaveBeenCalledWith('B') + expect(cmd.log).toHaveBeenCalledWith('F') + }) + + it('omits what when null', () => { + cmd.printOutput( + { + body: 'B', + footer: null, + hasResults: true, + header: null, + rows: [], + }, + 'Content Types', + null, + 'S' + ) + expect(cmd.log).toHaveBeenCalledWith( + expect.stringContaining('Requested Content Types on') + ) + }) + + it('logs no results when hasResults false', () => { + cmd.printOutput( + { + body: '', + footer: null, + hasResults: false, + header: null, + rows: [], + }, + 'things', + null, + 'S' + ) + expect(cmd.log).toHaveBeenCalledWith('No things found.') + }) + }) + + describe('setup', () => { + it('errors when not logged in', async () => { + ;(authenticationHandler as any).accessToken = undefined + await cmd.setup({ 'stack-api-key': 'k' }) + expect(cmd.error).toHaveBeenCalledWith( + expect.stringContaining('not logged in'), + expect.objectContaining({ exit: 2 }) + ) + }) + + it('exits when no token alias and no stack key', async () => { + const exitSpy = jest + .spyOn(process, 'exit') + .mockImplementation((code?: string | number | null) => { + throw new Error(`exit:${code}`) + }) + try { + await expect(cmd.setup({})).rejects.toThrow('exit:1') + expect(cliux.print).toHaveBeenCalledWith( + expect.stringContaining('token alias or a stack UID'), + { color: 'red' } + ) + } finally { + exitSpy.mockRestore() + } + }) + + it('sets apiKey from stack-api-key and constructs client', async () => { + await cmd.setup({ 'stack-api-key': 'stack-key-1' }) + expect((cmd as any).apiKey).toBe('stack-key-1') + expect(ContentstackClient).toHaveBeenCalledWith( + 'https://api.contentstack.io', + 'auth-token' + ) + }) + + it('sets apiKey from alias token and warns on delivery token', async () => { + jest.spyOn(cmd, 'getToken').mockReturnValue({ + apiKey: 'from-token', + type: 'delivery', + } as any) + await cmd.setup({ alias: 'my-alias' }) + expect((cmd as any).apiKey).toBe('from-token') + expect(cliux.print).toHaveBeenCalledWith( + expect.stringContaining('delivery token'), + { color: 'yellow' } + ) + }) + + it('sets apiKey from management token alias without warning', async () => { + jest.spyOn(cmd, 'getToken').mockReturnValue({ + apiKey: 'mgmt-key', + type: 'management', + } as any) + await cmd.setup({ alias: 'm' }) + expect((cmd as any).apiKey).toBe('mgmt-key') + expect(cliux.print).not.toHaveBeenCalledWith( + expect.stringContaining('delivery token'), + expect.anything() + ) + }) + }) + + describe('run', () => { + it('logs the default content-type command message', async () => { + await cmd.run() + expect(cmd.log).toHaveBeenCalledWith('content type command') + }) + }) +}) diff --git a/tests/core/content-type/audit.test.ts b/tests/core/content-type/audit.test.ts new file mode 100644 index 0000000..a52d0ab --- /dev/null +++ b/tests/core/content-type/audit.test.ts @@ -0,0 +1,38 @@ +import buildOutput from '../../../src/core/content-type/audit' +import * as format from '../../../src/core/content-type/formatting' + +describe('audit buildOutput', () => { + const users = [ + { first_name: 'Jane', last_name: 'Doe', uid: 'u1' } + ] + + it('should have no results when logs is empty', () => { + const out = buildOutput([], users) + expect(out.hasResults).toBe(false) + expect(out.rows).toHaveLength(1) + expect(out.rows[0]).toEqual(['Event', 'User', 'Modified', 'Version']) + expect(out.footer).toBeNull() + expect(out.body).toContain('Event') + }) + + it('should map one log row and set hasResults', () => { + const createdAt = '2024-01-10T10:00:00.000Z' + const logs = [ + { + created_at: createdAt, + created_by: 'u1', + event_type: 'update', + metadata: { version: 3 } + } + ] + const out = buildOutput(logs, users) + expect(out.hasResults).toBe(true) + expect(out.rows).toHaveLength(2) + expect(out.rows[1][0]).toBe('update') + expect(out.rows[1][1]).toBe('Jane Doe') + expect(out.rows[1][2]).toBe(format.date(createdAt)) + expect(out.rows[1][3]).toBe(3) + expect(out.body).toContain('update') + expect(out.body).toContain('Jane Doe') + }) +}) diff --git a/tests/core/content-type/compare.test.ts b/tests/core/content-type/compare.test.ts new file mode 100644 index 0000000..d092d9e --- /dev/null +++ b/tests/core/content-type/compare.test.ts @@ -0,0 +1,56 @@ +jest.mock('cli-ux', () => ({ + __esModule: true, + default: { + open: jest.fn().mockResolvedValue(undefined), + }, +})) + +jest.mock('tmp', () => ({ + file: jest.fn( + ( + _opts: unknown, + cb: (err: Error | null, path: string, fd: number, cleanup: () => void) => void + ) => { + cb(null, '/tmp/fake-compare.html', 0, jest.fn()) + } + ), +})) + +jest.mock('fs', () => ({ + writeFileSync: jest.fn(), +})) + +jest.mock('diff2html', () => ({ + parse: jest.fn(() => [{ value: 'parsed' }]), + html: jest.fn(() => '
diff-html
'), +})) + +jest.mock('git-diff', () => + jest.fn(() => '@@ -1 +1 @@\n+changed') +) + +import fs from 'fs' + +import buildOutput from '../../../src/core/content-type/compare' + +describe('compare buildOutput', () => { + const prev = { uid: 'ct', updated_at: '2020-01-01' } + const curr = { uid: 'ct', updated_at: '2021-01-01' } + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('returns BuildOutput and writes HTML file with diff2html body', async () => { + const out = await buildOutput('my-ct', prev, curr) + + expect(out.hasResults).toBe(true) + expect(out.body).toBe('Please check the browser output.') + expect(out.header).toBeNull() + expect(fs.writeFileSync).toHaveBeenCalled() + const written = (fs.writeFileSync as jest.Mock).mock.calls[0][1] as string + expect(written).toContain('') + expect(written).toContain('diff-html') + expect(written).toContain('diff2html') + }) +}) diff --git a/tests/core/content-type/details.test.ts b/tests/core/content-type/details.test.ts new file mode 100644 index 0000000..d8abb0b --- /dev/null +++ b/tests/core/content-type/details.test.ts @@ -0,0 +1,136 @@ +import buildOutput from '../../../src/core/content-type/details' + +describe('details buildOutput', () => { + const baseCt = { + _version: 1, + created_at: '2020-01-01T00:00:00.000Z', + description: 'Desc', + options: { is_page: false, singleton: false }, + schema: [] as any[], + title: 'My CT', + uid: 'ct1', + updated_at: '2021-01-01T00:00:00.000Z', + } + + it('builds header with description and references', () => { + const out = buildOutput( + baseCt, + { references: ['ref-a', 'ref-b'] }, + { showPath: true } + ) + expect(out.hasResults).toBe(false) + expect(out.header).toContain('Description: Desc') + expect(out.header).toContain('Referenced By: ref-a, ref-b') + expect(out.footer).toBeNull() + }) + + it('uses None when no description and no references', () => { + const out = buildOutput( + { ...baseCt, description: undefined }, + { references: [] }, + { showPath: true } + ) + expect(out.header).toContain('Description: None') + expect(out.header).toContain('Referenced By: None') + }) + + it('renders flat schema with R/M/U column', () => { + const ct = { + ...baseCt, + schema: [ + { + data_type: 'text', + display_name: 'Title', + mandatory: true, + multiple: false, + uid: 'title', + unique: true, + }, + ], + } + const out = buildOutput(ct, { references: [] }, { showPath: true }) + expect(out.hasResults).toBe(true) + expect(out.body).toContain('Title') + expect(out.body).toContain('text') + expect(out.body).toContain('title') + expect(out.body).toContain('RU') + }) + + it('nested schema adds parent prefix in display name', () => { + const ct = { + ...baseCt, + schema: [ + { + data_type: 'group', + display_name: 'Group', + schema: [ + { + data_type: 'text', + display_name: 'Child', + mandatory: false, + multiple: true, + uid: 'child', + unique: false, + }, + ], + uid: 'grp', + }, + ], + } + const out = buildOutput(ct, { references: [] }, { showPath: true }) + expect(out.body).toContain('[grp] Child') + expect(out.body).toContain('grp.child') + expect(out.body).toContain('M') + }) + + it('processes blocks with nested schema', () => { + const ct = { + ...baseCt, + schema: [ + { + blocks: [ + { + schema: [ + { + data_type: 'number', + display_name: 'Num', + mandatory: false, + multiple: false, + uid: 'num', + unique: false, + }, + ], + title: 'BlockTitle', + uid: 'blk', + }, + ], + data_type: 'blocks', + display_name: 'Modular', + uid: 'mod', + }, + ], + } + const out = buildOutput(ct, { references: [] }, { showPath: true }) + expect(out.body).toContain('[BlockTitle] Num') + expect(out.body).toContain('number') + }) + + it('hides path column when showPath false', () => { + const ct = { + ...baseCt, + schema: [ + { + data_type: 'text', + display_name: 'T', + mandatory: false, + multiple: false, + uid: 'u', + unique: false, + }, + ], + } + const out = buildOutput(ct, { references: [] }, { showPath: false }) + expect(out.body).not.toContain('Path') + expect(out.body).not.toContain('ct1.u') + }) +}) diff --git a/tests/core/content-type/diagram.test.ts b/tests/core/content-type/diagram.test.ts new file mode 100644 index 0000000..f659fda --- /dev/null +++ b/tests/core/content-type/diagram.test.ts @@ -0,0 +1,165 @@ +jest.mock('node-graphviz', () => ({ + graphviz: { + dot: jest.fn().mockResolvedValue(''), + }, +})) + +jest.mock('fs', () => ({ + writeFileSync: jest.fn(), + mkdirSync: jest.fn(), +})) + +jest.mock('@contentstack/cli-utilities', () => ({ + sanitizePath: (p: string) => p, +})) + +import fs from 'fs' +import { graphviz } from 'node-graphviz' + +import { createDiagram } from '../../../src/core/content-type/diagram' +import { DiagramOrientation } from '../../../src/types' + +describe('diagram createDiagram', () => { + beforeEach(() => { + jest.clearAllMocks() + ;(graphviz.dot as jest.Mock).mockResolvedValue('') + }) + + function options(overrides: Partial[0]> = {}) { + const contentTypes = [ + { + schema: [ + { + data_type: 'reference', + display_name: 'Ref to other', + mandatory: false, + multiple: false, + reference_to: ['other_ct'], + uid: 'ref_field', + unique: false, + }, + { + data_type: 'global_field', + display_name: 'GF', + mandatory: false, + multiple: false, + reference_to: 'global_uid', + uid: 'gf_field', + unique: false, + }, + { + data_type: 'group', + display_name: 'Group', + mandatory: false, + multiple: false, + schema: [ + { + data_type: 'text', + display_name: 'Nested', + mandatory: true, + multiple: false, + uid: 'nested_text', + unique: false, + }, + ], + uid: 'group1', + unique: false, + }, + { + blocks: [ + { + schema: [ + { + data_type: 'number', + display_name: 'In block', + mandatory: false, + multiple: false, + uid: 'num1', + unique: false, + }, + ], + title: 'Blk', + uid: 'blk1', + mandatory: false, + multiple: false, + unique: false, + }, + { + reference_to: 'gf_blk', + title: 'Blk2', + uid: 'blk2', + mandatory: false, + multiple: false, + unique: false, + }, + ], + data_type: 'blocks', + display_name: 'Modular', + mandatory: false, + multiple: false, + uid: 'mod1', + unique: false, + }, + ], + title: 'Blog', + uid: 'blog', + }, + ] + + const globalFields = [ + { + schema: [ + { + data_type: 'text', + display_name: 'GText', + mandatory: false, + multiple: false, + uid: 'g1', + unique: false, + }, + ], + title: 'Global X', + uid: 'gfx', + }, + ] + + return { + contentTypes, + globalFields, + outputFileName: 'out/model.svg', + outputFileType: 'svg', + stackName: 'My Stack', + style: { orientation: DiagramOrientation.Portrait }, + ...overrides, + } + } + + it('writes dot output to resolved path and passes DOT to graphviz', async () => { + const res = await createDiagram(options()) + + expect(res.outputPath).toContain('out') + expect(res.outputPath).toContain('model.svg') + expect(fs.mkdirSync).toHaveBeenCalled() + expect(fs.writeFileSync).toHaveBeenCalledWith(res.outputPath, '') + + expect(graphviz.dot).toHaveBeenCalled() + const dotArg = (graphviz.dot as jest.Mock).mock.calls[0][0] as string + expect(dotArg).toContain('digraph content_model') + expect(dotArg).toContain('My Stack') + expect(dotArg).toContain('Blog') + expect(dotArg).toContain('Global X') + expect(dotArg).toContain('reference') + expect(dotArg).toContain('global_field') + expect(dotArg).toContain('LR') + }) + + it('uses landscape orientation when set', async () => { + await createDiagram( + options({ + style: { orientation: DiagramOrientation.Landscape }, + }) + ) + const dotArg = (graphviz.dot as jest.Mock).mock.calls[0][0] as string + expect(dotArg).toContain('TD') + }) +}) diff --git a/tests/core/content-type/formatting.test.ts b/tests/core/content-type/formatting.test.ts new file mode 100644 index 0000000..92cf799 --- /dev/null +++ b/tests/core/content-type/formatting.test.ts @@ -0,0 +1,102 @@ +import * as format from '../../../src/core/content-type/formatting' + +describe('formatting', () => { + describe('date', () => { + it('should format a fixed ISO timestamp consistently', () => { + const iso = '2024-06-15T14:30:00.000Z' + const out = format.date(iso) + expect(typeof out).toBe('string') + expect(out.length).toBeGreaterThan(4) + }) + }) + + describe('fullName', () => { + it('should return Unknown when users is missing or empty', () => { + expect(format.fullName(undefined, 'u1')).toBe('Unknown') + expect(format.fullName([], 'u1')).toBe('Unknown') + }) + + it('should return Unknown when uid is not found', () => { + const users = [{ first_name: 'A', last_name: 'One', uid: 'a' }] + expect(format.fullName(users, 'missing')).toBe('Unknown') + }) + + it('should return first and last name when uid matches', () => { + const users = [ + { first_name: 'Ann', last_name: 'Alpha', uid: 'a' }, + { first_name: 'Bob', last_name: 'Beta', uid: 'b' } + ] + expect(format.fullName(users, 'b')).toBe('Bob Beta') + }) + }) + + describe('contentType', () => { + it('should return Unknown when contentType or options is missing', () => { + expect(format.contentType(undefined)).toBe('Unknown') + expect(format.contentType({})).toBe('Unknown') + }) + + it('should return Page when is_page is true', () => { + expect(format.contentType({ options: { is_page: true } })).toBe('Page') + }) + + it('should return Content Block when is_page is false', () => { + expect(format.contentType({ options: { is_page: false } })).toBe( + 'Content Block' + ) + }) + }) + + describe('allowMultiple', () => { + it('should return Unknown when contentType or options is missing', () => { + expect(format.allowMultiple(undefined)).toBe('Unknown') + expect(format.allowMultiple({})).toBe('Unknown') + }) + + it('should return False when singleton is true', () => { + expect(format.allowMultiple({ options: { singleton: true } })).toBe( + 'False' + ) + }) + + it('should return True when singleton is false', () => { + expect(format.allowMultiple({ options: { singleton: false } })).toBe( + 'True' + ) + }) + }) + + describe('urlPattern', () => { + it('should return Unknown when contentType or options is missing', () => { + expect(format.urlPattern(undefined)).toBe('Unknown') + expect(format.urlPattern({})).toBe('Unknown') + }) + + it('should return N/A when url_pattern is missing', () => { + expect( + format.urlPattern({ options: { url_prefix: '/blog' } }) + ).toBe('N/A') + }) + + it('should join url_prefix and url_pattern', () => { + expect( + format.urlPattern({ + options: { + url_pattern: ':slug', + url_prefix: '/articles' + } + }) + ).toBe('/articles/:slug') + }) + }) + + describe('checked', () => { + it('should return asterisk when value is true', () => { + expect(format.checked(true)).toBe('*') + }) + + it('should return undefined when value is false', () => { + expect(format.checked(false)).toBeUndefined() + }) + }) +}) diff --git a/tests/core/content-type/list.test.ts b/tests/core/content-type/list.test.ts new file mode 100644 index 0000000..930f344 --- /dev/null +++ b/tests/core/content-type/list.test.ts @@ -0,0 +1,49 @@ +import * as format from '../../../src/core/content-type/formatting' +import buildOutput from '../../../src/core/content-type/list' + +describe('list buildOutput', () => { + const cts = [ + { + _version: 1, + title: 'Zebra', + uid: 'z', + updated_at: '2020-01-01T00:00:00.000Z' + }, + { + _version: 2, + title: 'Alpha', + uid: 'a', + updated_at: '2024-06-01T00:00:00.000Z' + }, + { + _version: 3, + title: 'Mike', + uid: 'm', + updated_at: '2022-01-01T00:00:00.000Z' + } + ] + + it('should sort by title ascending', () => { + const out = buildOutput(cts, 'title') + expect(out.hasResults).toBe(true) + expect(out.footer).toBe('Count: 3') + const dataRows = out.rows.slice(1) + expect(dataRows.map((r) => r[0])).toEqual(['Alpha', 'Mike', 'Zebra']) + }) + + it('should sort by modified descending (newest first)', () => { + const out = buildOutput(cts, 'modified') + const dataRows = out.rows.slice(1) + expect(dataRows.map((r) => r[0])).toEqual(['Alpha', 'Mike', 'Zebra']) + expect(dataRows[0][2]).toBe( + format.date('2024-06-01T00:00:00.000Z') + ) + }) + + it('should report zero count when empty', () => { + const out = buildOutput([], 'title') + expect(out.hasResults).toBe(false) + expect(out.footer).toBe('Count: 0') + expect(out.rows).toHaveLength(1) + }) +}) diff --git a/tests/core/contentstack/client.test.ts b/tests/core/contentstack/client.test.ts new file mode 100644 index 0000000..4580083 --- /dev/null +++ b/tests/core/contentstack/client.test.ts @@ -0,0 +1,138 @@ +import axios from 'axios' + +import ContentstackClient from '../../../src/core/contentstack/client' +import ContentstackError from '../../../src/core/contentstack/error' + +jest.mock('axios') +jest.mock('@contentstack/cli-utilities', () => ({ + cliux: { + loaderV2: jest.fn() + } +})) + +const mockedAxios = axios as jest.Mocked + +describe('ContentstackClient', () => { + const mockGet = jest.fn() + const spinner = {} + + beforeEach(() => { + jest.clearAllMocks() + mockGet.mockResolvedValue({ data: { ok: true } }) + mockedAxios.create.mockReturnValue({ + get: mockGet + } as any) + }) + + describe('getContentTypeAuditLogs', () => { + it('should call GET /audit-logs with query and api_key header', async () => { + const client = new ContentstackClient('api.contentstack.io', 'token123') + await client.getContentTypeAuditLogs('stack-key', 'ct-uid', spinner) + + expect(mockedAxios.create).toHaveBeenCalledWith({ + baseURL: 'https://api.contentstack.io/v3/', + headers: { authtoken: 'token123' } + }) + expect(mockGet).toHaveBeenCalledWith( + '/audit-logs', + expect.objectContaining({ + headers: { api_key: 'stack-key' }, + params: { + query: { + $and: [ + { module: 'content_type' }, + { 'metadata.uid': 'ct-uid' } + ] + } + } + }) + ) + }) + + it('should use authorization header when token includes Bearer', async () => { + new ContentstackClient('host', 'Bearer xyz') + expect(mockedAxios.create).toHaveBeenCalledWith({ + baseURL: 'https://host/v3/', + headers: { authorization: 'Bearer xyz' } + }) + }) + + it('should return response data', async () => { + mockGet.mockResolvedValueOnce({ data: { logs: [{ id: 1 }] } }) + const client = new ContentstackClient('h', 't') + await expect( + client.getContentTypeAuditLogs('k', 'u', spinner) + ).resolves.toEqual({ logs: [{ id: 1 }] }) + }) + + it('should throw ContentstackError when API returns structured error', async () => { + mockGet.mockRejectedValueOnce({ + response: { + data: { + error_code: 401, + error_message: 'Bad key', + errors: { api_key: ['invalid'] } + } + } + }) + const client = new ContentstackClient('h', 't') + const err = await client + .getContentTypeAuditLogs('my-key', 'u', spinner) + .then( + () => { + throw new Error('expected rejection') + }, + (e) => e + ) + expect(err).toBeInstanceOf(ContentstackError) + expect((err as ContentstackError).message).toContain( + "Stack API Key 'my-key'" + ) + expect((err as ContentstackError).status).toBe(401) + }) + + it('should throw generic Error when response shape is unrecognized', async () => { + mockGet.mockRejectedValueOnce({ response: {} }) + const client = new ContentstackClient('h', 't') + await expect( + client.getContentTypeAuditLogs('k', 'u', spinner) + ).rejects.toThrow('Unrecognized error') + }) + }) + + describe('getContentTypeReferences', () => { + it('should call GET content_types uid references with params', async () => { + const client = new ContentstackClient('api.contentstack.io', 'token123') + await client.getContentTypeReferences('stack-key', 'ct-uid', spinner) + + expect(mockGet).toHaveBeenCalledWith('/content_types/ct-uid/references', { + headers: { api_key: 'stack-key' }, + params: { include_global_fields: true } + }) + }) + + it('should return response data', async () => { + mockGet.mockResolvedValueOnce({ data: { references: [] } }) + const client = new ContentstackClient('h', 't') + await expect( + client.getContentTypeReferences('k', 'uid-1', spinner) + ).resolves.toEqual({ references: [] }) + }) + + it('should throw when API returns errors for references', async () => { + mockGet.mockRejectedValueOnce({ + response: { + data: { + error_code: 404, + error_message: 'Missing', + errors: {} + } + } + }) + const client = new ContentstackClient('h', 't') + await expect( + client.getContentTypeReferences('k', 'uid-1', spinner) + ).rejects.toThrow() + }) + }) +}) diff --git a/tests/core/contentstack/error.test.ts b/tests/core/contentstack/error.test.ts new file mode 100644 index 0000000..484b13b --- /dev/null +++ b/tests/core/contentstack/error.test.ts @@ -0,0 +1,16 @@ +import ContentstackError from '../../../src/core/contentstack/error' + +describe('ContentstackError', () => { + it('should set message, status, and name', () => { + const err = new ContentstackError('not found', 404) + expect(err.message).toBe('not found') + expect(err.status).toBe(404) + expect(err.name).toBe('ContentstackError') + }) + + it('should be instanceof Error', () => { + const err = new ContentstackError('x', 500) + expect(err).toBeInstanceOf(Error) + expect(err).toBeInstanceOf(ContentstackError) + }) +}) diff --git a/tests/utils/index.test.ts b/tests/utils/index.test.ts new file mode 100644 index 0000000..e38d8e9 --- /dev/null +++ b/tests/utils/index.test.ts @@ -0,0 +1,292 @@ +/** + * Success-path tests for utils; error-path tests mock process.exit. + */ +import { cliux } from '@contentstack/cli-utilities' + +import config from '../../src/config' +import { + getContentType, + getContentTypes, + getGlobalFields, + getStack, + getUsers +} from '../../src/utils/index' + +jest.mock('@contentstack/cli-utilities', () => ({ + cliux: { + loaderV2: jest.fn(), + print: jest.fn() + } +})) + +describe('utils/index', () => { + const spinner = {} + + beforeEach(() => { + jest.clearAllMocks() + }) + + function stackFactory(overrides: { + contentTypeFetch?: jest.Mock + fetchData?: any + findImpl?: jest.Mock + globalFindImpl?: jest.Mock + usersData?: any + }) { + const { + contentTypeFetch, + fetchData = { + master_locale: 'en-us', + name: 'Stack', + org_uid: 'org-1', + uid: 'stack-uid' + }, + findImpl, + globalFindImpl, + usersData = [] + } = overrides + + const ctFind = + findImpl || + jest.fn().mockResolvedValue({ count: 1, items: [{ uid: 'ct1' }] }) + const gfFind = + globalFindImpl || + jest.fn().mockResolvedValue({ count: 1, items: [{ uid: 'gf1' }] }) + + return { + stack: jest.fn().mockReturnValue({ + contentType: jest.fn((uid?: string) => { + if (uid) { + return { + fetch: contentTypeFetch || jest.fn().mockResolvedValue({ + _version: 7, + title: 'CT', + uid + }) + } + } + return { + query: () => ({ + find: ctFind + }) + } + }), + fetch: jest.fn().mockResolvedValue(fetchData), + globalField: () => ({ + query: () => ({ + find: gfFind + }) + }), + users: jest.fn().mockResolvedValue(usersData) + }) + } + } + + it('getStack should map stack fields', async () => { + const sdk = stackFactory({}) + const stack = await getStack(sdk, 'api-key-1', spinner) + expect(stack).toEqual({ + api_key: 'api-key-1', + master_locale: 'en-us', + name: 'Stack', + org_uid: 'org-1', + uid: 'stack-uid' + }) + expect(sdk.stack).toHaveBeenCalledWith({ api_key: 'api-key-1' }) + }) + + it('getUsers should return users array', async () => { + const users = [{ first_name: 'A', last_name: 'B', uid: 'u1' }] + const sdk = stackFactory({ usersData: users }) + await expect(getUsers(sdk, 'k', spinner)).resolves.toEqual(users) + }) + + it('getContentTypes should aggregate single page', async () => { + const find = jest + .fn() + .mockResolvedValue({ count: 2, items: [{ uid: 'a' }, { uid: 'b' }] }) + const sdk = stackFactory({ findImpl: find }) + const items = await getContentTypes(sdk, 'k', spinner) + expect(items).toHaveLength(2) + expect(find).toHaveBeenCalledTimes(1) + }) + + it('getContentTypes should paginate when count exceeds skip + limit', async () => { + const limit = config.limit + const page1 = Array(limit) + .fill(null) + .map((_, i) => ({ uid: `p1-${i}` })) + const page2 = [{ uid: 'last' }] + const find = jest + .fn() + .mockResolvedValueOnce({ count: limit + 1, items: page1 }) + .mockResolvedValueOnce({ count: limit + 1, items: page2 }) + const sdk = stackFactory({ findImpl: find }) + const items = await getContentTypes(sdk, 'k', spinner) + expect(find).toHaveBeenCalledTimes(2) + expect(items).toHaveLength(limit + 1) + }) + + it('getGlobalFields should paginate like getContentTypes', async () => { + const limit = config.limit + const page1 = Array(limit) + .fill(null) + .map((_, i) => ({ uid: `gf-${i}` })) + const find = jest + .fn() + .mockResolvedValueOnce({ count: limit + 1, items: page1 }) + .mockResolvedValueOnce({ count: limit + 1, items: [{ uid: 'x' }] }) + const sdk = stackFactory({ globalFindImpl: find }) + const items = await getGlobalFields(sdk, 'k', spinner) + expect(find).toHaveBeenCalledTimes(2) + expect(items).toHaveLength(limit + 1) + }) + + it('getContentType should fetch without version when ctVersion omitted', async () => { + const fetch = jest.fn().mockResolvedValue({ title: 'T', uid: 'ct' }) + const sdk = stackFactory({ contentTypeFetch: fetch }) + const ct = await getContentType({ + apiKey: 'k', + managementSdk: sdk, + spinner, + uid: 'ct-uid' + }) + expect(ct).toEqual({ title: 'T', uid: 'ct' }) + expect(fetch).toHaveBeenCalledWith({}) + }) + + it('getContentType should pass version when ctVersion set', async () => { + const fetch = jest.fn().mockResolvedValue({ _version: 3, uid: 'ct' }) + const sdk = stackFactory({ contentTypeFetch: fetch }) + await getContentType({ + apiKey: 'k', + ctVersion: '2', + managementSdk: sdk, + spinner, + uid: 'ct-uid' + }) + expect(fetch).toHaveBeenCalledWith({ version: '2' }) + }) +}) + +describe('utils/index error paths', () => { + const spinner = {} + + function exitThrows() { + return jest + .spyOn(process, 'exit') + .mockImplementation((() => { + throw new Error('process.exit') + }) as (code?: string | number | null | undefined) => never) + } + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('getContentType: prints errorMessage and exits on API error', async () => { + const exitSpy = exitThrows() + const fetch = jest.fn().mockRejectedValue({ errorMessage: 'Bad request' }) + const sdk = { + stack: jest.fn().mockReturnValue({ + contentType: jest.fn(() => ({ + fetch + })) + }) + } + await expect( + getContentType({ + apiKey: 'k', + managementSdk: sdk, + spinner, + uid: 'u' + }) + ).rejects.toThrow('process.exit') + expect(cliux.print).toHaveBeenCalledWith('Error: Bad request', { + color: 'red' + }) + exitSpy.mockRestore() + }) + + it('getContentType: prints message when no errorMessage', async () => { + const exitSpy = exitThrows() + const fetch = jest.fn().mockRejectedValue({ message: 'Network down' }) + const sdk = { + stack: jest.fn().mockReturnValue({ + contentType: jest.fn(() => ({ + fetch + })) + }) + } + await expect( + getContentType({ + apiKey: 'k', + managementSdk: sdk, + spinner, + uid: 'u' + }) + ).rejects.toThrow('process.exit') + expect(cliux.print).toHaveBeenCalledWith('Error: Network down', { + color: 'red' + }) + exitSpy.mockRestore() + }) + + it('getContentType: prints fallback when error has no message', async () => { + const exitSpy = exitThrows() + const fetch = jest.fn().mockRejectedValue({}) + const sdk = { + stack: jest.fn().mockReturnValue({ + contentType: jest.fn(() => ({ + fetch + })) + }) + } + await expect( + getContentType({ + apiKey: 'k', + managementSdk: sdk, + spinner, + uid: 'u' + }) + ).rejects.toThrow('process.exit') + expect(cliux.print).toHaveBeenCalledWith( + 'Error: Something went wrong.Please try again!', + { color: 'red' } + ) + exitSpy.mockRestore() + }) + + it('getStack: prints errorMessage and exits on fetch failure', async () => { + const exitSpy = exitThrows() + const sdk = { + stack: jest.fn().mockReturnValue({ + fetch: jest.fn().mockRejectedValue({ errorMessage: 'no stack' }) + }) + } + await expect( + getStack(sdk, 'k', spinner) + ).rejects.toThrow('process.exit') + expect(cliux.print).toHaveBeenCalledWith('Error: no stack', { color: 'red' }) + exitSpy.mockRestore() + }) + + it('getContentTypes: prints message on query failure', async () => { + const exitSpy = exitThrows() + const find = jest.fn().mockRejectedValue({ message: 'query failed' }) + const sdk = { + stack: jest.fn().mockReturnValue({ + contentType: jest.fn(() => ({ + query: () => ({ find }) + })) + }) + } + await expect(getContentTypes(sdk, 'k', spinner)).rejects.toThrow( + 'process.exit' + ) + expect(cliux.print).toHaveBeenCalledWith('Error: query failed', { + color: 'red' + }) + exitSpy.mockRestore() + }) +})