From 369f58f022a4bf4b0c6e912c0be557ccb226d5ad Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 7 Sep 2026 23:34:35 -0500 Subject: [PATCH 01/15] feat(spec): add issue resolution specification for SDK packaging version issues This commit introduces a new specification document addressing defects in the SDK packaging mechanism, specifically focusing on malformed version strings and incorrect dist-tags. The spec outlines the problem, reproduction steps, expected vs. actual behavior, and the impact on customers, aiming to guide the resolution process for these critical issues. --- specs/37399-sdk-packaging-version-fix/spec.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 specs/37399-sdk-packaging-version-fix/spec.md diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md new file mode 100644 index 000000000000..f7c0a7411276 --- /dev/null +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -0,0 +1,137 @@ +# Issue Resolution Specification: SDK packaging — malformed published version strings and floating/orphaned dist-tags in SDK sources and example apps + +**Feature Branch**: `37399-sdk-packaging-version-fix` + +**Created**: 2026-09-04 + +**Status**: Draft + +**Type**: Issue / Bug Resolution + +**Related GitHub Issue**: [#36891](https://github.com/dotCMS/core/issues/36891) + +**Input**: User description: "SDK packaging malformed version strings and floating dist-tags — see agreed scope below" + + + +## Problem Statement *(mandatory)* + +The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) has two related classes of defect that together make the SDK's published packages, and the example apps that scaffold customer projects, unreliable: + +1. **Published packages report a version that does not exist.** The version string written into a published package's `package.json` (and into the version ranges other `@dotcms/*` packages depend on) is not valid semver and does not match what the npm registry advertises for that same package. +2. **Floating/incorrect version specifiers leak into what customers install, and sibling SDK packages are declared with the wrong dependency type.** SDK library source files hardcode `"@dotcms/*": "latest"`, and — separately from the value being wrong — `react`, `angular`, `vue`, and `analytics` declare their runtime siblings (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies` rather than `peerDependencies`. A regular `dependency` gets independently resolved by yarn/pnpm, which is what lets those two package managers install a second, mismatched copy of `@dotcms/client` nested under the consumer's own pinned copy — the same failure mode `react-dom` avoids by declaring `react` as a peer instead of a dependency. `main`'s example apps (`nextjs`, `vuejs`) point to `"next"`, an internal dev/QA pre-release tag never meant for customers — wrong regardless of any version-skew question, since it isn't even a released version. And critically, both active LTS branches' own dedicated `examples/nextjs` copies point to a floating tag (`"latest"`) instead of a version pinned to that LTS server, letting the installed SDK run far ahead of it and breaking the scaffolded app — these LTS-branch copies exist specifically to demonstrate against that LTS server, so floating there defeats their purpose. (`main`'s own four examples deliberately tracking `"latest"` going forward — as the correct target for dotCMS Evergreen customers — is an accepted product decision, not a defect; see Fix Scope & Non-Goals.) On two package managers (yarn, pnpm) the SDK library issue above additionally silently overrides a customer's own explicit version pin. + +**Severity / Impact**: High. This has already caused a live, escalated customer support ticket (Freshdesk #38677, against a 25.07.10 LTS server) and three prior tickets of the same class (Freshdesk #36678, #37710, #38038). Affected customers are anyone who: (a) inspects an installed `@dotcms/*` package's reported version (SBOM/license scanners, vulnerability tooling, bundler metadata), (b) installs the SDK with yarn or pnpm alongside an explicit version pin, or (c) scaffolds one of the four documented example apps, especially against an LTS server. The scaffold path is the most severe because it is the officially-documented onboarding path and, per the SDK's own `DotCMSPage` GraphQL fragment being hardcoded, pinning the SDK version is the *only* customer-side mitigation available — and that mitigation is exactly what's broken. + +## Reproduction *(mandatory)* + +**Environment**: npm registry (`registry.npmjs.org`), `dotCMS/core` `main` branch and the two active LTS branches (`release-25.07.10_lts_v12`, `release-25.07.10_lts_v16`); reproduced with npm 11, yarn 1.22.21, pnpm 11.7.0. + +**Steps to Reproduce**: + +1. **Malformed version (Defect A)**: `curl -s https://registry.npmjs.org/@dotcms/client/ | jq -r .version` returns a well-formed version (e.g. `26.8.3-1`); downloading and extracting that same tarball and reading its bundled `package.json` returns a different, invalid-semver string with leading zeros (e.g. `26.08.03-01`) — and `@dotcms/react`'s own dependency on `@dotcms/client` is pinned to that same malformed string, which is not a version that exists in the registry's `versions` map. +2. **Silent pin override (Defect B1, yarn/pnpm only)**: create a manifest pinning `@dotcms/client`, `@dotcms/react`, `@dotcms/uve`, `@dotcms/types` all at an explicit version (e.g. `1.2.0`); `yarn install` produces a second, nested copy of `@dotcms/client` at a different (floating-resolved) version under `node_modules/@dotcms/react/node_modules/`, and `pnpm install` symlinks `@dotcms/react`'s dependency to the floating-resolved version instead of the pin. `npm install` is unaffected (dedupes correctly). +3. **Orphaned/floating example scaffold (Defect B2/B3)**: `npx create-next-app my-app --example https://github.com/dotCMS/core/tree/main/examples/nextjs`, then `npm ls @dotcms/client` inside the scaffolded app — installs whatever `next` currently resolves to, an internal dev/QA pre-release never meant for customers. Repeating this against `examples/nextjs` on `release-25.07.10_lts_v12` or `_v16` installs `"latest"`, which resolves to the current SDK release (months ahead of that LTS server), and the scaffolded page fails at runtime because its hardcoded GraphQL fragment requests fields (`numberContents`, `styleEditorSchemas`, `lockedBy`, `lockedByName`, layout `metadata`) that don't exist on the older LTS schema. + +**Expected Behavior**: An installed `@dotcms/*` package reports the exact version it was published as, matching registry metadata and dependency pins byte-for-byte. A customer's explicit version pin is honored identically on npm, yarn, and pnpm. A scaffolded example app installs a published, supported release compatible with the branch/server it came from — `main`'s examples install a current stable release, and each LTS branch's examples install a version compatible with that LTS server. + +**Actual Behavior**: The tarball-internal version and inter-package dependency ranges contain an invalid, non-existent semver string; a customer's explicit pin is silently overridden on yarn and pnpm; scaffolded examples install an internal pre-release tag (`main`) or a version far too new for the server (LTS branches), producing runtime GraphQL schema-mismatch errors with no way to trim the query as a workaround. + +**Reproducibility**: Always, for every package currently published under the new release mechanism (confirmed live as of 2026-09-04: `@dotcms/client` `latest` = `26.9.3-1`, `next` = `26.9.3-1-next.2632`) and for every example app on `main` and on both active LTS branches, as verified directly against the registry and against `origin/main`/`origin/release-25.07.10_lts_v12`/`origin/release-25.07.10_lts_v16` in this repo. + +## Scope of Investigation *(mandatory)* + +- **Affected area**: SDK release/packaging pipeline (npm publish automation) and the example apps used for customer onboarding/scaffolding. Not the SDK's runtime application logic (data fetching, rendering, page building), except for one downstream verification noted below. +- **Suspected surface**: Entirely CI/CD + repo configuration, not `com.dotcms.*`/`com.dotmarketing.*` Java code: + - `.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml` — the composite action that writes the release version into every SDK package's `package.json` and publishes to npm; used by both `cicd_release-sdk.yml` (real releases, `latest` tag) and `cicd_3-trunk.yml` (`next` tag on SDK-touching merges to `main`). + - **All seven** `core-web/libs/sdk/*/package.json` files hardcode a floating `"@dotcms/*": "latest"` specifier in source, confirmed by scripted inspection of every SDK lib manifest (not just the three originally suspected): `analytics`, `angular`, `client`, `experiments`, `react`, `uve`, `vue`. It appears in `dependencies` (`react`, `angular`, `vue`, `analytics`), `peerDependencies` (`experiments`), and `devDependencies` (`@dotcms/types`, in all seven) — so the fix and the CI guardrail must cover all three fields, not just `dependencies`/`peerDependencies`. None of these fields are consulted by local development or by `pnpm install` at the `core-web` root: there is no `pnpm-workspace.yaml` and no `workspaces` field in `core-web/package.json`, so these lib manifests are not real pnpm workspace members — they are inert publish-artifact templates until `deploy-javascript-sdk/action.yml`'s `jq` step rewrites them, right before `npm publish`. + - `core-web/bump-sdk-versions.js` — confirmed dead code (no workflow references it) that only ever rewrote `peerDependencies`, never `dependencies`. + - `examples/nextjs/package.json`, `examples/vuejs/package.json` (pin `"next"`), `examples/angular/package.json`, `examples/astro/package.json` (pin `"latest"`) on `main`. + - `examples/nextjs/package.json` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` (pins `"latest"`). + - No existing CI check guards against a floating `@dotcms/*` specifier reappearing in any of the above. +- **Related known decisions**: **ADR-0019 (SDK/CMS date-lockstep versioning, accepted 2026-06-23)** was read in full during this investigation (not just referenced) and is directly on point: + - It states the exact rule Defect A fails to implement: *"dotCMS release `2026.06.24` → SDK version `2026.6.24` (leading zeros dropped so the string is a valid, ordered npm SemVer triple)."* Defect A is therefore non-compliance with an already-accepted decision, not a new design problem. + - Its Implementation Notes list says `examples/*` should pin an exact date version instead of `latest`. **This spec deliberately diverges from that literal note for `main`'s four examples only**, resolving an internal tension in the ADR itself: the ADR's own "How this plays with Evergreen" section states that for Evergreen customers (always on the latest dotCMS release, upgraded ~every 2 weeks), *"'install the latest SDK' and 'install the SDK matching my CMS' converge to the same thing... they simply track the latest SDK, which by construction matches their always-latest CMS."* An exact pin set once at implementation time would drift stale within weeks relative to Evergreen's cadence, while `"latest"` never does — so for `main`'s Evergreen-facing examples, floating is the choice that actually matches the ADR's stated goal of self-evident compatibility with zero synchronization hacks, even though it departs from the ADR's literal implementation note. This divergence is scoped narrowly to `main`'s four examples; the LTS-branch example copies (which exist to demonstrate against a fixed, non-Evergreen server) follow the ADR's literal exact-pin note without exception — see Fix Scope. + - It also calls for retiring/disabling the standalone SDK publish workflow (the `next`-dist-tag job in `cicd_3-trunk.yml`) and a redesigned runtime compatibility check against `/api/v1/appconfiguration`. **Both are explicitly out of scope for this fix** — see Non-Goals. + The plan phase must consult `dotCMS/platform-adrs` for ADR-0019 (and any related ADR) before finalizing the fix, and must record this Evergreen-based divergence explicitly in its ADR Alignment section, since the constitution requires any conflict with an accepted ADR to be resolved or justified rather than silently applied. + +## Root-Cause Hypothesis + +**Defect A**: The release pipeline's version string (e.g. `26.08.03-01`, zero-padded per the `yy.mm.dd-##` CalVer format) is written verbatim into every SDK package's `version` field and into inter-package `@dotcms/*` dependency/`peerDependencies` ranges via `jq`, with no semver normalization step. npm normalizes leading zeros when it records registry *metadata*, but not the tarball-packed `package.json` contents nor the dependency range strings — producing the observed mismatch. Confirmed by direct code inspection of `deploy-javascript-sdk/action.yml`'s "Update package.json versions" step. **This is not a novel problem to design a solution for**: ADR-0019 (accepted 2026-06-23, before this pipeline shipped) already prescribes dropping leading zeros for exactly this string; the pipeline simply never implemented that rule. + +**Defect B1**: `deploy-javascript-sdk/action.yml`'s rewrite loop only iterates `dependencies`/`peerDependencies` — it does **not** touch `devDependencies`. So today's behavior splits in two: + - For `dependencies`/`peerDependencies`, the rewrite masks the problem before every current release — current publishes correctly carry an exact (if malformed by Defect A) version, not literal `"latest"`. This is still real residual risk for the future (any publish path that bypasses this action step reintroduces literal `"latest"`, exactly as shipped permanently in `@dotcms/react`/`@dotcms/angular` `1.0.6`–`1.7.0`, published before this rewrite existed). + - For `devDependencies`, there is **no masking at all** — confirmed live: `@dotcms/react@26.9.3-1` (today's published `latest`) carries `"devDependencies": {"@dotcms/types": "latest"}` verbatim, in every currently-published package, right now. This doesn't cascade into what a downstream consumer's own `npm install` resolves (devDependencies aren't installed transitively), but it is visible, incorrect metadata sitting in every published tarball today, and the fix must extend the pipeline's rewrite loop to include `devDependencies`, not just add a source-level placeholder. + All seven `core-web/libs/sdk/*/package.json` files hardcode `"@dotcms/*": "latest"` in source (not only `react`/`angular`/`experiments` as originally suspected — confirmed by scripted inspection of all seven, see Scope of Investigation). + +**Defect B1b (dependency type)**: Beyond the wrong *value*, `react`, `angular`, `vue`, and `analytics` declare their sibling runtime packages (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies`. A regular dependency is resolved independently by each package manager, which is the actual mechanism behind the yarn/pnpm silent-override repro in Defect B1's steps: even once the *value* is fixed, a `dependency` entry can still be resolved to a different copy than the one the consumer explicitly pinned. `experiments` already declares its four sibling `@dotcms/*` entries as `peerDependencies` — the correct shape, just with the wrong (`"latest"`) value. `peerDependencies` are what defer to whatever the consuming project has already installed instead of bringing an independent copy, matching how `react-dom` avoids shipping its own `react` — but `peerDependencies` are only auto-installed by npm 7+ and pnpm; **yarn classic (1.x) does not auto-install peer dependencies**, it only warns if one is missing. This is a real, customer-visible behavior change that must be documented (see Fix Scope). + +**Defect B2**: `examples/nextjs` and `examples/vuejs` on `main` point to `"next"`, an internal dev/QA pre-release tag — wrong regardless of server-version considerations, since it isn't even a released version and was never meant for customers. (`examples/angular`/`examples/astro` on `main` already point to `"latest"`, which this fix keeps and extends to the other two — see Fix Scope for why `"latest"` is the right target on `main` specifically.) + +**Defect B3**: The LTS branches' own `examples/nextjs` copies point to `"latest"` instead of a version compatible with that LTS server, and nothing has ever back-filled them with a correct, fixed pin. Unlike `main` — where floating matches the Evergreen audience the example targets — floating is never correct here, since these branch-specific copies exist precisely to demonstrate against a fixed, older server (this is what broke the live customer ticket, Freshdesk #38677). + +**Downstream, out-of-scope-by-design**: The server↔SDK minimum-version compatibility check (`MinSdkVersion.java`, `SdkVersionWebInterceptor.java`, `core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.ts`) sources its own notion of "the SDK's version" (`SDK_VERSION`) from the same `package.json.version` field Defect A corrupts. Its comparison logic (`compareVersions`/`parseVersionSegments`, splitting on `[.-]` and using JS `Number()` per segment) was independently verified to already treat `"26.08.03-01"` and `"26.8.3-1"` as equal (`Number("08")` is `8` in JS string-to-number conversion, not octal). This mechanism therefore has no bug to fix and is automatically corrected once Defect A's normalization lands — it is named here only so the plan phase does not re-open it, and so a regression test can lock in that this remains true. + +## Fix Scope & Non-Goals *(mandatory)* + +**In scope**: + +- Normalize the release-version string to valid semver (strip leading zeros from each numeric segment) at a single point before it is written into any `package.json` field in `deploy-javascript-sdk/action.yml`, and use that one normalized string consistently for: the package's own `version`, every inter-package `@dotcms/*` dependency/`peerDependencies`/**`devDependencies`** pin, and the `npm view`/idempotency check used to detect an already-published version. +- **Move** `@dotcms/client` and `@dotcms/uve` from `dependencies` to `peerDependencies` in `react`, `angular`, `vue`, and `analytics` (wherever each currently declares them) — matching the shape `experiments` already uses. This is the structural fix for the yarn/pnpm duplicate-copy behavior in Defect B1's repro, not just a value change (see Root-Cause, Defect B1b). +- **Value**: every sibling `@dotcms/*` entry in a `peerDependencies` field, across all seven `core-web/libs/sdk/*/package.json` files, uses the sentinel `"0.0.0"` instead of `"latest"` — this covers the newly-moved entries in `react`/`angular`/`vue`/`analytics` and `experiments`' four existing `peerDependencies` entries alike. `"0.0.0"` (not `"workspace:*"` — these libs aren't real pnpm workspace members, no `pnpm-workspace.yaml` exists) is what makes local peer-dependency-satisfaction checks behave sanely during pre-publish testing: `"latest"` is not a valid semver range, so every peer-dependency check against it fails or warns regardless of what's actually installed, which is the real cause of the local-testing breakage this fix addresses. `dependencies` and `devDependencies` fields (e.g. `devDependencies.@dotcms/types`, present in all seven) are **left as `"latest"` in source, unchanged** — they are (or, for `devDependencies`, will be, per the next bullet) masked by the publish-time rewrite, and changing their source value has no customer-facing effect. +- Extend `deploy-javascript-sdk/action.yml`'s rewrite loop to also cover `devDependencies` (today it only rewrites `dependencies`/`peerDependencies`) — this is why every currently-published package still carries a live, unmasked `"latest"` in `devDependencies.@dotcms/types`, confirmed on npm today. The loop must also correctly handle entries that moved from `dependencies` to `peerDependencies` in source. +- Document, in each affected SDK lib's README, that a consumer on **yarn classic (1.x)** must explicitly install `@dotcms/client`/`@dotcms/uve` alongside `@dotcms/react`/`@dotcms/angular`/`@dotcms/vue` — yarn 1.x does not auto-install peer dependencies the way npm 7+ and pnpm do. +- Retire `core-web/bump-sdk-versions.js` (dead code, superseded by `deploy-javascript-sdk/action.yml`'s rewrite logic). +- Change `examples/nextjs` and `examples/vuejs` on `main` from `"next"` to `"latest"`, matching `examples/angular`/`examples/astro` (already `"latest"`). All four `main` examples deliberately track the floating `"latest"` npm dist-tag going forward — not an exact pin. Rationale: `main`'s examples target dotCMS Evergreen customers, who are always on the current release; `"latest"` converges with "the version matching my CMS" for that audience by construction (see Scope of Investigation's Evergreen note), and never goes stale the way a one-time exact pin would. +- Add a short, prominent note to each of `main`'s four example READMEs: a customer whose dotCMS instance is **not** on Evergreen/the current release (self-hosted on an older version, or LTS) must manually replace the `@dotcms/*` version in that example's `package.json` with the version matching their own dotCMS instance, and a one-line pointer to how to find that version. This is the documented, customer-side mitigation for the version-skew case `"latest"` doesn't handle on `main`. +- Fix `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` to an exact, LTS-compatible pinned version (never a floating tag) — these copies exist specifically to demonstrate against that LTS server, not Evergreen, so `"latest"` is wrong here regardless of the `main` rationale above. This is what resolves the live customer ticket (Freshdesk #38677). +- Add a CI check that fails the build if: + - any `core-web/libs/sdk/*/package.json` declares a floating specifier (`latest`, `next`, `*`) for an `@dotcms/*` entry in `peerDependencies` — must be real semver (the `"0.0.0"` sentinel today); `"latest"` in `dependencies`/`devDependencies` of these same files is **not** flagged (intentional, masked at publish — see In Scope); + - `@dotcms/client` or `@dotcms/uve` reappears in the `dependencies` field of `react`, `angular`, `vue`, or `analytics` (regression guard on the B1b structural fix — they belong in `peerDependencies`); + - any `examples/*/package.json` declares `"next"`/`"*"` for an `@dotcms/*` dependency on **any** branch, or declares `"latest"` on any **non-`main`** branch (a `release-*`/LTS branch, where an exact pin is required). `"latest"` in `examples/*/package.json` on `main` is intentional and must NOT be flagged. + This check only fails the build — it does not auto-fix or auto-commit. +- Add one regression test asserting `compareVersions("26.08.03-01", "26.8.3-1")` (or the equivalent post-fix normalized/unnormalized pair) is treated as equal, to protect the already-correct `sdk-compatibility.ts` behavior against future refactors. + +**Explicitly out of scope / non-goals**: + +- No code change to `core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.ts`, `fetch-http-client.ts`, or `rollup.config.cjs` — only the one regression test noted above. +- No change to `MinSdkVersion.java` or `SdkVersionWebInterceptor.java` (Java backend) or any other `com.dotcms.*`/`com.dotmarketing.*` code. +- No change to Angular/React UI components. +- **Making `main`'s example apps work against an old/non-Evergreen dotCMS server via code/CI is explicitly out of scope.** `main`'s four examples deliberately track `"latest"` for the Evergreen audience they target (see Fix Scope). A customer on an older/LTS server who scaffolds `main`'s example and hits a version-skew failure is expected to consult the documented note (see Fix Scope) and manually pin the version matching their own instance — this is a deliberate, documented customer-side responsibility, not something this fix can solve with a static pin (a pin set once would itself go stale relative to Evergreen, recreating the same class of problem). A customer on an old/LTS server who wants a working example **without** manual adjustment should scaffold from their own server's release/LTS branch instead — which is exactly why the LTS branches carry their own dedicated, separately-pinned example copies (see In Scope). +- **Full adoption of ADR-0019 is out of scope for this fix**, beyond the specific points above. ADR-0019 additionally calls for: retiring/disabling the standalone SDK publish workflow (`cicd_3-trunk.yml`'s `publish-sdk-next` job, the `next` npm dist-tag) and folding SDK publishing entirely into the dotCMS release pipeline; bidirectional coupling (an SDK change triggers a new dotCMS release); and a redesigned runtime compatibility check reading `/api/v1/appconfiguration` (replacing the current `MinSdkVersion.java`/`SdkVersionWebInterceptor.java`/`sdk-compatibility.ts` header-based mechanism). None of that is undertaken here — it is a separate, larger migration. Specifically on the `next` tag: the team has already decided, independently of this issue, to keep it active because internal developers need it for testing — ADR-0019 itself anticipates and allows exactly this ("keep a non-version-publishing subset of the workflow for them"), so this is not a new, unjustified deviation. +- No automated, ongoing mechanism to keep the **LTS branches'** example pin in sync with newer compatible releases (e.g. a pipeline step that auto-commits a newer LTS-compatible version over time). The developer explicitly chose the simpler CI-guardrail-only approach over auto-commit, given the risk of unattended commits to protected/LTS branches. Keeping the LTS pin current is a manual, as-needed responsibility; the CI check only guards against it regressing to a *floating* specifier, not against it becoming *stale but still pinned*. (This staleness concern does not apply to `main`'s four examples — they intentionally use the floating `"latest"` tag precisely so they never go stale.) +- No retroactive fix of already-published `1.x` npm packages (npm immutability makes this impossible); support-docs guidance for customers stuck on those versions is tracked separately per the original issue's own note (`overrides`/`resolutions`/`pnpm-workspace.yaml` guidance), not as engineering work here. +- No change to the `next` dist-tag publishing mechanism itself (`cicd_3-trunk.yml`'s `publish-sdk-next` job) — it was already restored by a prior, unrelated PR (#36722) and is confirmed live/current as of this writing; only the example apps' *pinning* of `next` is in scope. + +## Regression Risk *(mandatory)* + +- **Blast radius**: `deploy-javascript-sdk/action.yml` is the single publish path for **every** future dotCMS release's SDK packages (both `latest` real releases and `next` dev/QA builds off `main`). A defect introduced in the normalization step would affect every subsequent SDK publish, not just this fix's target versions. This is the highest-risk piece of the change and needs a dry-run/manual-dispatch verification (the workflow already supports `dry-run: true`) before trusting it against a real release. +- **Backward compatibility**: Already-published `1.x`/pre-fix versions cannot be changed (npm immutability) — this fix is forward-only. The normalized version string must match ADR-0019's literal rule (leading zeros dropped, e.g. `26.8.3-1`) exactly, not just its general date-lockstep intent, so existing tooling that parses the release tag format continues to work; the plan phase must confirm this against the ADR text directly. Separately, moving `@dotcms/client`/`@dotcms/uve` from `dependencies` to `peerDependencies` (Defect B1b) is a genuine behavior change for any customer on **yarn classic (1.x)** who today installs only `@dotcms/react`/`@dotcms/angular`/`@dotcms/vue` and relies on the sibling package coming along automatically — after this fix, yarn 1.x will not auto-install that peer, and the customer's app breaks with a missing-module error unless they explicitly add it. This must be called out prominently in release notes/READMEs, not just silently shipped. +- **Data considerations**: None (no persisted application data; this is packaging/config only). + +## Acceptance & Verification *(mandatory)* + +- **AC-001**: For a newly published `@dotcms/*` package, the tarball-internal `package.json` `version` field is valid semver and matches the npm registry metadata `version` exactly (no leading zeros, no divergence). +- **AC-002**: Injected `@dotcms/*` inter-package dependency/`peerDependencies` pins use that same exact, normalized version string; a strict (non-loose) semver parse of every published `@dotcms/*` version and internal dependency range succeeds. +- **AC-003**: In each of the seven `core-web/libs/sdk/*/package.json` files (`analytics`, `angular`, `client`, `experiments`, `react`, `uve`, `vue`), every sibling `@dotcms/*` entry in `peerDependencies` uses the `"0.0.0"` sentinel, never `latest`/`next`/`*`. `dependencies`/`devDependencies` entries are unaffected by this AC (they intentionally keep `"latest"` in source — see AC-002's masking and Fix Scope). +- **AC-003b**: `@dotcms/client` and `@dotcms/uve` are declared in `peerDependencies`, not `dependencies`, in `react`, `angular`, `vue`, and `analytics`; installing a pinned version of any of these four under yarn or pnpm no longer produces a second, independently-resolved copy of `@dotcms/client`/`@dotcms/uve` nested in that package's own `node_modules`. +- **AC-004**: `examples/nextjs` and `examples/vuejs` on `main` point to `"latest"` (no longer `"next"`), matching `examples/angular`/`examples/astro`; scaffolding each, installing, and running `npm ls @dotcms/client` yields exactly one copy, resolved from the `latest` npm dist-tag (never `next`, never a stale pin). Each example's README carries the documented note (see Fix Scope) for non-Evergreen customers. +- **AC-005**: `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` pins a version verified compatible with that LTS server's GraphQL schema; scaffolding and rendering a page against a 25.07.10 LTS server produces no `FieldUndefined` errors. +- **AC-006**: Installing a pinned `@dotcms/react` under npm and pnpm each yields exactly one `@dotcms/client` at the pinned version (no silent override). Under yarn classic (1.x), installing `@dotcms/react` **alone** (without also explicitly installing `@dotcms/client`) is expected to warn about an unmet peer dependency rather than silently install a mismatched copy — this is the documented behavior change (see AC-003b, Fix Scope), verified against the README note added for it. +- **AC-007**: A CI run fails the build if: any `core-web/libs/sdk/*/package.json` declares `latest`/`next`/`*` for an `@dotcms/*` entry in `peerDependencies`; `@dotcms/client` or `@dotcms/uve` reappears in the `dependencies` field of `react`/`angular`/`vue`/`analytics`; any `examples/*/package.json` on any branch declares `next` or `*`; or any `examples/*/package.json` declares `latest` on a non-`main` branch. A CI run must NOT fail for `latest` in `dependencies`/`devDependencies` of the SDK libs, nor for `latest` in `examples/*/package.json` on `main` — both intentional (AC-003, AC-004). +- **AC-008**: `compareVersions()` in `sdk-compatibility.ts` continues to treat a zero-padded and a normalized form of the same version as equal (regression test, not new behavior). +- **Verification method**: A dry-run (`workflow_dispatch` with `dry-run: true`) of the updated `cicd_release-sdk.yml`/`deploy-javascript-sdk` action against a scratch/test scope to confirm the written `package.json` contents before any real publish is risked; a new Jest unit test for the version-normalization logic and for `compareVersions()` (AC-008); manual scaffold-and-install verification per AC-003b/AC-004/AC-005/AC-006 across npm/yarn/pnpm; a new CI job/script test for AC-007 (e.g. run the guardrail check against fixture `package.json` files that intentionally contain a floating `peerDependencies` value or a reintroduced `dependencies` entry, and confirm each fails). + +## Assumptions + +- The LTS-compatible pin for `release-25.07.10_lts_v12`/`_v16`'s `examples/nextjs` is assumed to be `1.2.0` (the newest release the original issue's author identified as validating against that LTS server's GraphQL schema). **Resolution (developer decision)**: this has not been independently re-verified against a live 25.07.10 LTS server in this investigation, and will not be taken as given — `/speckit-plan`/`/speckit-implement` MUST confirm `1.2.0` (or identify the correct version) by actually scaffolding `examples/nextjs` and rendering a page against a real 25.07.10 LTS server before it is committed as the LTS example's pin. This confirmation step is now folded into AC-005's verification method, not left as an open question blocking this spec. +- `main`'s four examples deliberately keep using the floating `"latest"` npm dist-tag (not an exact pin) going forward — confirmed with the developer as an intentional decision, grounded in ADR-0019's own Evergreen-convergence reasoning (see Scope of Investigation): a customer scaffolding from `main` is expected to be on dotCMS Evergreen (always the current release), for whom `"latest"` and "the version matching my CMS" are the same thing by construction. A customer on an older/non-Evergreen server is documented (Fix Scope) as needing to manually pin the version matching their own instance. This is the one deliberate exception to "no floating `@dotcms/*` specifiers" elsewhere in this spec, and the CI guardrail (AC-007) must not flag it. +- No separate GitHub issue/spec is being opened for the "keep the LTS example pin in sync with newer compatible releases over time" concern; it is accepted as a manual, as-needed process step, guarded only against the floating-specifier regression (see Non-Goals). This does not apply to `main`'s examples, which cannot go stale since they float by design. +- Keeping the standalone `next`-tag publish workflow (`cicd_3-trunk.yml`) active, rather than retiring/disabling it as ADR-0019's Implementation Notes suggest, is a decision the team already made independently of this issue — internal developers need it for testing. This spec does not revisit that decision; it is explicitly out of scope (see Non-Goals). +- The `"0.0.0"` sentinel is deliberately scoped to `peerDependencies` only, not `dependencies`/`devDependencies` — confirmed with the developer. Rationale: customers never build from this repo's source, so a `dependencies`/`devDependencies` value that's already masked by the publish-time rewrite has no customer-facing effect, and changing it buys nothing. `peerDependencies` is different because it's where dotCMS's own developers hit real, local pre-publish testing breakage (an npm/yarn/pnpm peer-dependency-satisfaction check can't evaluate `"latest"` as a semver range), so only that field's source value needs to be a real semver. From a7a2ee50fceb26dbc0d63c39c2e72b5385a6b50a Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 00:29:45 -0500 Subject: [PATCH 02/15] Address PR review: name the exact READMEs and add @dotcms/types to the doc fix rjvelazco's review on PR #37452 pointed out that react/README.md, angular/README.md, and vue/README.md currently claim installing the main package "will automatically install the required dependencies" -- a claim this fix's peerDependencies change makes unreliable, and one that was already inaccurate for @dotcms/types (a devDependency, never auto-installed even before this fix, but never called out in the docs either). Names the exact files/sections to correct, expands the required manual install list to all three affected packages (@dotcms/client, @dotcms/uve, @dotcms/types), and broadens "yarn classic" to also cover npm below v7, which has the same no-auto-install-peers behavior. Adds AC-009 to make this independently verifiable. Co-Authored-By: Claude Sonnet 5 --- specs/37399-sdk-packaging-version-fix/spec.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md index f7c0a7411276..0c3557cae910 100644 --- a/specs/37399-sdk-packaging-version-fix/spec.md +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -70,7 +70,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - For `devDependencies`, there is **no masking at all** — confirmed live: `@dotcms/react@26.9.3-1` (today's published `latest`) carries `"devDependencies": {"@dotcms/types": "latest"}` verbatim, in every currently-published package, right now. This doesn't cascade into what a downstream consumer's own `npm install` resolves (devDependencies aren't installed transitively), but it is visible, incorrect metadata sitting in every published tarball today, and the fix must extend the pipeline's rewrite loop to include `devDependencies`, not just add a source-level placeholder. All seven `core-web/libs/sdk/*/package.json` files hardcode `"@dotcms/*": "latest"` in source (not only `react`/`angular`/`experiments` as originally suspected — confirmed by scripted inspection of all seven, see Scope of Investigation). -**Defect B1b (dependency type)**: Beyond the wrong *value*, `react`, `angular`, `vue`, and `analytics` declare their sibling runtime packages (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies`. A regular dependency is resolved independently by each package manager, which is the actual mechanism behind the yarn/pnpm silent-override repro in Defect B1's steps: even once the *value* is fixed, a `dependency` entry can still be resolved to a different copy than the one the consumer explicitly pinned. `experiments` already declares its four sibling `@dotcms/*` entries as `peerDependencies` — the correct shape, just with the wrong (`"latest"`) value. `peerDependencies` are what defer to whatever the consuming project has already installed instead of bringing an independent copy, matching how `react-dom` avoids shipping its own `react` — but `peerDependencies` are only auto-installed by npm 7+ and pnpm; **yarn classic (1.x) does not auto-install peer dependencies**, it only warns if one is missing. This is a real, customer-visible behavior change that must be documented (see Fix Scope). +**Defect B1b (dependency type)**: Beyond the wrong *value*, `react`, `angular`, `vue`, and `analytics` declare their sibling runtime packages (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies`. A regular dependency is resolved independently by each package manager, which is the actual mechanism behind the yarn/pnpm silent-override repro in Defect B1's steps: even once the *value* is fixed, a `dependency` entry can still be resolved to a different copy than the one the consumer explicitly pinned. `experiments` already declares its four sibling `@dotcms/*` entries as `peerDependencies` — the correct shape, just with the wrong (`"latest"`) value. `peerDependencies` are what defer to whatever the consuming project has already installed instead of bringing an independent copy, matching how `react-dom` avoids shipping its own `react` — but `peerDependencies` are only auto-installed by npm 7+ and pnpm; **yarn classic (1.x) and npm below v7 do not auto-install peer dependencies**, they only warn if one is missing. This is a real, customer-visible behavior change that must be documented (see Fix Scope) — confirmed via reviewer feedback on this spec's PR (see PR #37452 review comment) that the affected SDKs' READMEs currently promise automatic installation and must be corrected, and that `@dotcms/types` (already a `devDependency`, never auto-installed even before this fix) belongs in the same corrected instructions. **Defect B2**: `examples/nextjs` and `examples/vuejs` on `main` point to `"next"`, an internal dev/QA pre-release tag — wrong regardless of server-version considerations, since it isn't even a released version and was never meant for customers. (`examples/angular`/`examples/astro` on `main` already point to `"latest"`, which this fix keeps and extends to the other two — see Fix Scope for why `"latest"` is the right target on `main` specifically.) @@ -86,7 +86,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - **Move** `@dotcms/client` and `@dotcms/uve` from `dependencies` to `peerDependencies` in `react`, `angular`, `vue`, and `analytics` (wherever each currently declares them) — matching the shape `experiments` already uses. This is the structural fix for the yarn/pnpm duplicate-copy behavior in Defect B1's repro, not just a value change (see Root-Cause, Defect B1b). - **Value**: every sibling `@dotcms/*` entry in a `peerDependencies` field, across all seven `core-web/libs/sdk/*/package.json` files, uses the sentinel `"0.0.0"` instead of `"latest"` — this covers the newly-moved entries in `react`/`angular`/`vue`/`analytics` and `experiments`' four existing `peerDependencies` entries alike. `"0.0.0"` (not `"workspace:*"` — these libs aren't real pnpm workspace members, no `pnpm-workspace.yaml` exists) is what makes local peer-dependency-satisfaction checks behave sanely during pre-publish testing: `"latest"` is not a valid semver range, so every peer-dependency check against it fails or warns regardless of what's actually installed, which is the real cause of the local-testing breakage this fix addresses. `dependencies` and `devDependencies` fields (e.g. `devDependencies.@dotcms/types`, present in all seven) are **left as `"latest"` in source, unchanged** — they are (or, for `devDependencies`, will be, per the next bullet) masked by the publish-time rewrite, and changing their source value has no customer-facing effect. - Extend `deploy-javascript-sdk/action.yml`'s rewrite loop to also cover `devDependencies` (today it only rewrites `dependencies`/`peerDependencies`) — this is why every currently-published package still carries a live, unmasked `"latest"` in `devDependencies.@dotcms/types`, confirmed on npm today. The loop must also correctly handle entries that moved from `dependencies` to `peerDependencies` in source. -- Document, in each affected SDK lib's README, that a consumer on **yarn classic (1.x)** must explicitly install `@dotcms/client`/`@dotcms/uve` alongside `@dotcms/react`/`@dotcms/angular`/`@dotcms/vue` — yarn 1.x does not auto-install peer dependencies the way npm 7+ and pnpm do. +- Update the "Installation" section of `core-web/libs/sdk/react/README.md`, `core-web/libs/sdk/angular/README.md`, and `core-web/libs/sdk/vue/README.md` (each currently states installing the main package "will automatically install the required dependencies," listing `@dotcms/uve`/`@dotcms/client` — confirmed by direct inspection, e.g. `react/README.md` lines ~101-109). This claim is no longer reliably true and must be corrected to explicitly instruct the consumer to install `@dotcms/client`, `@dotcms/uve`, **and `@dotcms/types`** alongside the main package for full UVE support. (`@dotcms/types` was never auto-installed even before this fix — it has always been a `devDependency` — but the README never said so; fixed here since the same section is being corrected anyway.) Explicitly name the affected consumers: **yarn classic (1.x)** and **npm below v7** do not auto-install peer dependencies at all (only warn if missing); npm 7+ and pnpm do auto-install them but a customer relying on the old "it just works" claim should not be surprised either way. - Retire `core-web/bump-sdk-versions.js` (dead code, superseded by `deploy-javascript-sdk/action.yml`'s rewrite logic). - Change `examples/nextjs` and `examples/vuejs` on `main` from `"next"` to `"latest"`, matching `examples/angular`/`examples/astro` (already `"latest"`). All four `main` examples deliberately track the floating `"latest"` npm dist-tag going forward — not an exact pin. Rationale: `main`'s examples target dotCMS Evergreen customers, who are always on the current release; `"latest"` converges with "the version matching my CMS" for that audience by construction (see Scope of Investigation's Evergreen note), and never goes stale the way a one-time exact pin would. - Add a short, prominent note to each of `main`'s four example READMEs: a customer whose dotCMS instance is **not** on Evergreen/the current release (self-hosted on an older version, or LTS) must manually replace the `@dotcms/*` version in that example's `package.json` with the version matching their own dotCMS instance, and a one-line pointer to how to find that version. This is the documented, customer-side mitigation for the version-skew case `"latest"` doesn't handle on `main`. @@ -112,7 +112,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) ## Regression Risk *(mandatory)* - **Blast radius**: `deploy-javascript-sdk/action.yml` is the single publish path for **every** future dotCMS release's SDK packages (both `latest` real releases and `next` dev/QA builds off `main`). A defect introduced in the normalization step would affect every subsequent SDK publish, not just this fix's target versions. This is the highest-risk piece of the change and needs a dry-run/manual-dispatch verification (the workflow already supports `dry-run: true`) before trusting it against a real release. -- **Backward compatibility**: Already-published `1.x`/pre-fix versions cannot be changed (npm immutability) — this fix is forward-only. The normalized version string must match ADR-0019's literal rule (leading zeros dropped, e.g. `26.8.3-1`) exactly, not just its general date-lockstep intent, so existing tooling that parses the release tag format continues to work; the plan phase must confirm this against the ADR text directly. Separately, moving `@dotcms/client`/`@dotcms/uve` from `dependencies` to `peerDependencies` (Defect B1b) is a genuine behavior change for any customer on **yarn classic (1.x)** who today installs only `@dotcms/react`/`@dotcms/angular`/`@dotcms/vue` and relies on the sibling package coming along automatically — after this fix, yarn 1.x will not auto-install that peer, and the customer's app breaks with a missing-module error unless they explicitly add it. This must be called out prominently in release notes/READMEs, not just silently shipped. +- **Backward compatibility**: Already-published `1.x`/pre-fix versions cannot be changed (npm immutability) — this fix is forward-only. The normalized version string must match ADR-0019's literal rule (leading zeros dropped, e.g. `26.8.3-1`) exactly, not just its general date-lockstep intent, so existing tooling that parses the release tag format continues to work; the plan phase must confirm this against the ADR text directly. Separately, moving `@dotcms/client`/`@dotcms/uve` from `dependencies` to `peerDependencies` (Defect B1b) is a genuine behavior change for any customer on **yarn classic (1.x) or npm below v7** who today installs only `@dotcms/react`/`@dotcms/angular`/`@dotcms/vue` and relies on the sibling package coming along automatically — after this fix, those tools will not auto-install that peer, and the customer's app breaks with a missing-module error unless they explicitly add it. This must be called out prominently in release notes and in each SDK's README (see Fix Scope), not just silently shipped. - **Data considerations**: None (no persisted application data; this is packaging/config only). ## Acceptance & Verification *(mandatory)* @@ -123,10 +123,11 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - **AC-003b**: `@dotcms/client` and `@dotcms/uve` are declared in `peerDependencies`, not `dependencies`, in `react`, `angular`, `vue`, and `analytics`; installing a pinned version of any of these four under yarn or pnpm no longer produces a second, independently-resolved copy of `@dotcms/client`/`@dotcms/uve` nested in that package's own `node_modules`. - **AC-004**: `examples/nextjs` and `examples/vuejs` on `main` point to `"latest"` (no longer `"next"`), matching `examples/angular`/`examples/astro`; scaffolding each, installing, and running `npm ls @dotcms/client` yields exactly one copy, resolved from the `latest` npm dist-tag (never `next`, never a stale pin). Each example's README carries the documented note (see Fix Scope) for non-Evergreen customers. - **AC-005**: `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` pins a version verified compatible with that LTS server's GraphQL schema; scaffolding and rendering a page against a 25.07.10 LTS server produces no `FieldUndefined` errors. -- **AC-006**: Installing a pinned `@dotcms/react` under npm and pnpm each yields exactly one `@dotcms/client` at the pinned version (no silent override). Under yarn classic (1.x), installing `@dotcms/react` **alone** (without also explicitly installing `@dotcms/client`) is expected to warn about an unmet peer dependency rather than silently install a mismatched copy — this is the documented behavior change (see AC-003b, Fix Scope), verified against the README note added for it. +- **AC-006**: Installing a pinned `@dotcms/react` under npm 7+ and pnpm each yields exactly one `@dotcms/client` at the pinned version (no silent override). Under yarn classic (1.x) or npm below v7, installing `@dotcms/react` **alone** (without also explicitly installing `@dotcms/client`) is expected to warn about an unmet peer dependency (or fail with a missing-module error at runtime) rather than silently install a mismatched copy — this is the documented behavior change (see AC-003b, Fix Scope). +- **AC-009**: `react/README.md`, `angular/README.md`, and `vue/README.md`'s Installation sections no longer claim that installing the main package automatically installs its required dependencies; each explicitly instructs the consumer to also install `@dotcms/client`, `@dotcms/uve`, and `@dotcms/types`, and names yarn classic (1.x) and npm below v7 as the package managers that require this manual step. - **AC-007**: A CI run fails the build if: any `core-web/libs/sdk/*/package.json` declares `latest`/`next`/`*` for an `@dotcms/*` entry in `peerDependencies`; `@dotcms/client` or `@dotcms/uve` reappears in the `dependencies` field of `react`/`angular`/`vue`/`analytics`; any `examples/*/package.json` on any branch declares `next` or `*`; or any `examples/*/package.json` declares `latest` on a non-`main` branch. A CI run must NOT fail for `latest` in `dependencies`/`devDependencies` of the SDK libs, nor for `latest` in `examples/*/package.json` on `main` — both intentional (AC-003, AC-004). - **AC-008**: `compareVersions()` in `sdk-compatibility.ts` continues to treat a zero-padded and a normalized form of the same version as equal (regression test, not new behavior). -- **Verification method**: A dry-run (`workflow_dispatch` with `dry-run: true`) of the updated `cicd_release-sdk.yml`/`deploy-javascript-sdk` action against a scratch/test scope to confirm the written `package.json` contents before any real publish is risked; a new Jest unit test for the version-normalization logic and for `compareVersions()` (AC-008); manual scaffold-and-install verification per AC-003b/AC-004/AC-005/AC-006 across npm/yarn/pnpm; a new CI job/script test for AC-007 (e.g. run the guardrail check against fixture `package.json` files that intentionally contain a floating `peerDependencies` value or a reintroduced `dependencies` entry, and confirm each fails). +- **Verification method**: A dry-run (`workflow_dispatch` with `dry-run: true`) of the updated `cicd_release-sdk.yml`/`deploy-javascript-sdk` action against a scratch/test scope to confirm the written `package.json` contents before any real publish is risked; a new Jest unit test for the version-normalization logic and for `compareVersions()` (AC-008); manual scaffold-and-install verification per AC-003b/AC-004/AC-005/AC-006 across npm/yarn/pnpm; a new CI job/script test for AC-007 (e.g. run the guardrail check against fixture `package.json` files that intentionally contain a floating `peerDependencies` value or a reintroduced `dependencies` entry, and confirm each fails); a manual read-through of the three corrected READMEs for AC-009. ## Assumptions From 67d3fa8af49ac46489437ff823c407385b6f3237 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 09:56:48 -0500 Subject: [PATCH 03/15] Add SDK package.json shape validator; retire dead bump-sdk-versions.js Setup + Foundational phase (tasks T001-T007) for issue #36891 / spec specs/37399-sdk-packaging-version-fix. - Delete core-web/bump-sdk-versions.js (confirmed unreferenced by any workflow -- superseded by deploy-javascript-sdk/action.yml's rewrite logic, per the spec's Defect B1 root-cause). - Add .github/scripts/validate-sdk-package-shapes, a standalone TS/Jest package (matching the gather-release-data/release-qa-status convention) exporting validateSdkLibPackageJson and validateExamplePackageJson, plus a CLI entry point. Written test-first (10 tests, developer-approved, confirmed Red against the missing module before implementation). Every SDK/B1/B2/B3 task from here on uses this as its automated test. Co-Authored-By: Claude Sonnet 5 --- .../jest.config.js | 7 + .../package-lock.json | 4027 +++++++++++++++++ .../validate-sdk-package-shapes/package.json | 20 + .../validate-sdk-package-shapes/src/index.ts | 68 + .../src/validate.test.ts | 117 + .../src/validate.ts | 85 + .../validate-sdk-package-shapes/tsconfig.json | 19 + core-web/bump-sdk-versions.js | 116 - 8 files changed, 4343 insertions(+), 116 deletions(-) create mode 100644 .github/scripts/validate-sdk-package-shapes/jest.config.js create mode 100644 .github/scripts/validate-sdk-package-shapes/package-lock.json create mode 100644 .github/scripts/validate-sdk-package-shapes/package.json create mode 100644 .github/scripts/validate-sdk-package-shapes/src/index.ts create mode 100644 .github/scripts/validate-sdk-package-shapes/src/validate.test.ts create mode 100644 .github/scripts/validate-sdk-package-shapes/src/validate.ts create mode 100644 .github/scripts/validate-sdk-package-shapes/tsconfig.json delete mode 100644 core-web/bump-sdk-versions.js diff --git a/.github/scripts/validate-sdk-package-shapes/jest.config.js b/.github/scripts/validate-sdk-package-shapes/jest.config.js new file mode 100644 index 000000000000..4dffe23c18a4 --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/jest.config.js @@ -0,0 +1,7 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/src'], + testMatch: ['**/*.test.ts'], +}; diff --git a/.github/scripts/validate-sdk-package-shapes/package-lock.json b/.github/scripts/validate-sdk-package-shapes/package-lock.json new file mode 100644 index 000000000000..52e0d443a556 --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/package-lock.json @@ -0,0 +1,4027 @@ +{ + "name": "@dotcms/validate-sdk-package-shapes", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@dotcms/validate-sdk-package-shapes", + "version": "1.0.0", + "bin": { + "validate-sdk-package-shapes": "dist/index.js" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^22.15.3", + "jest": "^29.7.0", + "ts-jest": "^29.3.4", + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.425", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.425.tgz", + "integrity": "sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "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": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.12", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", + "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.5", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "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 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/.github/scripts/validate-sdk-package-shapes/package.json b/.github/scripts/validate-sdk-package-shapes/package.json new file mode 100644 index 000000000000..52cdbaff8971 --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/package.json @@ -0,0 +1,20 @@ +{ + "name": "@dotcms/validate-sdk-package-shapes", + "version": "1.0.0", + "description": "Fails CI if an @dotcms/* SDK lib or example app package.json regresses to a floating version specifier, or a sibling SDK dependency reappears in the wrong field", + "main": "dist/index.js", + "bin": "dist/index.js", + "scripts": { + "build": "tsc", + "start": "ts-node src/index.ts", + "test": "jest" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^22.15.3", + "jest": "^29.7.0", + "ts-jest": "^29.3.4", + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + } +} diff --git a/.github/scripts/validate-sdk-package-shapes/src/index.ts b/.github/scripts/validate-sdk-package-shapes/src/index.ts new file mode 100644 index 000000000000..f4a6f84eee6d --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/src/index.ts @@ -0,0 +1,68 @@ +#!/usr/bin/env node +import { readFileSync } from 'fs'; +import { basename, dirname } from 'path'; + +import { validateExamplePackageJson, validateSdkLibPackageJson } from './validate'; + +/** + * CLI entry point. Usage: + * + * ts-node src/index.ts [--branch ] + * + * A path under `.../libs/sdk//package.json` is validated as an SDK lib manifest + * (pkgName is inferred from the parent directory name). Any other path is validated as an + * example app manifest, using --branch (default "main") to decide whether a floating "latest" + * is acceptable. + * + * Exits non-zero with one line per violation if any file fails. + */ + +function parseArgs(argv: string[]): { filePaths: string[]; branch: string } { + const branchFlagIndex = argv.indexOf('--branch'); + let branch = 'main'; + let filePaths = argv; + + if (branchFlagIndex !== -1) { + branch = argv[branchFlagIndex + 1] ?? 'main'; + filePaths = [...argv.slice(0, branchFlagIndex), ...argv.slice(branchFlagIndex + 2)]; + } + + return { filePaths, branch }; +} + +function isSdkLibManifest(filePath: string): boolean { + return dirname(filePath).replace(/\\/g, '/').includes('/libs/sdk/'); +} + +function main(): void { + const { filePaths, branch } = parseArgs(process.argv.slice(2)); + + if (filePaths.length === 0) { + console.error('Usage: index.ts [...more paths] [--branch ]'); + process.exit(2); + } + + const allViolations: string[] = []; + + for (const filePath of filePaths) { + const pkg = JSON.parse(readFileSync(filePath, 'utf8')); + + const violations = isSdkLibManifest(filePath) + ? validateSdkLibPackageJson(pkg, basename(dirname(filePath))) + : validateExamplePackageJson(pkg, branch); + + allViolations.push(...violations.map((v) => `${filePath}: ${v}`)); + } + + if (allViolations.length > 0) { + console.error('SDK package.json shape violations found:\n'); + for (const violation of allViolations) { + console.error(` - ${violation}`); + } + process.exit(1); + } + + console.log(`OK — ${filePaths.length} file(s) validated, no violations.`); +} + +main(); diff --git a/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts b/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts new file mode 100644 index 000000000000..a8099b74f7c6 --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts @@ -0,0 +1,117 @@ +import { validateSdkLibPackageJson, validateExamplePackageJson } from './validate'; + +describe('validateSdkLibPackageJson', () => { + it('flags a floating peerDependencies value (latest/next/*)', () => { + const pkg = { + name: '@dotcms/experiments', + peerDependencies: { + '@dotcms/client': 'latest', + '@dotcms/react': 'next', + '@dotcms/uve': '*', + '@dotcms/types': '0.0.0' + } + }; + + const violations = validateSdkLibPackageJson(pkg, 'experiments'); + + expect(violations).toEqual( + expect.arrayContaining([ + expect.stringContaining('@dotcms/client'), + expect.stringContaining('@dotcms/react'), + expect.stringContaining('@dotcms/uve') + ]) + ); + expect(violations.some((v) => v.includes('@dotcms/types'))).toBe(false); + }); + + it('passes when every peerDependencies entry uses the 0.0.0 sentinel', () => { + const pkg = { + name: '@dotcms/experiments', + peerDependencies: { + '@dotcms/client': '0.0.0', + '@dotcms/react': '0.0.0', + '@dotcms/uve': '0.0.0', + '@dotcms/types': '0.0.0' + } + }; + + expect(validateSdkLibPackageJson(pkg, 'experiments')).toEqual([]); + }); + + it('flags @dotcms/client or @dotcms/uve reintroduced in dependencies for react/angular/vue/analytics', () => { + const pkg = { + name: '@dotcms/react', + dependencies: { + '@dotcms/uve': 'latest', + '@dotcms/client': 'latest', + '@tinymce/tinymce-react': '6.2.1' + }, + devDependencies: { '@dotcms/types': 'latest' } + }; + + const violations = validateSdkLibPackageJson(pkg, 'react'); + + expect(violations).toEqual( + expect.arrayContaining([ + expect.stringContaining('@dotcms/uve'), + expect.stringContaining('@dotcms/client') + ]) + ); + }); + + it('does not flag dependencies/devDependencies "latest" for packages other than the sibling-dependency guard (masked at publish)', () => { + const pkg = { + name: '@dotcms/react', + dependencies: { '@tinymce/tinymce-react': '6.2.1' }, + devDependencies: { '@dotcms/types': 'latest' } + }; + + expect(validateSdkLibPackageJson(pkg, 'react')).toEqual([]); + }); + + it('does not apply the dependencies structural guard to packages other than react/angular/vue/analytics', () => { + // client/uve only ever have @dotcms/types in devDependencies — never a dependencies-field concern + const pkg = { name: '@dotcms/client', devDependencies: { '@dotcms/types': 'latest' } }; + + expect(validateSdkLibPackageJson(pkg, 'client')).toEqual([]); + }); +}); + +describe('validateExamplePackageJson', () => { + it('flags "next" on any branch', () => { + const pkg = { dependencies: { '@dotcms/client': 'next', '@dotcms/uve': 'next' } }; + + const violations = validateExamplePackageJson(pkg, 'main'); + + expect(violations.length).toBeGreaterThan(0); + }); + + it('flags "*" on any branch', () => { + const pkg = { dependencies: { '@dotcms/client': '*' } }; + + expect(validateExamplePackageJson(pkg, 'main').length).toBeGreaterThan(0); + }); + + it('flags "latest" on a non-main (LTS/release) branch', () => { + const pkg = { dependencies: { '@dotcms/client': 'latest' } }; + + const violations = validateExamplePackageJson(pkg, 'release-25.07.10_lts_v12'); + + expect(violations.length).toBeGreaterThan(0); + }); + + it('does NOT flag "latest" on main — intentional per ADR-0019 Evergreen alignment', () => { + const pkg = { + dependencies: { '@dotcms/client': 'latest', '@dotcms/uve': 'latest' } + }; + + expect(validateExamplePackageJson(pkg, 'main')).toEqual([]); + }); + + it('passes an exact pinned version on any branch', () => { + const pkg = { dependencies: { '@dotcms/client': '1.2.0' } }; + + expect(validateExamplePackageJson(pkg, 'release-25.07.10_lts_v12')).toEqual([]); + expect(validateExamplePackageJson(pkg, 'main')).toEqual([]); + }); +}); diff --git a/.github/scripts/validate-sdk-package-shapes/src/validate.ts b/.github/scripts/validate-sdk-package-shapes/src/validate.ts new file mode 100644 index 000000000000..fa8a5de5ec13 --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/src/validate.ts @@ -0,0 +1,85 @@ +/** + * Guards the package.json contract described in + * specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md. + */ + +const FLOATING_SPECIFIERS = new Set(['latest', 'next', '*']); + +/** react/angular/vue/analytics must not carry these as regular `dependencies` — see Defect B1b. */ +const SIBLING_DEPENDENCY_GUARD_PACKAGES = new Set(['react', 'angular', 'vue', 'analytics']); +const GUARDED_SIBLING_DEPS = ['@dotcms/client', '@dotcms/uve']; + +type PackageJson = { + name?: string; + dependencies?: Record; + peerDependencies?: Record; + devDependencies?: Record; +}; + +const isDotcmsPackage = (specifier: string): boolean => specifier.startsWith('@dotcms/'); + +/** + * Validates a core-web/libs/sdk//package.json. + * + * - Any `@dotcms/*` entry in `peerDependencies` must not be a floating specifier + * (`latest`/`next`/`*`) — the published value is always rewritten to an exact version at + * publish time, but the source placeholder must be real semver (the `"0.0.0"` sentinel) so + * local peer-dependency-satisfaction checks behave sanely before publish. + * - `@dotcms/client`/`@dotcms/uve` must never be regular `dependencies` of react/angular/vue/ + * analytics — that is the actual mechanism behind the yarn/pnpm duplicate-copy bug (Defect + * B1b), independent of whatever value they hold. + * - `dependencies`/`devDependencies` values are intentionally NOT checked: they are masked by + * the release pipeline's rewrite step before publish, so a floating value there is not a + * customer-facing defect. + */ +export function validateSdkLibPackageJson(pkg: PackageJson, pkgName: string): string[] { + const violations: string[] = []; + + for (const [dep, version] of Object.entries(pkg.peerDependencies ?? {})) { + if (isDotcmsPackage(dep) && FLOATING_SPECIFIERS.has(version)) { + violations.push( + `${pkgName}: peerDependencies["${dep}"] is "${version}" — must be real semver (the "0.0.0" sentinel), never latest/next/*` + ); + } + } + + if (SIBLING_DEPENDENCY_GUARD_PACKAGES.has(pkgName)) { + for (const dep of GUARDED_SIBLING_DEPS) { + if (pkg.dependencies && dep in pkg.dependencies) { + violations.push( + `${pkgName}: dependencies["${dep}"] must not exist — it belongs in peerDependencies (Defect B1b regression guard)` + ); + } + } + } + + return violations; +} + +/** + * Validates an examples//package.json for the given branch. + * + * - `next`/`*` are never valid, on any branch. + * - `latest` is valid ONLY on `main` (the deliberate Evergreen-alignment exception — see + * ADR-0019's "How this plays with Evergreen" note and this spec's Fix Scope). Every other + * branch (LTS release branches) must pin an exact version. + */ +export function validateExamplePackageJson(pkg: PackageJson, branch: string): string[] { + const violations: string[] = []; + + for (const [dep, version] of Object.entries(pkg.dependencies ?? {})) { + if (!isDotcmsPackage(dep)) { + continue; + } + + if (version === 'next' || version === '*') { + violations.push(`examples: dependencies["${dep}"] is "${version}" on branch "${branch}" — never valid, on any branch`); + } else if (version === 'latest' && branch !== 'main') { + violations.push( + `examples: dependencies["${dep}"] is "latest" on branch "${branch}" — only "main" may float; this branch must pin an exact version` + ); + } + } + + return violations; +} diff --git a/.github/scripts/validate-sdk-package-shapes/tsconfig.json b/.github/scripts/validate-sdk-package-shapes/tsconfig.json new file mode 100644 index 000000000000..9424ecd5a57e --- /dev/null +++ b/.github/scripts/validate-sdk-package-shapes/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} diff --git a/core-web/bump-sdk-versions.js b/core-web/bump-sdk-versions.js deleted file mode 100644 index 8493a0c61921..000000000000 --- a/core-web/bump-sdk-versions.js +++ /dev/null @@ -1,116 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -// Function to read and parse JSON -const readJSON = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8')); - -// Function to write JSON to file -const writeJSON = (filePath, data) => - fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n'); - -// Function to bump the version -const bumpVersion = (version) => { - const match = version.match(/^(\d+\.\d+\.\d+)(-alpha\.)(\d+)?$/); - if (!match) { - throw new Error(`Invalid version format: ${version}`); - } - - const [, majorMinorPatch, alphaPrefix, buildNumber = 0] = match; - const newBuildNumber = parseInt(buildNumber) + 1; - return `${majorMinorPatch}${alphaPrefix}${newBuildNumber}`; -}; - -// Function to update the version in a package.json file -const updateVersionInPackageJson = (packageJsonPath, newVersion) => { - const packageJson = readJSON(packageJsonPath); - packageJson.version = newVersion; - writeJSON(packageJsonPath, packageJson); - console.log(`Updated version in ${packageJsonPath} to ${newVersion}`); -}; - -// Function to update peerDependencies in a package.json file -const updatePeerDependencies = (packageJsonPath, newVersion) => { - const packageJson = readJSON(packageJsonPath); - let updated = false; - - if (packageJson.peerDependencies) { - Object.keys(packageJson.peerDependencies).forEach((dep) => { - if (dep.startsWith('@dotcms/')) { - packageJson.peerDependencies[dep] = newVersion; - updated = true; - } - }); - } - - if (updated) { - writeJSON(packageJsonPath, packageJson); - console.log(`Updated peerDependencies in ${packageJsonPath} to version ${newVersion}`); - } -}; - -// Function to update dependencies in a package.json file for examples -const updateDependenciesInExamples = (packageJsonPath, sdkDependencies) => { - const packageJson = readJSON(packageJsonPath); - let updated = false; - - Object.keys(sdkDependencies).forEach((dep) => { - if (packageJson.dependencies && packageJson.dependencies[dep]) { - packageJson.dependencies[dep] = sdkDependencies[dep]; - updated = true; - } - }); - - if (updated) { - writeJSON(packageJsonPath, packageJson); - console.log(`Updated dependencies in ${packageJsonPath} to new SDK versions`); - } -}; - -// Paths -const sdkDir = path.join(__dirname, 'libs/sdk'); -const examplesDir = path.join(__dirname, '../examples'); - -// Step 1: Bump the version of the client library -const clientPackageJsonPath = path.join(sdkDir, 'client/package.json'); -const clientPackageJson = readJSON(clientPackageJsonPath); -const currentVersion = clientPackageJson.version; -const newVersion = bumpVersion(currentVersion); -console.log(`Bumping version of client from ${currentVersion} to ${newVersion}`); - -// Step 2: Update the version in all SDK libraries -const sdkLibraries = fs - .readdirSync(sdkDir) - .filter((lib) => fs.existsSync(path.join(sdkDir, lib, 'package.json'))); -sdkLibraries.forEach((lib) => { - const packageJsonPath = path.join(sdkDir, lib, 'package.json'); - updateVersionInPackageJson(packageJsonPath, newVersion); -}); - -// Step 3: Update peerDependencies in other SDK libraries -sdkLibraries.forEach((lib) => { - if (lib !== 'client') { - const packageJsonPath = path.join(sdkDir, lib, 'package.json'); - updatePeerDependencies(packageJsonPath, newVersion); - } -}); - -// Step 4: Dynamically build the sdkDependencies object -const sdkDependencies = sdkLibraries.reduce((deps, lib) => { - const packageJsonPath = path.join(sdkDir, lib, 'package.json'); - const packageJson = readJSON(packageJsonPath); - if (packageJson.name) { - deps[packageJson.name] = newVersion; - } - return deps; -}, {}); - -// Step 5: Update dependencies in example projects -const exampleProjects = fs - .readdirSync(examplesDir) - .filter((proj) => fs.existsSync(path.join(examplesDir, proj, 'package.json'))); -exampleProjects.forEach((proj) => { - const packageJsonPath = path.join(examplesDir, proj, 'package.json'); - updateDependenciesInExamples(packageJsonPath, sdkDependencies); -}); - -console.log(`All updates complete. New SDK version: ${newVersion}`); From aaa9e86af4a7620ed3f2b5c537a818e20787d41e Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 10:21:13 -0500 Subject: [PATCH 04/15] Expand Defect B2 scope: main has 6 examples, not 4 Discovered during implementation: main also carries examples/angular-ssr (already correctly "latest") and examples/nextjs-experiments (still "next", same defect as nextjs/vuejs) -- neither was in the original file listing. Confirmed via git ls-tree that neither exists on either LTS branch (both carry only the original four), so Defect B3's scope is unaffected. Every "four main examples" reference updated to six; the shared validation script needs no code change since it takes a path/branch generically rather than hardcoding the example list. Co-Authored-By: Claude Sonnet 5 --- specs/37399-sdk-packaging-version-fix/spec.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md index 0c3557cae910..d87d481a130f 100644 --- a/specs/37399-sdk-packaging-version-fix/spec.md +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -25,9 +25,9 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) has two related classes of defect that together make the SDK's published packages, and the example apps that scaffold customer projects, unreliable: 1. **Published packages report a version that does not exist.** The version string written into a published package's `package.json` (and into the version ranges other `@dotcms/*` packages depend on) is not valid semver and does not match what the npm registry advertises for that same package. -2. **Floating/incorrect version specifiers leak into what customers install, and sibling SDK packages are declared with the wrong dependency type.** SDK library source files hardcode `"@dotcms/*": "latest"`, and — separately from the value being wrong — `react`, `angular`, `vue`, and `analytics` declare their runtime siblings (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies` rather than `peerDependencies`. A regular `dependency` gets independently resolved by yarn/pnpm, which is what lets those two package managers install a second, mismatched copy of `@dotcms/client` nested under the consumer's own pinned copy — the same failure mode `react-dom` avoids by declaring `react` as a peer instead of a dependency. `main`'s example apps (`nextjs`, `vuejs`) point to `"next"`, an internal dev/QA pre-release tag never meant for customers — wrong regardless of any version-skew question, since it isn't even a released version. And critically, both active LTS branches' own dedicated `examples/nextjs` copies point to a floating tag (`"latest"`) instead of a version pinned to that LTS server, letting the installed SDK run far ahead of it and breaking the scaffolded app — these LTS-branch copies exist specifically to demonstrate against that LTS server, so floating there defeats their purpose. (`main`'s own four examples deliberately tracking `"latest"` going forward — as the correct target for dotCMS Evergreen customers — is an accepted product decision, not a defect; see Fix Scope & Non-Goals.) On two package managers (yarn, pnpm) the SDK library issue above additionally silently overrides a customer's own explicit version pin. +2. **Floating/incorrect version specifiers leak into what customers install, and sibling SDK packages are declared with the wrong dependency type.** SDK library source files hardcode `"@dotcms/*": "latest"`, and — separately from the value being wrong — `react`, `angular`, `vue`, and `analytics` declare their runtime siblings (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies` rather than `peerDependencies`. A regular `dependency` gets independently resolved by yarn/pnpm, which is what lets those two package managers install a second, mismatched copy of `@dotcms/client` nested under the consumer's own pinned copy — the same failure mode `react-dom` avoids by declaring `react` as a peer instead of a dependency. `main`'s example apps `nextjs`, `vuejs`, and `nextjs-experiments` point to `"next"`, an internal dev/QA pre-release tag never meant for customers — wrong regardless of any version-skew question, since it isn't even a released version. And critically, both active LTS branches' own dedicated `examples/nextjs` copies point to a floating tag (`"latest"`) instead of a version pinned to that LTS server, letting the installed SDK run far ahead of it and breaking the scaffolded app — these LTS-branch copies exist specifically to demonstrate against that LTS server, so floating there defeats their purpose. (`main`'s own six examples deliberately tracking `"latest"` going forward — as the correct target for dotCMS Evergreen customers — is an accepted product decision, not a defect; see Fix Scope & Non-Goals.) On two package managers (yarn, pnpm) the SDK library issue above additionally silently overrides a customer's own explicit version pin. -**Severity / Impact**: High. This has already caused a live, escalated customer support ticket (Freshdesk #38677, against a 25.07.10 LTS server) and three prior tickets of the same class (Freshdesk #36678, #37710, #38038). Affected customers are anyone who: (a) inspects an installed `@dotcms/*` package's reported version (SBOM/license scanners, vulnerability tooling, bundler metadata), (b) installs the SDK with yarn or pnpm alongside an explicit version pin, or (c) scaffolds one of the four documented example apps, especially against an LTS server. The scaffold path is the most severe because it is the officially-documented onboarding path and, per the SDK's own `DotCMSPage` GraphQL fragment being hardcoded, pinning the SDK version is the *only* customer-side mitigation available — and that mitigation is exactly what's broken. +**Severity / Impact**: High. This has already caused a live, escalated customer support ticket (Freshdesk #38677, against a 25.07.10 LTS server) and three prior tickets of the same class (Freshdesk #36678, #37710, #38038). Affected customers are anyone who: (a) inspects an installed `@dotcms/*` package's reported version (SBOM/license scanners, vulnerability tooling, bundler metadata), (b) installs the SDK with yarn or pnpm alongside an explicit version pin, or (c) scaffolds one of the six documented example apps, especially against an LTS server. The scaffold path is the most severe because it is the officially-documented onboarding path and, per the SDK's own `DotCMSPage` GraphQL fragment being hardcoded, pinning the SDK version is the *only* customer-side mitigation available — and that mitigation is exactly what's broken. ## Reproduction *(mandatory)* @@ -52,12 +52,12 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - `.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml` — the composite action that writes the release version into every SDK package's `package.json` and publishes to npm; used by both `cicd_release-sdk.yml` (real releases, `latest` tag) and `cicd_3-trunk.yml` (`next` tag on SDK-touching merges to `main`). - **All seven** `core-web/libs/sdk/*/package.json` files hardcode a floating `"@dotcms/*": "latest"` specifier in source, confirmed by scripted inspection of every SDK lib manifest (not just the three originally suspected): `analytics`, `angular`, `client`, `experiments`, `react`, `uve`, `vue`. It appears in `dependencies` (`react`, `angular`, `vue`, `analytics`), `peerDependencies` (`experiments`), and `devDependencies` (`@dotcms/types`, in all seven) — so the fix and the CI guardrail must cover all three fields, not just `dependencies`/`peerDependencies`. None of these fields are consulted by local development or by `pnpm install` at the `core-web` root: there is no `pnpm-workspace.yaml` and no `workspaces` field in `core-web/package.json`, so these lib manifests are not real pnpm workspace members — they are inert publish-artifact templates until `deploy-javascript-sdk/action.yml`'s `jq` step rewrites them, right before `npm publish`. - `core-web/bump-sdk-versions.js` — confirmed dead code (no workflow references it) that only ever rewrote `peerDependencies`, never `dependencies`. - - `examples/nextjs/package.json`, `examples/vuejs/package.json` (pin `"next"`), `examples/angular/package.json`, `examples/astro/package.json` (pin `"latest"`) on `main`. + - `examples/nextjs/package.json`, `examples/vuejs/package.json`, `examples/nextjs-experiments/package.json` (pin `"next"`), `examples/angular/package.json`, `examples/astro/package.json`, `examples/angular-ssr/package.json` (pin `"latest"`) on `main`. Confirmed by directory listing that `main` has six example apps, not the four originally suspected — `angular-ssr` and `nextjs-experiments` are recent additions with the identical defect pattern. Neither exists on either LTS branch (confirmed via `git ls-tree` — both LTS branches carry only the original four). - `examples/nextjs/package.json` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` (pins `"latest"`). - No existing CI check guards against a floating `@dotcms/*` specifier reappearing in any of the above. - **Related known decisions**: **ADR-0019 (SDK/CMS date-lockstep versioning, accepted 2026-06-23)** was read in full during this investigation (not just referenced) and is directly on point: - It states the exact rule Defect A fails to implement: *"dotCMS release `2026.06.24` → SDK version `2026.6.24` (leading zeros dropped so the string is a valid, ordered npm SemVer triple)."* Defect A is therefore non-compliance with an already-accepted decision, not a new design problem. - - Its Implementation Notes list says `examples/*` should pin an exact date version instead of `latest`. **This spec deliberately diverges from that literal note for `main`'s four examples only**, resolving an internal tension in the ADR itself: the ADR's own "How this plays with Evergreen" section states that for Evergreen customers (always on the latest dotCMS release, upgraded ~every 2 weeks), *"'install the latest SDK' and 'install the SDK matching my CMS' converge to the same thing... they simply track the latest SDK, which by construction matches their always-latest CMS."* An exact pin set once at implementation time would drift stale within weeks relative to Evergreen's cadence, while `"latest"` never does — so for `main`'s Evergreen-facing examples, floating is the choice that actually matches the ADR's stated goal of self-evident compatibility with zero synchronization hacks, even though it departs from the ADR's literal implementation note. This divergence is scoped narrowly to `main`'s four examples; the LTS-branch example copies (which exist to demonstrate against a fixed, non-Evergreen server) follow the ADR's literal exact-pin note without exception — see Fix Scope. + - Its Implementation Notes list says `examples/*` should pin an exact date version instead of `latest`. **This spec deliberately diverges from that literal note for `main`'s six examples only**, resolving an internal tension in the ADR itself: the ADR's own "How this plays with Evergreen" section states that for Evergreen customers (always on the latest dotCMS release, upgraded ~every 2 weeks), *"'install the latest SDK' and 'install the SDK matching my CMS' converge to the same thing... they simply track the latest SDK, which by construction matches their always-latest CMS."* An exact pin set once at implementation time would drift stale within weeks relative to Evergreen's cadence, while `"latest"` never does — so for `main`'s Evergreen-facing examples, floating is the choice that actually matches the ADR's stated goal of self-evident compatibility with zero synchronization hacks, even though it departs from the ADR's literal implementation note. This divergence is scoped narrowly to `main`'s six examples; the LTS-branch example copies (which exist to demonstrate against a fixed, non-Evergreen server) follow the ADR's literal exact-pin note without exception — see Fix Scope. - It also calls for retiring/disabling the standalone SDK publish workflow (the `next`-dist-tag job in `cicd_3-trunk.yml`) and a redesigned runtime compatibility check against `/api/v1/appconfiguration`. **Both are explicitly out of scope for this fix** — see Non-Goals. The plan phase must consult `dotCMS/platform-adrs` for ADR-0019 (and any related ADR) before finalizing the fix, and must record this Evergreen-based divergence explicitly in its ADR Alignment section, since the constitution requires any conflict with an accepted ADR to be resolved or justified rather than silently applied. @@ -72,7 +72,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) **Defect B1b (dependency type)**: Beyond the wrong *value*, `react`, `angular`, `vue`, and `analytics` declare their sibling runtime packages (`@dotcms/client`, `@dotcms/uve`) as regular `dependencies`. A regular dependency is resolved independently by each package manager, which is the actual mechanism behind the yarn/pnpm silent-override repro in Defect B1's steps: even once the *value* is fixed, a `dependency` entry can still be resolved to a different copy than the one the consumer explicitly pinned. `experiments` already declares its four sibling `@dotcms/*` entries as `peerDependencies` — the correct shape, just with the wrong (`"latest"`) value. `peerDependencies` are what defer to whatever the consuming project has already installed instead of bringing an independent copy, matching how `react-dom` avoids shipping its own `react` — but `peerDependencies` are only auto-installed by npm 7+ and pnpm; **yarn classic (1.x) and npm below v7 do not auto-install peer dependencies**, they only warn if one is missing. This is a real, customer-visible behavior change that must be documented (see Fix Scope) — confirmed via reviewer feedback on this spec's PR (see PR #37452 review comment) that the affected SDKs' READMEs currently promise automatic installation and must be corrected, and that `@dotcms/types` (already a `devDependency`, never auto-installed even before this fix) belongs in the same corrected instructions. -**Defect B2**: `examples/nextjs` and `examples/vuejs` on `main` point to `"next"`, an internal dev/QA pre-release tag — wrong regardless of server-version considerations, since it isn't even a released version and was never meant for customers. (`examples/angular`/`examples/astro` on `main` already point to `"latest"`, which this fix keeps and extends to the other two — see Fix Scope for why `"latest"` is the right target on `main` specifically.) +**Defect B2**: `examples/nextjs`, `examples/vuejs`, and `examples/nextjs-experiments` on `main` point to `"next"`, an internal dev/QA pre-release tag — wrong regardless of server-version considerations, since it isn't even a released version and was never meant for customers. (`examples/angular`, `examples/astro`, and `examples/angular-ssr` on `main` already point to `"latest"`, which this fix keeps and extends to the other three — see Fix Scope for why `"latest"` is the right target on `main` specifically.) **Defect B3**: The LTS branches' own `examples/nextjs` copies point to `"latest"` instead of a version compatible with that LTS server, and nothing has ever back-filled them with a correct, fixed pin. Unlike `main` — where floating matches the Evergreen audience the example targets — floating is never correct here, since these branch-specific copies exist precisely to demonstrate against a fixed, older server (this is what broke the live customer ticket, Freshdesk #38677). @@ -88,8 +88,8 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - Extend `deploy-javascript-sdk/action.yml`'s rewrite loop to also cover `devDependencies` (today it only rewrites `dependencies`/`peerDependencies`) — this is why every currently-published package still carries a live, unmasked `"latest"` in `devDependencies.@dotcms/types`, confirmed on npm today. The loop must also correctly handle entries that moved from `dependencies` to `peerDependencies` in source. - Update the "Installation" section of `core-web/libs/sdk/react/README.md`, `core-web/libs/sdk/angular/README.md`, and `core-web/libs/sdk/vue/README.md` (each currently states installing the main package "will automatically install the required dependencies," listing `@dotcms/uve`/`@dotcms/client` — confirmed by direct inspection, e.g. `react/README.md` lines ~101-109). This claim is no longer reliably true and must be corrected to explicitly instruct the consumer to install `@dotcms/client`, `@dotcms/uve`, **and `@dotcms/types`** alongside the main package for full UVE support. (`@dotcms/types` was never auto-installed even before this fix — it has always been a `devDependency` — but the README never said so; fixed here since the same section is being corrected anyway.) Explicitly name the affected consumers: **yarn classic (1.x)** and **npm below v7** do not auto-install peer dependencies at all (only warn if missing); npm 7+ and pnpm do auto-install them but a customer relying on the old "it just works" claim should not be surprised either way. - Retire `core-web/bump-sdk-versions.js` (dead code, superseded by `deploy-javascript-sdk/action.yml`'s rewrite logic). -- Change `examples/nextjs` and `examples/vuejs` on `main` from `"next"` to `"latest"`, matching `examples/angular`/`examples/astro` (already `"latest"`). All four `main` examples deliberately track the floating `"latest"` npm dist-tag going forward — not an exact pin. Rationale: `main`'s examples target dotCMS Evergreen customers, who are always on the current release; `"latest"` converges with "the version matching my CMS" for that audience by construction (see Scope of Investigation's Evergreen note), and never goes stale the way a one-time exact pin would. -- Add a short, prominent note to each of `main`'s four example READMEs: a customer whose dotCMS instance is **not** on Evergreen/the current release (self-hosted on an older version, or LTS) must manually replace the `@dotcms/*` version in that example's `package.json` with the version matching their own dotCMS instance, and a one-line pointer to how to find that version. This is the documented, customer-side mitigation for the version-skew case `"latest"` doesn't handle on `main`. +- Change `examples/nextjs`, `examples/vuejs`, and `examples/nextjs-experiments` on `main` from `"next"` to `"latest"`, matching `examples/angular`/`examples/astro`/`examples/angular-ssr` (already `"latest"`). All six `main` examples deliberately track the floating `"latest"` npm dist-tag going forward — not an exact pin. Rationale: `main`'s examples target dotCMS Evergreen customers, who are always on the current release; `"latest"` converges with "the version matching my CMS" for that audience by construction (see Scope of Investigation's Evergreen note), and never goes stale the way a one-time exact pin would. +- Add a short, prominent note to each of `main`'s six example READMEs: a customer whose dotCMS instance is **not** on Evergreen/the current release (self-hosted on an older version, or LTS) must manually replace the `@dotcms/*` version in that example's `package.json` with the version matching their own dotCMS instance, and a one-line pointer to how to find that version. This is the documented, customer-side mitigation for the version-skew case `"latest"` doesn't handle on `main`. - Fix `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` to an exact, LTS-compatible pinned version (never a floating tag) — these copies exist specifically to demonstrate against that LTS server, not Evergreen, so `"latest"` is wrong here regardless of the `main` rationale above. This is what resolves the live customer ticket (Freshdesk #38677). - Add a CI check that fails the build if: - any `core-web/libs/sdk/*/package.json` declares a floating specifier (`latest`, `next`, `*`) for an `@dotcms/*` entry in `peerDependencies` — must be real semver (the `"0.0.0"` sentinel today); `"latest"` in `dependencies`/`devDependencies` of these same files is **not** flagged (intentional, masked at publish — see In Scope); @@ -103,9 +103,9 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - No code change to `core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.ts`, `fetch-http-client.ts`, or `rollup.config.cjs` — only the one regression test noted above. - No change to `MinSdkVersion.java` or `SdkVersionWebInterceptor.java` (Java backend) or any other `com.dotcms.*`/`com.dotmarketing.*` code. - No change to Angular/React UI components. -- **Making `main`'s example apps work against an old/non-Evergreen dotCMS server via code/CI is explicitly out of scope.** `main`'s four examples deliberately track `"latest"` for the Evergreen audience they target (see Fix Scope). A customer on an older/LTS server who scaffolds `main`'s example and hits a version-skew failure is expected to consult the documented note (see Fix Scope) and manually pin the version matching their own instance — this is a deliberate, documented customer-side responsibility, not something this fix can solve with a static pin (a pin set once would itself go stale relative to Evergreen, recreating the same class of problem). A customer on an old/LTS server who wants a working example **without** manual adjustment should scaffold from their own server's release/LTS branch instead — which is exactly why the LTS branches carry their own dedicated, separately-pinned example copies (see In Scope). +- **Making `main`'s example apps work against an old/non-Evergreen dotCMS server via code/CI is explicitly out of scope.** `main`'s six examples deliberately track `"latest"` for the Evergreen audience they target (see Fix Scope). A customer on an older/LTS server who scaffolds `main`'s example and hits a version-skew failure is expected to consult the documented note (see Fix Scope) and manually pin the version matching their own instance — this is a deliberate, documented customer-side responsibility, not something this fix can solve with a static pin (a pin set once would itself go stale relative to Evergreen, recreating the same class of problem). A customer on an old/LTS server who wants a working example **without** manual adjustment should scaffold from their own server's release/LTS branch instead — which is exactly why the LTS branches carry their own dedicated, separately-pinned example copies (see In Scope). - **Full adoption of ADR-0019 is out of scope for this fix**, beyond the specific points above. ADR-0019 additionally calls for: retiring/disabling the standalone SDK publish workflow (`cicd_3-trunk.yml`'s `publish-sdk-next` job, the `next` npm dist-tag) and folding SDK publishing entirely into the dotCMS release pipeline; bidirectional coupling (an SDK change triggers a new dotCMS release); and a redesigned runtime compatibility check reading `/api/v1/appconfiguration` (replacing the current `MinSdkVersion.java`/`SdkVersionWebInterceptor.java`/`sdk-compatibility.ts` header-based mechanism). None of that is undertaken here — it is a separate, larger migration. Specifically on the `next` tag: the team has already decided, independently of this issue, to keep it active because internal developers need it for testing — ADR-0019 itself anticipates and allows exactly this ("keep a non-version-publishing subset of the workflow for them"), so this is not a new, unjustified deviation. -- No automated, ongoing mechanism to keep the **LTS branches'** example pin in sync with newer compatible releases (e.g. a pipeline step that auto-commits a newer LTS-compatible version over time). The developer explicitly chose the simpler CI-guardrail-only approach over auto-commit, given the risk of unattended commits to protected/LTS branches. Keeping the LTS pin current is a manual, as-needed responsibility; the CI check only guards against it regressing to a *floating* specifier, not against it becoming *stale but still pinned*. (This staleness concern does not apply to `main`'s four examples — they intentionally use the floating `"latest"` tag precisely so they never go stale.) +- No automated, ongoing mechanism to keep the **LTS branches'** example pin in sync with newer compatible releases (e.g. a pipeline step that auto-commits a newer LTS-compatible version over time). The developer explicitly chose the simpler CI-guardrail-only approach over auto-commit, given the risk of unattended commits to protected/LTS branches. Keeping the LTS pin current is a manual, as-needed responsibility; the CI check only guards against it regressing to a *floating* specifier, not against it becoming *stale but still pinned*. (This staleness concern does not apply to `main`'s six examples — they intentionally use the floating `"latest"` tag precisely so they never go stale.) - No retroactive fix of already-published `1.x` npm packages (npm immutability makes this impossible); support-docs guidance for customers stuck on those versions is tracked separately per the original issue's own note (`overrides`/`resolutions`/`pnpm-workspace.yaml` guidance), not as engineering work here. - No change to the `next` dist-tag publishing mechanism itself (`cicd_3-trunk.yml`'s `publish-sdk-next` job) — it was already restored by a prior, unrelated PR (#36722) and is confirmed live/current as of this writing; only the example apps' *pinning* of `next` is in scope. @@ -121,7 +121,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - **AC-002**: Injected `@dotcms/*` inter-package dependency/`peerDependencies` pins use that same exact, normalized version string; a strict (non-loose) semver parse of every published `@dotcms/*` version and internal dependency range succeeds. - **AC-003**: In each of the seven `core-web/libs/sdk/*/package.json` files (`analytics`, `angular`, `client`, `experiments`, `react`, `uve`, `vue`), every sibling `@dotcms/*` entry in `peerDependencies` uses the `"0.0.0"` sentinel, never `latest`/`next`/`*`. `dependencies`/`devDependencies` entries are unaffected by this AC (they intentionally keep `"latest"` in source — see AC-002's masking and Fix Scope). - **AC-003b**: `@dotcms/client` and `@dotcms/uve` are declared in `peerDependencies`, not `dependencies`, in `react`, `angular`, `vue`, and `analytics`; installing a pinned version of any of these four under yarn or pnpm no longer produces a second, independently-resolved copy of `@dotcms/client`/`@dotcms/uve` nested in that package's own `node_modules`. -- **AC-004**: `examples/nextjs` and `examples/vuejs` on `main` point to `"latest"` (no longer `"next"`), matching `examples/angular`/`examples/astro`; scaffolding each, installing, and running `npm ls @dotcms/client` yields exactly one copy, resolved from the `latest` npm dist-tag (never `next`, never a stale pin). Each example's README carries the documented note (see Fix Scope) for non-Evergreen customers. +- **AC-004**: `examples/nextjs`, `examples/vuejs`, and `examples/nextjs-experiments` on `main` point to `"latest"` (no longer `"next"`), matching `examples/angular`/`examples/astro`/`examples/angular-ssr`; scaffolding each of the six, installing, and running `npm ls @dotcms/client` yields exactly one copy, resolved from the `latest` npm dist-tag (never `next`, never a stale pin). Each example's README carries the documented note (see Fix Scope) for non-Evergreen customers. - **AC-005**: `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` pins a version verified compatible with that LTS server's GraphQL schema; scaffolding and rendering a page against a 25.07.10 LTS server produces no `FieldUndefined` errors. - **AC-006**: Installing a pinned `@dotcms/react` under npm 7+ and pnpm each yields exactly one `@dotcms/client` at the pinned version (no silent override). Under yarn classic (1.x) or npm below v7, installing `@dotcms/react` **alone** (without also explicitly installing `@dotcms/client`) is expected to warn about an unmet peer dependency (or fail with a missing-module error at runtime) rather than silently install a mismatched copy — this is the documented behavior change (see AC-003b, Fix Scope). - **AC-009**: `react/README.md`, `angular/README.md`, and `vue/README.md`'s Installation sections no longer claim that installing the main package automatically installs its required dependencies; each explicitly instructs the consumer to also install `@dotcms/client`, `@dotcms/uve`, and `@dotcms/types`, and names yarn classic (1.x) and npm below v7 as the package managers that require this manual step. @@ -132,7 +132,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) ## Assumptions - The LTS-compatible pin for `release-25.07.10_lts_v12`/`_v16`'s `examples/nextjs` is assumed to be `1.2.0` (the newest release the original issue's author identified as validating against that LTS server's GraphQL schema). **Resolution (developer decision)**: this has not been independently re-verified against a live 25.07.10 LTS server in this investigation, and will not be taken as given — `/speckit-plan`/`/speckit-implement` MUST confirm `1.2.0` (or identify the correct version) by actually scaffolding `examples/nextjs` and rendering a page against a real 25.07.10 LTS server before it is committed as the LTS example's pin. This confirmation step is now folded into AC-005's verification method, not left as an open question blocking this spec. -- `main`'s four examples deliberately keep using the floating `"latest"` npm dist-tag (not an exact pin) going forward — confirmed with the developer as an intentional decision, grounded in ADR-0019's own Evergreen-convergence reasoning (see Scope of Investigation): a customer scaffolding from `main` is expected to be on dotCMS Evergreen (always the current release), for whom `"latest"` and "the version matching my CMS" are the same thing by construction. A customer on an older/non-Evergreen server is documented (Fix Scope) as needing to manually pin the version matching their own instance. This is the one deliberate exception to "no floating `@dotcms/*` specifiers" elsewhere in this spec, and the CI guardrail (AC-007) must not flag it. +- `main`'s six examples deliberately keep using the floating `"latest"` npm dist-tag (not an exact pin) going forward — confirmed with the developer as an intentional decision, grounded in ADR-0019's own Evergreen-convergence reasoning (see Scope of Investigation): a customer scaffolding from `main` is expected to be on dotCMS Evergreen (always the current release), for whom `"latest"` and "the version matching my CMS" are the same thing by construction. A customer on an older/non-Evergreen server is documented (Fix Scope) as needing to manually pin the version matching their own instance. This is the one deliberate exception to "no floating `@dotcms/*` specifiers" elsewhere in this spec, and the CI guardrail (AC-007) must not flag it. - No separate GitHub issue/spec is being opened for the "keep the LTS example pin in sync with newer compatible releases over time" concern; it is accepted as a manual, as-needed process step, guarded only against the floating-specifier regression (see Non-Goals). This does not apply to `main`'s examples, which cannot go stale since they float by design. - Keeping the standalone `next`-tag publish workflow (`cicd_3-trunk.yml`) active, rather than retiring/disabling it as ADR-0019's Implementation Notes suggest, is a decision the team already made independently of this issue — internal developers need it for testing. This spec does not revisit that decision; it is explicitly out of scope (see Non-Goals). - The `"0.0.0"` sentinel is deliberately scoped to `peerDependencies` only, not `dependencies`/`devDependencies` — confirmed with the developer. Rationale: customers never build from this repo's source, so a `dependencies`/`devDependencies` value that's already masked by the publish-time rewrite has no customer-facing effect, and changing it buys nothing. `peerDependencies` is different because it's where dotCMS's own developers hit real, local pre-publish testing breakage (an npm/yarn/pnpm peer-dependency-satisfaction check can't evaluate `"latest"` as a semver range), so only that field's source value needs to be a real semver. From adf9b37a2cff088778d4e8d758f6c0729ed64e69 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 10:25:04 -0500 Subject: [PATCH 05/15] Normalize SDK release version to valid semver (Defect A) Tasks T013-T019 for spec specs/37399-sdk-packaging-version-fix. The release pipeline wrote the zero-padded dotCMS release tag (e.g. 26.08.03-01) verbatim into every published package.json field, mismatching npm's own normalized registry metadata (26.8.3-1) -- ADR-0019 already mandates stripping the zeros; the pipeline never implemented that rule. - deploy-javascript-sdk/action.yml: normalize $RELEASE_VERSION once, right after checkout, before it's used anywhere. Surface the normalized value as a step output and reference it explicitly from the later "Publish" step's own env block and the action's own output -- a step's literal `env: FOO: ${{ inputs.x }}` always wins over an earlier step's $GITHUB_ENV write, so normalizing only the first step's local variable would not have propagated. - Extend the same rewrite loop to devDependencies (previously only dependencies/peerDependencies) -- confirmed live on npm that this field was the one actually shipping unmasked to customers today (devDependencies.@dotcms/types: "latest" in every currently-published package). - sdk-compatibility.spec.ts: add a regression-lock test asserting compareVersions('26.08.03-01', '26.8.3-1') === 0 (AC-008) -- this already passes today, locking in that a future refactor can't break it. Verified locally against a scratch copy of the real core-web/libs/sdk/* package.json files (both before and after the fix) rather than via `workflow_dispatch --dry-run`: that flag turned out to skip the entire action under test, and running without it risks a real, irreversible npm publish -- neither is an acceptable way to test this. Co-Authored-By: Claude Sonnet 5 --- .../deploy-javascript-sdk/action.yml | 32 +++++++++++++++---- .../src/lib/utils/sdk-compatibility.spec.ts | 9 ++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml b/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml index a7bb6bbdffc0..8dff65d45a77 100644 --- a/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml +++ b/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml @@ -39,7 +39,7 @@ inputs: outputs: npm-package-version: description: 'SDK libs - NPM package version that was published' - value: ${{ inputs.version }} + value: ${{ steps.update-versions.outputs.normalized_version }} published: description: 'SDK libs - Published (true if all packages published successfully)' value: ${{ steps.deployment_status.outputs.published }} @@ -58,6 +58,7 @@ runs: node-version-file: 'core-web/.nvmrc' - name: 'Update package.json versions' + id: update-versions working-directory: ${{ github.workspace }}/core-web/libs/sdk/ env: RELEASE_VERSION: ${{ inputs.version }} @@ -66,6 +67,21 @@ runs: set -euo pipefail echo "::group::Update package.json versions" + # ADR-0019 requires the published SDK version to be valid, ordered npm SemVer — + # e.g. "2026.6.24", not "2026.06.24" — but the dotCMS release tag this value comes + # from is zero-padded (yy.mm.dd-##), so strip leading zeros from each numeric + # segment before writing it anywhere. This is the single point of normalization; + # every jq call below, and the idempotency check in the publish step, must use + # this normalized value, not the raw $RELEASE_VERSION input. + RELEASE_VERSION=$(echo "$RELEASE_VERSION" | awk -F'[.-]' '{ + for (i = 1; i <= NF; i++) { sub(/^0+/, "", $i); if ($i == "") $i = "0" } + out = $1 "." $2 "." $3 + if (NF >= 4) out = out "-" $4 + print out + }') + echo "Normalized release version: $RELEASE_VERSION" + echo "normalized_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT" + sdk_packages=($(find . -maxdepth 1 -mindepth 1 -type d -exec basename {} \;)) echo "Found SDK packages: ${sdk_packages[*]}" @@ -75,11 +91,13 @@ runs: jq --arg v "$RELEASE_VERSION" '.version = $v' "$pkg" > tmp.$$.json && mv tmp.$$.json "$pkg" echo " ✅ $sdk -> $RELEASE_VERSION" - # Update dependencies/peerDependencies that reference other SDK packages. - # Pinned exact (no caret): sibling SDK packages always publish in lockstep at - # the same version, so a range only reintroduces the ambiguity ADR-0019 removes. + # Update dependencies/peerDependencies/devDependencies that reference other SDK + # packages. Pinned exact (no caret): sibling SDK packages always publish in + # lockstep at the same version, so a range only reintroduces the ambiguity + # ADR-0019 removes. devDependencies is included so @dotcms/types (a dev-only + # dependency in every SDK lib) no longer ships with an unmasked "latest". for dep in "${sdk_packages[@]}"; do - for field in dependencies peerDependencies; do + for field in dependencies peerDependencies devDependencies; do if jq -e ".${field}[\"@dotcms/$dep\"]" "$pkg" >/dev/null 2>&1; then jq --arg field "$field" --arg dep "@dotcms/$dep" --arg v "$RELEASE_VERSION" \ '.[$field][$dep] = $v' "$pkg" > tmp.$$.json && mv tmp.$$.json "$pkg" @@ -158,7 +176,9 @@ runs: id: publish_packages working-directory: ${{ github.workspace }}/core-web/dist/libs/sdk/ env: - RELEASE_VERSION: ${{ inputs.version }} + # Normalized by the "Update package.json versions" step above — must match exactly + # what was written into each package.json, not the raw (possibly zero-padded) input. + RELEASE_VERSION: ${{ steps.update-versions.outputs.normalized_version }} NPM_AUTH_TOKEN: ${{ inputs.npm-token }} NPM_TAG: ${{ inputs.npm-tag }} run: | diff --git a/core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.spec.ts b/core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.spec.ts index a032d55c68ef..2f6f08e137e2 100644 --- a/core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.spec.ts +++ b/core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.spec.ts @@ -27,6 +27,15 @@ describe('compareVersions', () => { expect(compareVersions('26.7.13_lts_v1', '26.7.13')).toBeNull(); expect(compareVersions('not-a-version', '26.7.13')).toBeNull(); }); + + it('treats a zero-padded version as equal to its normalized form (regression lock)', () => { + // Defect A: the release pipeline used to write the zero-padded CalVer date + // (e.g. "26.08.03-01") verbatim into published package.json files, instead of the + // normalized, valid-semver form npm's registry metadata shows ("26.8.3-1"). This + // already compares as equal today -- Number("08") is 8 in JS string-to-number + // conversion, not octal -- so this locks in that correctness against future refactors. + expect(compareVersions('26.08.03-01', '26.8.3-1')).toBe(0); + }); }); describe('checkSdkCompatibility', () => { From 864c3755979e700627684837d2530d7e5df47fc9 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 10:30:51 -0500 Subject: [PATCH 06/15] Move sibling SDK deps to peerDependencies with 0.0.0 sentinel (Defect B1/B1b) Tasks T020-T027 for spec specs/37399-sdk-packaging-version-fix. react, angular, vue, and analytics declared @dotcms/client/@dotcms/uve as regular `dependencies` -- the actual mechanism letting yarn/pnpm install a second, independently-resolved copy alongside a consumer's own pinned version, regardless of the value's correctness. Moved to `peerDependencies` (the shape `experiments` already used) so they defer to whatever the consumer already installed, matching how react-dom avoids shipping its own react. Value fixed to the "0.0.0" sentinel everywhere a sibling @dotcms/* package appears in peerDependencies (including experiments' pre-existing four entries) -- "latest" is not a valid semver range, so a local peer-dependency-satisfaction check can't evaluate it sanely regardless of what's actually installed, which was the real cause of local pre-publish testing breakage. dependencies/devDependencies keep "latest" unchanged -- masked by the publish-time rewrite (Defect A fix), no customer-facing effect. Corrected react/angular/vue's READMEs: each claimed installing the main package "will automatically install the required dependencies" -- no longer reliably true. Now lists @dotcms/client, @dotcms/uve, and @dotcms/types as required manual installs, naming yarn classic (1.x) and npm below v7 as needing this (AC-009, addresses PR #37452 review feedback). Verified with real locally-built tarballs (sdk-react + sdk-client) across npm, pnpm, and yarn classic 1.22.22: no nested duplicate @dotcms/client under react's own node_modules in any of the three -- the original duplicate-copy bug is fixed. Yarn classic correctly warns instead of auto-installing a peer that's genuinely missing from the manifest, confirming the documented behavior change is real, not hypothetical. Co-Authored-By: Claude Sonnet 5 --- core-web/libs/sdk/analytics/package.json | 4 ++-- core-web/libs/sdk/angular/README.md | 11 ++++++++++- core-web/libs/sdk/angular/package.json | 8 ++++---- core-web/libs/sdk/experiments/package.json | 8 ++++---- core-web/libs/sdk/react/README.md | 11 ++++++++++- core-web/libs/sdk/react/package.json | 6 +++--- core-web/libs/sdk/vue/README.md | 14 ++++++++++++-- core-web/libs/sdk/vue/package.json | 6 +++--- 8 files changed, 48 insertions(+), 20 deletions(-) diff --git a/core-web/libs/sdk/analytics/package.json b/core-web/libs/sdk/analytics/package.json index 69959710739a..637d61f854fa 100644 --- a/core-web/libs/sdk/analytics/package.json +++ b/core-web/libs/sdk/analytics/package.json @@ -24,11 +24,11 @@ "@analytics/queue-utils": "^0.1.3", "@analytics/router-utils": "^0.1.1", "@analytics/storage-utils": "^0.4.0", - "@dotcms/uve": "latest", "analytics": "^0.8.0" }, "peerDependencies": { - "react": "^18 || ^19" + "react": "^18 || ^19", + "@dotcms/uve": "0.0.0" }, "devDependencies": { "@dotcms/types": "latest", diff --git a/core-web/libs/sdk/angular/README.md b/core-web/libs/sdk/angular/README.md index 84d294aa76b7..97e7c6e0ddf5 100644 --- a/core-web/libs/sdk/angular/README.md +++ b/core-web/libs/sdk/angular/README.md @@ -107,9 +107,18 @@ For detailed instructions, please refer to the [dotCMS API Documentation - Read- npm install @dotcms/angular@latest ``` -This will automatically install the required dependencies: +You also need to install these packages yourself: - `@dotcms/uve`: Enables interaction with the [Universal Visual Editor](https://dev.dotcms.com/docs/uve-headless-config) for real-time content editing - `@dotcms/client`: Provides the core client functionality for fetching and managing dotCMS data +- `@dotcms/types`: TypeScript definitions used throughout the API + +```bash +npm install @dotcms/uve @dotcms/client @dotcms/types +``` + +> npm 7+ and pnpm install these automatically as peer dependencies alongside `@dotcms/angular`. +> **Yarn Classic (1.x) and npm below v7 do not** — they only print a warning if one is missing, +> so on those package managers you must add them explicitly as shown above. ## Configuration diff --git a/core-web/libs/sdk/angular/package.json b/core-web/libs/sdk/angular/package.json index 3d04edcfb0ec..cc5545844232 100644 --- a/core-web/libs/sdk/angular/package.json +++ b/core-web/libs/sdk/angular/package.json @@ -5,12 +5,12 @@ "rxjs": ">=7.0.0", "@angular/common": ">=17.0.0", "@angular/core": ">=17.0.0", - "@angular/router": ">=17.0.0" + "@angular/router": ">=17.0.0", + "@dotcms/client": "0.0.0", + "@dotcms/uve": "0.0.0" }, "dependencies": { - "@tinymce/tinymce-angular": "9.0.0", - "@dotcms/client": "latest", - "@dotcms/uve": "latest" + "@tinymce/tinymce-angular": "9.0.0" }, "devDependencies": { "@dotcms/types": "latest" diff --git a/core-web/libs/sdk/experiments/package.json b/core-web/libs/sdk/experiments/package.json index 6df1f127001d..1bfc08f0d21b 100644 --- a/core-web/libs/sdk/experiments/package.json +++ b/core-web/libs/sdk/experiments/package.json @@ -25,9 +25,9 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "@dotcms/client": "latest", - "@dotcms/react": "latest", - "@dotcms/uve": "latest", - "@dotcms/types": "latest" + "@dotcms/client": "0.0.0", + "@dotcms/react": "0.0.0", + "@dotcms/uve": "0.0.0", + "@dotcms/types": "0.0.0" } } \ No newline at end of file diff --git a/core-web/libs/sdk/react/README.md b/core-web/libs/sdk/react/README.md index 781d7136cbe6..29d71313474b 100644 --- a/core-web/libs/sdk/react/README.md +++ b/core-web/libs/sdk/react/README.md @@ -104,9 +104,18 @@ For detailed instructions, please refer to the [dotCMS API Documentation - Read- npm install @dotcms/react@latest ``` -This will automatically install the required dependencies: +You also need to install these packages yourself: - `@dotcms/uve`: Enables interaction with the [Universal Visual Editor](https://dev.dotcms.com/docs/uve-headless-config) for real-time content editing - `@dotcms/client`: Provides the core client functionality for fetching and managing dotCMS data +- `@dotcms/types`: TypeScript definitions used throughout the API + +```bash +npm install @dotcms/uve @dotcms/client @dotcms/types +``` + +> npm 7+ and pnpm install these automatically as peer dependencies alongside `@dotcms/react`. +> **Yarn Classic (1.x) and npm below v7 do not** — they only print a warning if one is missing, +> so on those package managers you must add them explicitly as shown above. ### dotCMS Client Configuration diff --git a/core-web/libs/sdk/react/package.json b/core-web/libs/sdk/react/package.json index 90c23bb8d62b..ec50b47ca289 100644 --- a/core-web/libs/sdk/react/package.json +++ b/core-web/libs/sdk/react/package.json @@ -3,11 +3,11 @@ "version": "1.2.6", "peerDependencies": { "react": ">=18", - "react-dom": ">=18" + "react-dom": ">=18", + "@dotcms/uve": "0.0.0", + "@dotcms/client": "0.0.0" }, "dependencies": { - "@dotcms/uve": "latest", - "@dotcms/client": "latest", "@tinymce/tinymce-react": "6.2.1" }, "devDependencies": { diff --git a/core-web/libs/sdk/vue/README.md b/core-web/libs/sdk/vue/README.md index 49a24dca7778..a0e14d18ff92 100644 --- a/core-web/libs/sdk/vue/README.md +++ b/core-web/libs/sdk/vue/README.md @@ -111,11 +111,21 @@ For detailed instructions, please refer to the [dotCMS API Documentation - Read- npm install @dotcms/vue@latest ``` -Requires **Vue 3.4+** (declared as a peer dependency). The install also brings in: +Requires **Vue 3.4+** (declared as a peer dependency). The install also brings in +`@tinymce/tinymce-vue` (powers inline text editing in [`DotCMSEditableText`](#dotcmseditabletext)), +but you need to install these packages yourself: - `@dotcms/uve`: Enables interaction with the [Universal Visual Editor](https://dev.dotcms.com/docs/uve-headless-config) for real-time content editing - `@dotcms/client`: Provides the core client functionality for fetching and managing dotCMS data -- `@tinymce/tinymce-vue`: Powers inline text editing in [`DotCMSEditableText`](#dotcmseditabletext) +- `@dotcms/types`: TypeScript definitions used throughout the API + +```bash +npm install @dotcms/uve @dotcms/client @dotcms/types +``` + +> npm 7+ and pnpm install these automatically as peer dependencies alongside `@dotcms/vue`. +> **Yarn Classic (1.x) and npm below v7 do not** — they only print a warning if one is missing, +> so on those package managers you must add them explicitly as shown above. ### dotCMS Client Configuration diff --git a/core-web/libs/sdk/vue/package.json b/core-web/libs/sdk/vue/package.json index 2bc0bc9be637..53b22d8bb7f4 100644 --- a/core-web/libs/sdk/vue/package.json +++ b/core-web/libs/sdk/vue/package.json @@ -2,11 +2,11 @@ "name": "@dotcms/vue", "version": "1.5.5", "peerDependencies": { - "vue": ">=3.4" + "vue": ">=3.4", + "@dotcms/uve": "0.0.0", + "@dotcms/client": "0.0.0" }, "dependencies": { - "@dotcms/uve": "latest", - "@dotcms/client": "latest", "@tinymce/tinymce-vue": "^6.1.0" }, "devDependencies": { From d861ba1497c8d4f1cdd147c2eb14293adbf60b8a Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 10:36:35 -0500 Subject: [PATCH 07/15] Fix main example pins to latest, document Evergreen assumption (Defect B2) Tasks T028-T033 for spec specs/37399-sdk-packaging-version-fix. examples/nextjs and examples/vuejs pinned "next" -- an internal dev/QA pre-release tag, never meant for customers. A third example not in the original scope, examples/nextjs-experiments, had the same defect (discovered mid-implementation, alongside examples/angular-ssr which was already correctly "latest"). All three fixed to "latest", matching examples/angular/astro/angular-ssr. main's six examples deliberately track "latest" rather than an exact pin (ADR-0019's Evergreen-convergence reasoning, see spec) -- added a note to each of the six READMEs telling a non-Evergreen customer (older self-hosted release, or LTS) to replace "latest" with their own server's exact version before installing. Verified against the real, currently-published npm registry (not a local tarball, since these examples install real @dotcms/* packages): deleted node_modules/package-lock.json and ran a clean install for each of the six, then `npm ls @dotcms/client`. All six resolve to exactly one deduped copy at the current `latest` (26.9.3-1). Confirmed the bug was live before this fix: nextjs's pre-fix node_modules had resolved the stale 26.9.3-1-next.2632 pre-release. Co-Authored-By: Claude Sonnet 5 --- examples/angular-ssr/README.md | 7 +++++++ examples/angular/README.md | 7 +++++++ examples/astro/README.md | 7 +++++++ examples/nextjs-experiments/README.md | 7 +++++++ examples/nextjs-experiments/package.json | 10 +++++----- examples/nextjs/README.md | 7 +++++++ examples/nextjs/package.json | 10 +++++----- examples/vuejs/README.md | 7 +++++++ examples/vuejs/package.json | 8 ++++---- 9 files changed, 56 insertions(+), 14 deletions(-) diff --git a/examples/angular-ssr/README.md b/examples/angular-ssr/README.md index 3dfc02635029..8ee4c2f1cb39 100644 --- a/examples/angular-ssr/README.md +++ b/examples/angular-ssr/README.md @@ -1,5 +1,12 @@ # Angular SSR with dotCMS Integration +> [!NOTE] +> This example's `@dotcms/*` dependencies are pinned to `latest`, matching a dotCMS Evergreen +> instance (always the current release). If your dotCMS instance is **not** on Evergreen — an +> older self-hosted release, or an LTS server — installing as-is may fail with GraphQL +> `FieldUndefined` errors. Check your server's version and replace `latest` with that exact +> version for every `@dotcms/*` entry in `package.json` before installing. + This Angular project demonstrates how to implement editable dotCMS pages using Angular Server Side Rendering (SSR). It showcases best practices for integrating dotCMS content management with Angular's hybrid rendering capabilities. ### Content Management Features diff --git a/examples/angular/README.md b/examples/angular/README.md index 1f0cead544c1..a9e7731a1425 100644 --- a/examples/angular/README.md +++ b/examples/angular/README.md @@ -1,5 +1,12 @@ # Angular Client-Side Rendering with dotCMS Integration +> [!NOTE] +> This example's `@dotcms/*` dependencies are pinned to `latest`, matching a dotCMS Evergreen +> instance (always the current release). If your dotCMS instance is **not** on Evergreen — an +> older self-hosted release, or an LTS server — installing as-is may fail with GraphQL +> `FieldUndefined` errors. Check your server's version and replace `latest` with that exact +> version for every `@dotcms/*` entry in `package.json` before installing. + This Angular project demonstrates how to implement editable dotCMS pages using Angular Client-Side Rendering (CSR). It showcases best practices for integrating dotCMS content management with Angular's client-side rendering capabilities. ### Content Management Features diff --git a/examples/astro/README.md b/examples/astro/README.md index dd34d0210b74..2710106503f1 100644 --- a/examples/astro/README.md +++ b/examples/astro/README.md @@ -1,5 +1,12 @@ # Fully Editable Page Using dotCMS + Astro +> [!NOTE] +> This example's `@dotcms/*` dependencies are pinned to `latest`, matching a dotCMS Evergreen +> instance (always the current release). If your dotCMS instance is **not** on Evergreen — an +> older self-hosted release, or an LTS server — installing as-is may fail with GraphQL +> `FieldUndefined` errors. Check your server's version and replace `latest` with that exact +> version for every `@dotcms/*` entry in `package.json` before installing. + ## Introduction & Overview This project demonstrates how to build dynamic, fully editable pages using [dotCMS](https://dotcms.com/) as a headless CMS with a [Astro](https://astro.build/) front end. By combining these technologies, you can: diff --git a/examples/nextjs-experiments/README.md b/examples/nextjs-experiments/README.md index 11d31f16f39b..f59b59e84e85 100644 --- a/examples/nextjs-experiments/README.md +++ b/examples/nextjs-experiments/README.md @@ -1,5 +1,12 @@ # dotCMS + Next.js: Experiments (A/B Testing) Example +> [!NOTE] +> This example's `@dotcms/*` dependencies are pinned to `latest`, matching a dotCMS Evergreen +> instance (always the current release). If your dotCMS instance is **not** on Evergreen — an +> older self-hosted release, or an LTS server — installing as-is may fail with GraphQL +> `FieldUndefined` errors. Check your server's version and replace `latest` with that exact +> version for every `@dotcms/*` entry in `package.json` before installing. + ## Introduction & Overview This project demonstrates how to run **A/B Experiments** on a [Next.js](https://nextjs.org/) front end powered by [dotCMS](https://dotcms.com/) as a headless CMS. It builds on the standard fully-editable-page integration and adds the dotCMS **Experiments** feature, so you can: diff --git a/examples/nextjs-experiments/package.json b/examples/nextjs-experiments/package.json index bd57327b4769..777132912b92 100644 --- a/examples/nextjs-experiments/package.json +++ b/examples/nextjs-experiments/package.json @@ -9,11 +9,11 @@ "lint": "eslint" }, "dependencies": { - "@dotcms/client": "next", - "@dotcms/experiments": "next", - "@dotcms/react": "next", - "@dotcms/types": "next", - "@dotcms/uve": "next", + "@dotcms/client": "latest", + "@dotcms/experiments": "latest", + "@dotcms/react": "latest", + "@dotcms/types": "latest", + "@dotcms/uve": "latest", "@tinymce/tinymce-react": "6.2.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index 9b7718b87add..98e25a5100ee 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -1,5 +1,12 @@ # Fully Editable Page Using dotCMS + Next.js +> [!NOTE] +> This example's `@dotcms/*` dependencies are pinned to `latest`, matching a dotCMS Evergreen +> instance (always the current release). If your dotCMS instance is **not** on Evergreen — an +> older self-hosted release, or an LTS server — installing as-is may fail with GraphQL +> `FieldUndefined` errors. Check your server's version and replace `latest` with that exact +> version for every `@dotcms/*` entry in `package.json` before installing. + ## Introduction & Overview This project demonstrates how to build dynamic, fully editable pages using [dotCMS](https://dotcms.com/) as a headless CMS with a [Next.js](https://nextjs.org/) front end. By combining these technologies, you can: diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index bd57327b4769..777132912b92 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -9,11 +9,11 @@ "lint": "eslint" }, "dependencies": { - "@dotcms/client": "next", - "@dotcms/experiments": "next", - "@dotcms/react": "next", - "@dotcms/types": "next", - "@dotcms/uve": "next", + "@dotcms/client": "latest", + "@dotcms/experiments": "latest", + "@dotcms/react": "latest", + "@dotcms/types": "latest", + "@dotcms/uve": "latest", "@tinymce/tinymce-react": "6.2.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/examples/vuejs/README.md b/examples/vuejs/README.md index 51c3c8b3fb3a..7d5a297a0e31 100644 --- a/examples/vuejs/README.md +++ b/examples/vuejs/README.md @@ -1,5 +1,12 @@ # dotCMS Vue.js Example — TravelLux +> [!NOTE] +> This example's `@dotcms/*` dependencies are pinned to `latest`, matching a dotCMS Evergreen +> instance (always the current release). If your dotCMS instance is **not** on Evergreen — an +> older self-hosted release, or an LTS server — installing as-is may fail with GraphQL +> `FieldUndefined` errors. Check your server's version and replace `latest` with that exact +> version for every `@dotcms/*` entry in `package.json` before installing. + An editorial travel front end ("TravelLux") powered by [dotCMS](https://www.dotcms.com/) as a headless CMS and **Vue 3 + Vite + TypeScript + Tailwind CSS v4**, using the [`@dotcms/vue`](../../core-web/libs/sdk/vue) SDK. Content is managed in dotCMS and rendered here, fully editable in-context through the **Universal Visual Editor (UVE)**. This mirrors the [Next.js example](../nextjs) feature-for-feature (minus AI search), showing the same content-type components, block-editor rendering, inline editing, and layout system. diff --git a/examples/vuejs/package.json b/examples/vuejs/package.json index 0402f59e9245..18b0b5f99ece 100644 --- a/examples/vuejs/package.json +++ b/examples/vuejs/package.json @@ -11,10 +11,10 @@ "type-check": "vue-tsc --build" }, "dependencies": { - "@dotcms/client": "next", - "@dotcms/types": "next", - "@dotcms/uve": "next", - "@dotcms/vue": "next", + "@dotcms/client": "latest", + "@dotcms/types": "latest", + "@dotcms/uve": "latest", + "@dotcms/vue": "latest", "@tinymce/tinymce-vue": "^6.1.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", From 4b3a63f39a111b88b9584abc230fcd2bf412a289 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 10:39:43 -0500 Subject: [PATCH 08/15] Wire the SDK package.json shape guardrail into CI (AC-007) Task T034 for spec specs/37399-sdk-packaging-version-fix. Added a new `examples` path filter (.github/filters.yaml) alongside the existing `sdk_libs` one, registered as an info_filter in cicd_comp_initialize-phase.yml. New "Validate SDK package.json shapes" job, gated on either filter, added to both cicd_1-pr.yml and cicd_5-lts.yml -- discovered mid-implementation that cicd_1-pr.yml only triggers for PRs against main/master, so LTS release-* branches would otherwise never get this check at all (their only CI is cicd_5-lts.yml, on push, post-merge). Both jobs build and run .github/scripts/validate-sdk-package-shapes against every SDK lib and example manifest, passing the PR's base ref (cicd_1-pr.yml) or the pushed branch name (cicd_5-lts.yml) so the examples check can allow "latest" on main and require an exact pin everywhere else. Wired into each workflow's `finalize` job so a violation blocks the aggregate PR/build status, not just a silent warning. Verified locally (the exact commands each job runs): current repo state passes across all 16 manifests; a deliberately-reintroduced peerDependency violation in react/package.json correctly fails, cleanly restored after. Co-Authored-By: Claude Sonnet 5 --- .github/filters.yaml | 3 ++ .github/workflows/cicd_1-pr.yml | 53 ++++++++++++++++++- .github/workflows/cicd_5-lts.yml | 52 +++++++++++++++++- .../workflows/cicd_comp_initialize-phase.yml | 2 +- 4 files changed, 107 insertions(+), 3 deletions(-) diff --git a/.github/filters.yaml b/.github/filters.yaml index ccdbbab0aa00..89d8627e148d 100644 --- a/.github/filters.yaml +++ b/.github/filters.yaml @@ -50,6 +50,9 @@ cli: &cli sdk_libs: - 'core-web/libs/sdk/**' +examples: + - 'examples/**' + # PR area labeling filters # These are separate from test-triggering filters to avoid false positives # (e.g., workflow changes should trigger backend tests but shouldn't label PR as "Area : Backend") diff --git a/.github/workflows/cicd_1-pr.yml b/.github/workflows/cicd_1-pr.yml index 8ba392e770a5..1ae1f8bce743 100644 --- a/.github/workflows/cicd_1-pr.yml +++ b/.github/workflows/cicd_1-pr.yml @@ -127,11 +127,62 @@ jobs: secrets: DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }} + # Validate SDK package.json shapes - guards against a floating @dotcms/* version + # specifier (latest/next/*) regressing into an SDK lib's peerDependencies, a sibling + # SDK dependency reappearing in the wrong field, or an example app pinning a floating + # tag anywhere it isn't allowed (main's examples are the one deliberate exception for + # "latest" - see specs/37399-sdk-packaging-version-fix/spec.md). + validate-sdk-package-shapes: + name: Validate SDK package.json shapes + needs: [ initialize ] + if: >- + always() && !cancelled() && + (fromJSON(needs.initialize.outputs.filters).sdk_libs == 'true' || + fromJSON(needs.initialize.outputs.filters).examples == 'true') + runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} + steps: + - name: 'Checkout' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: 'Setup Node' + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: 'core-web/.nvmrc' + + - name: 'Build validator' + working-directory: .github/scripts/validate-sdk-package-shapes + run: | + npm ci + npm run build + shell: bash + + - name: 'Validate SDK lib and example manifests' + working-directory: .github/scripts/validate-sdk-package-shapes + env: + # This workflow only triggers for PRs targeting main/master (see the `on:` + # trigger above), so "main" is always the correct branch context here - + # examples on main are allowed to float on "latest" (see file header). + TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} + run: | + set -euo pipefail + FAIL=0 + + for pkg_json in $(find ../../../core-web/libs/sdk -maxdepth 2 -name package.json); do + node dist/index.js "$pkg_json" || FAIL=1 + done + + for pkg_json in $(find ../../../examples -maxdepth 2 -name package.json); do + node dist/index.js "$pkg_json" --branch "$TARGET_BRANCH" || FAIL=1 + done + + exit $FAIL + shell: bash + # Finalize job - aggregates results from previous jobs finalize: name: Finalize if: always() - needs: [ test ] + needs: [ test, validate-sdk-package-shapes ] uses: ./.github/workflows/cicd_comp_finalize-phase.yml with: needsData: ${{ toJson(needs) }} diff --git a/.github/workflows/cicd_5-lts.yml b/.github/workflows/cicd_5-lts.yml index 10c65a100d2a..86e069e9f81a 100644 --- a/.github/workflows/cicd_5-lts.yml +++ b/.github/workflows/cicd_5-lts.yml @@ -95,11 +95,61 @@ jobs: secrets: DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }} + # Validate SDK package.json shapes - same guardrail as cicd_1-pr.yml. LTS branches + # never get a PR check (this workflow's only trigger is `push`), so this is the one + # place a floating specifier or wrong dependency field on a release-* branch gets + # caught at all - see specs/37399-sdk-packaging-version-fix/spec.md. + validate-sdk-package-shapes: + name: Validate SDK package.json shapes + needs: [ initialize ] + if: >- + always() && !cancelled() && + (fromJSON(needs.initialize.outputs.filters).sdk_libs == 'true' || + fromJSON(needs.initialize.outputs.filters).examples == 'true') + runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} + steps: + - name: 'Checkout' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: 'Setup Node' + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: 'core-web/.nvmrc' + + - name: 'Build validator' + working-directory: .github/scripts/validate-sdk-package-shapes + run: | + npm ci + npm run build + shell: bash + + - name: 'Validate SDK lib and example manifests' + working-directory: .github/scripts/validate-sdk-package-shapes + env: + # A `push` trigger has no "base" ref - the branch pushed to IS the context + # (e.g. release-25.07.10_lts_v12), which is never "main", so examples here + # must always pin an exact version, never float on "latest". + TARGET_BRANCH: ${{ github.ref_name }} + run: | + set -euo pipefail + FAIL=0 + + for pkg_json in $(find ../../../core-web/libs/sdk -maxdepth 2 -name package.json); do + node dist/index.js "$pkg_json" || FAIL=1 + done + + for pkg_json in $(find ../../../examples -maxdepth 2 -name package.json); do + node dist/index.js "$pkg_json" --branch "$TARGET_BRANCH" || FAIL=1 + done + + exit $FAIL + shell: bash + # Finalize job - aggregates results from previous jobs finalize: name: Finalize if: always() - needs: [ test ] + needs: [ test, validate-sdk-package-shapes ] uses: ./.github/workflows/cicd_comp_finalize-phase.yml with: needsData: ${{ toJson(needs) }} diff --git a/.github/workflows/cicd_comp_initialize-phase.yml b/.github/workflows/cicd_comp_initialize-phase.yml index cb813169f24b..314b9c638235 100644 --- a/.github/workflows/cicd_comp_initialize-phase.yml +++ b/.github/workflows/cicd_comp_initialize-phase.yml @@ -164,7 +164,7 @@ jobs: # test_filters: Subset of build_test_filters affected by CICD_SKIP_TESTS # ============================================================ build_test_filters="frontend cli backend build jvm_unit_test" - info_filters="sdk_libs documentation cicd" + info_filters="sdk_libs examples documentation cicd" test_filters="frontend cli backend jvm_unit_test" declare -A results From 593f85245c3bd7ae30f01a6da92676829a58796d Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 10:52:05 -0500 Subject: [PATCH 09/15] Resolve LTS pin: 26.9.3-1 verified against a real server, not 1.2.0 Developer scaffolded examples/nextjs from release-25.07.10_lts_v12 against a real 25.07.10 LTS server and found the then-current "latest" pin (resolving to 26.9.3-1) rendered correctly, including inside UVE -- no FieldUndefined errors. 1.2.0 was never independently verified and is now known to be far behind the SDK's current feature set. Backport PR: https://github.com/dotCMS/core/pull/37475 Co-Authored-By: Claude Sonnet 5 --- specs/37399-sdk-packaging-version-fix/spec.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md index d87d481a130f..a69201dc498d 100644 --- a/specs/37399-sdk-packaging-version-fix/spec.md +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -131,7 +131,22 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) ## Assumptions -- The LTS-compatible pin for `release-25.07.10_lts_v12`/`_v16`'s `examples/nextjs` is assumed to be `1.2.0` (the newest release the original issue's author identified as validating against that LTS server's GraphQL schema). **Resolution (developer decision)**: this has not been independently re-verified against a live 25.07.10 LTS server in this investigation, and will not be taken as given — `/speckit-plan`/`/speckit-implement` MUST confirm `1.2.0` (or identify the correct version) by actually scaffolding `examples/nextjs` and rendering a page against a real 25.07.10 LTS server before it is committed as the LTS example's pin. This confirmation step is now folded into AC-005's verification method, not left as an open question blocking this spec. +- **Resolved during implementation (2026-09-09)**: the LTS-compatible pin for + `release-25.07.10_lts_v12`'s `examples/nextjs` is **`26.9.3-1`**, not the `1.2.0` this spec + originally assumed. The developer scaffolded the example (in a separate `git worktree`, not + the feature branch) against a real 25.07.10 LTS server using the then-current `"latest"` pin + and found it rendered correctly — including inside UVE — with no `FieldUndefined` errors, + installing `26.9.3-1` (confirmed via the installed tarball's still-malformed `26.09.03-01`, + independent live evidence of Defect A). `1.2.0` was never independently tested and is now + known to be far behind the SDK's current feature set; `26.9.3-1` is preferred because it is + developer-verified-working today, not a guess. Likely explanation for why `"latest"` no longer + reproduces the original failure: this LTS branch has received schema-compatible patches since + the original issue was filed (2026-08-05) that closed the specific field gap. This does **not** + change the conclusion that an exact pin is still required (a floating `"latest"` could break + again on some future SDK release) — only which version to pin. Backport PR: + [dotCMS/core#37475](https://github.com/dotCMS/core/pull/37475) against + `release-25.07.10_lts_v12`. `release-25.07.10_lts_v16` must be verified independently — not + assumed identical — before its own backport PR is opened. - `main`'s six examples deliberately keep using the floating `"latest"` npm dist-tag (not an exact pin) going forward — confirmed with the developer as an intentional decision, grounded in ADR-0019's own Evergreen-convergence reasoning (see Scope of Investigation): a customer scaffolding from `main` is expected to be on dotCMS Evergreen (always the current release), for whom `"latest"` and "the version matching my CMS" are the same thing by construction. A customer on an older/non-Evergreen server is documented (Fix Scope) as needing to manually pin the version matching their own instance. This is the one deliberate exception to "no floating `@dotcms/*` specifiers" elsewhere in this spec, and the CI guardrail (AC-007) must not flag it. - No separate GitHub issue/spec is being opened for the "keep the LTS example pin in sync with newer compatible releases over time" concern; it is accepted as a manual, as-needed process step, guarded only against the floating-specifier regression (see Non-Goals). This does not apply to `main`'s examples, which cannot go stale since they float by design. - Keeping the standalone `next`-tag publish workflow (`cicd_3-trunk.yml`) active, rather than retiring/disabling it as ADR-0019's Implementation Notes suggest, is a decision the team already made independently of this issue — internal developers need it for testing. This spec does not revisit that decision; it is explicitly out of scope (see Non-Goals). From 397d2d45958e35d54cf9a276e9601a9923e50e36 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 11:07:16 -0500 Subject: [PATCH 10/15] Add root-cause fix: auto-pin examples at release-branch-cut time (AC-010) Developer correctly objected that manually re-pinning the two existing LTS branches doesn't stop the *next* release branch (LTS or not) from being cut with the same floating-example defect -- main's examples deliberately float on "latest" by design, so any freshly-cut branch inherits that. Root-cause fix: cicd_comp_release-prepare-phase.yml (the workflow that cuts every release branch) now pins every example's @dotcms/* dependency to that release's own exact, normalized version at cut time -- correct by construction per ADR-0019's date-lockstep guarantee, no per-branch manual verification needed going forward. Adds AC-010, updates Defect B3's root-cause analysis, narrows the "no ongoing re-sync" non-goal to only cover already-existing branches (the new step handles every future one automatically), and updates Blast Radius / Verification method for the second high-blast-radius workflow this fix now touches. Co-Authored-By: Claude Sonnet 5 --- specs/37399-sdk-packaging-version-fix/spec.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md index a69201dc498d..ad2e22c46952 100644 --- a/specs/37399-sdk-packaging-version-fix/spec.md +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -76,6 +76,8 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) **Defect B3**: The LTS branches' own `examples/nextjs` copies point to `"latest"` instead of a version compatible with that LTS server, and nothing has ever back-filled them with a correct, fixed pin. Unlike `main` — where floating matches the Evergreen audience the example targets — floating is never correct here, since these branch-specific copies exist precisely to demonstrate against a fixed, older server (this is what broke the live customer ticket, Freshdesk #38677). +**Defect B3, systemic root cause**: the reason `examples/nextjs` on `release-25.07.10_lts_v12`/`_v16` was never fixed is that **nothing pins any release branch's example apps at the moment that branch is created.** `cicd_comp_release-prepare-phase.yml` cuts every release branch (LTS and non-LTS alike) and bumps several other versioned artifacts on it (`.mvn/maven.config`, the LICENSE change date, conditionally `MinSdkVersion.VALUE`) — but never touches `examples/*/package.json`. A release branch is therefore always born with whatever floating specifier `main` happened to have at that moment, and nothing ever corrects it afterward. Manually re-pinning today's two existing LTS branches (see Fix Scope) does not fix this — the *next* LTS (or any regular release) branch cut from `main` would be born with the exact same defect, since `main`'s examples deliberately float on `"latest"` by design (Defect B2's fix). This was surfaced by the developer during implementation, not anticipated in the original issue. + **Downstream, out-of-scope-by-design**: The server↔SDK minimum-version compatibility check (`MinSdkVersion.java`, `SdkVersionWebInterceptor.java`, `core-web/libs/sdk/client/src/lib/utils/sdk-compatibility.ts`) sources its own notion of "the SDK's version" (`SDK_VERSION`) from the same `package.json.version` field Defect A corrupts. Its comparison logic (`compareVersions`/`parseVersionSegments`, splitting on `[.-]` and using JS `Number()` per segment) was independently verified to already treat `"26.08.03-01"` and `"26.8.3-1"` as equal (`Number("08")` is `8` in JS string-to-number conversion, not octal). This mechanism therefore has no bug to fix and is automatically corrected once Defect A's normalization lands — it is named here only so the plan phase does not re-open it, and so a regression test can lock in that this remains true. ## Fix Scope & Non-Goals *(mandatory)* @@ -90,7 +92,8 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - Retire `core-web/bump-sdk-versions.js` (dead code, superseded by `deploy-javascript-sdk/action.yml`'s rewrite logic). - Change `examples/nextjs`, `examples/vuejs`, and `examples/nextjs-experiments` on `main` from `"next"` to `"latest"`, matching `examples/angular`/`examples/astro`/`examples/angular-ssr` (already `"latest"`). All six `main` examples deliberately track the floating `"latest"` npm dist-tag going forward — not an exact pin. Rationale: `main`'s examples target dotCMS Evergreen customers, who are always on the current release; `"latest"` converges with "the version matching my CMS" for that audience by construction (see Scope of Investigation's Evergreen note), and never goes stale the way a one-time exact pin would. - Add a short, prominent note to each of `main`'s six example READMEs: a customer whose dotCMS instance is **not** on Evergreen/the current release (self-hosted on an older version, or LTS) must manually replace the `@dotcms/*` version in that example's `package.json` with the version matching their own dotCMS instance, and a one-line pointer to how to find that version. This is the documented, customer-side mitigation for the version-skew case `"latest"` doesn't handle on `main`. -- Fix `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` to an exact, LTS-compatible pinned version (never a floating tag) — these copies exist specifically to demonstrate against that LTS server, not Evergreen, so `"latest"` is wrong here regardless of the `main` rationale above. This is what resolves the live customer ticket (Freshdesk #38677). +- Fix `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` to an exact, LTS-compatible pinned version (never a floating tag) — these copies exist specifically to demonstrate against that LTS server, not Evergreen, so `"latest"` is wrong here regardless of the `main` rationale above. This is what resolves the live customer ticket (Freshdesk #38677). This is a one-time, manual retrofit for these two already-existing branches — see the next bullet for the root-cause fix that keeps it from recurring. +- **Root-cause fix (scope added during implementation)**: add a step to `cicd_comp_release-prepare-phase.yml` — the workflow that cuts every release branch, LTS and non-LTS alike — that pins every `examples/*/package.json`'s `@dotcms/*` dependencies to that release's exact, normalized SDK version (the same leading-zero-stripped form Defect A's fix produces), committed and pushed to the new branch before it's used for anything. This is correct by construction, with no runtime verification needed at cut time: ADR-0019's date-lockstep guarantee means the SDK version matching a given release is known with certainty the moment the branch exists. `main` is never touched by this step and keeps floating on `"latest"` (Defect B2). This closes Defect B3's systemic root cause — no future release branch (LTS or not) can be born with a floating example pin again, without needing anyone to remember a manual fix. - Add a CI check that fails the build if: - any `core-web/libs/sdk/*/package.json` declares a floating specifier (`latest`, `next`, `*`) for an `@dotcms/*` entry in `peerDependencies` — must be real semver (the `"0.0.0"` sentinel today); `"latest"` in `dependencies`/`devDependencies` of these same files is **not** flagged (intentional, masked at publish — see In Scope); - `@dotcms/client` or `@dotcms/uve` reappears in the `dependencies` field of `react`, `angular`, `vue`, or `analytics` (regression guard on the B1b structural fix — they belong in `peerDependencies`); @@ -105,13 +108,13 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - No change to Angular/React UI components. - **Making `main`'s example apps work against an old/non-Evergreen dotCMS server via code/CI is explicitly out of scope.** `main`'s six examples deliberately track `"latest"` for the Evergreen audience they target (see Fix Scope). A customer on an older/LTS server who scaffolds `main`'s example and hits a version-skew failure is expected to consult the documented note (see Fix Scope) and manually pin the version matching their own instance — this is a deliberate, documented customer-side responsibility, not something this fix can solve with a static pin (a pin set once would itself go stale relative to Evergreen, recreating the same class of problem). A customer on an old/LTS server who wants a working example **without** manual adjustment should scaffold from their own server's release/LTS branch instead — which is exactly why the LTS branches carry their own dedicated, separately-pinned example copies (see In Scope). - **Full adoption of ADR-0019 is out of scope for this fix**, beyond the specific points above. ADR-0019 additionally calls for: retiring/disabling the standalone SDK publish workflow (`cicd_3-trunk.yml`'s `publish-sdk-next` job, the `next` npm dist-tag) and folding SDK publishing entirely into the dotCMS release pipeline; bidirectional coupling (an SDK change triggers a new dotCMS release); and a redesigned runtime compatibility check reading `/api/v1/appconfiguration` (replacing the current `MinSdkVersion.java`/`SdkVersionWebInterceptor.java`/`sdk-compatibility.ts` header-based mechanism). None of that is undertaken here — it is a separate, larger migration. Specifically on the `next` tag: the team has already decided, independently of this issue, to keep it active because internal developers need it for testing — ADR-0019 itself anticipates and allows exactly this ("keep a non-version-publishing subset of the workflow for them"), so this is not a new, unjustified deviation. -- No automated, ongoing mechanism to keep the **LTS branches'** example pin in sync with newer compatible releases (e.g. a pipeline step that auto-commits a newer LTS-compatible version over time). The developer explicitly chose the simpler CI-guardrail-only approach over auto-commit, given the risk of unattended commits to protected/LTS branches. Keeping the LTS pin current is a manual, as-needed responsibility; the CI check only guards against it regressing to a *floating* specifier, not against it becoming *stale but still pinned*. (This staleness concern does not apply to `main`'s six examples — they intentionally use the floating `"latest"` tag precisely so they never go stale.) +- **No ongoing re-sync of an *already-existing* release branch's example pin** (e.g. a scheduled job that bumps a live LTS branch's pin to a newer compatible release months after that branch was cut). The new release-prepare-phase step (see In Scope) only ever pins a branch's examples once, automatically, at the moment that branch is created — it does not revisit a branch afterward. Keeping an already-cut branch's pin current, if it should ever move to a *newer* compatible version after the fact (as opposed to the version it was correctly born with), remains a manual, as-needed responsibility; the CI check (both `cicd_1-pr.yml` and `cicd_5-lts.yml`) only guards against a pin regressing to a *floating* specifier, not against a correctly-pinned version becoming stale over time. (This is a narrower non-goal than originally scoped: the developer's initial concern — every future LTS branch repeating today's manual fix — is now resolved by the automated at-cut-time pin, not left as a recurring manual task.) - No retroactive fix of already-published `1.x` npm packages (npm immutability makes this impossible); support-docs guidance for customers stuck on those versions is tracked separately per the original issue's own note (`overrides`/`resolutions`/`pnpm-workspace.yaml` guidance), not as engineering work here. - No change to the `next` dist-tag publishing mechanism itself (`cicd_3-trunk.yml`'s `publish-sdk-next` job) — it was already restored by a prior, unrelated PR (#36722) and is confirmed live/current as of this writing; only the example apps' *pinning* of `next` is in scope. ## Regression Risk *(mandatory)* -- **Blast radius**: `deploy-javascript-sdk/action.yml` is the single publish path for **every** future dotCMS release's SDK packages (both `latest` real releases and `next` dev/QA builds off `main`). A defect introduced in the normalization step would affect every subsequent SDK publish, not just this fix's target versions. This is the highest-risk piece of the change and needs a dry-run/manual-dispatch verification (the workflow already supports `dry-run: true`) before trusting it against a real release. +- **Blast radius**: `deploy-javascript-sdk/action.yml` is the single publish path for **every** future dotCMS release's SDK packages (both `latest` real releases and `next` dev/QA builds off `main`). A defect introduced in the normalization step would affect every subsequent SDK publish, not just this fix's target versions. This is the highest-risk piece of the change and needs a dry-run/manual-dispatch verification (the workflow already supports `dry-run: true`) before trusting it against a real release. `cicd_comp_release-prepare-phase.yml` (the new example-pinning step, AC-010) is a second high-blast-radius surface for the same reason: it is the single branch-cutting path for **every** future release, LTS and non-LTS alike. Its new step must be tested against a local reproduction (not a real release dispatch) before being trusted, the same way Defect A's normalization step was. - **Backward compatibility**: Already-published `1.x`/pre-fix versions cannot be changed (npm immutability) — this fix is forward-only. The normalized version string must match ADR-0019's literal rule (leading zeros dropped, e.g. `26.8.3-1`) exactly, not just its general date-lockstep intent, so existing tooling that parses the release tag format continues to work; the plan phase must confirm this against the ADR text directly. Separately, moving `@dotcms/client`/`@dotcms/uve` from `dependencies` to `peerDependencies` (Defect B1b) is a genuine behavior change for any customer on **yarn classic (1.x) or npm below v7** who today installs only `@dotcms/react`/`@dotcms/angular`/`@dotcms/vue` and relies on the sibling package coming along automatically — after this fix, those tools will not auto-install that peer, and the customer's app breaks with a missing-module error unless they explicitly add it. This must be called out prominently in release notes and in each SDK's README (see Fix Scope), not just silently shipped. - **Data considerations**: None (no persisted application data; this is packaging/config only). @@ -123,11 +126,12 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - **AC-003b**: `@dotcms/client` and `@dotcms/uve` are declared in `peerDependencies`, not `dependencies`, in `react`, `angular`, `vue`, and `analytics`; installing a pinned version of any of these four under yarn or pnpm no longer produces a second, independently-resolved copy of `@dotcms/client`/`@dotcms/uve` nested in that package's own `node_modules`. - **AC-004**: `examples/nextjs`, `examples/vuejs`, and `examples/nextjs-experiments` on `main` point to `"latest"` (no longer `"next"`), matching `examples/angular`/`examples/astro`/`examples/angular-ssr`; scaffolding each of the six, installing, and running `npm ls @dotcms/client` yields exactly one copy, resolved from the `latest` npm dist-tag (never `next`, never a stale pin). Each example's README carries the documented note (see Fix Scope) for non-Evergreen customers. - **AC-005**: `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` pins a version verified compatible with that LTS server's GraphQL schema; scaffolding and rendering a page against a 25.07.10 LTS server produces no `FieldUndefined` errors. +- **AC-010**: Cutting a new release branch (via `cicd_comp_release-prepare-phase.yml`, either LTS or non-LTS format) results in every `examples/*/package.json`'s `@dotcms/*` dependencies being pinned to that release's exact, normalized version — never `"latest"`/`"next"`/`"*"` — with no manual step required. `main` is unaffected and continues to float on `"latest"`. - **AC-006**: Installing a pinned `@dotcms/react` under npm 7+ and pnpm each yields exactly one `@dotcms/client` at the pinned version (no silent override). Under yarn classic (1.x) or npm below v7, installing `@dotcms/react` **alone** (without also explicitly installing `@dotcms/client`) is expected to warn about an unmet peer dependency (or fail with a missing-module error at runtime) rather than silently install a mismatched copy — this is the documented behavior change (see AC-003b, Fix Scope). - **AC-009**: `react/README.md`, `angular/README.md`, and `vue/README.md`'s Installation sections no longer claim that installing the main package automatically installs its required dependencies; each explicitly instructs the consumer to also install `@dotcms/client`, `@dotcms/uve`, and `@dotcms/types`, and names yarn classic (1.x) and npm below v7 as the package managers that require this manual step. - **AC-007**: A CI run fails the build if: any `core-web/libs/sdk/*/package.json` declares `latest`/`next`/`*` for an `@dotcms/*` entry in `peerDependencies`; `@dotcms/client` or `@dotcms/uve` reappears in the `dependencies` field of `react`/`angular`/`vue`/`analytics`; any `examples/*/package.json` on any branch declares `next` or `*`; or any `examples/*/package.json` declares `latest` on a non-`main` branch. A CI run must NOT fail for `latest` in `dependencies`/`devDependencies` of the SDK libs, nor for `latest` in `examples/*/package.json` on `main` — both intentional (AC-003, AC-004). - **AC-008**: `compareVersions()` in `sdk-compatibility.ts` continues to treat a zero-padded and a normalized form of the same version as equal (regression test, not new behavior). -- **Verification method**: A dry-run (`workflow_dispatch` with `dry-run: true`) of the updated `cicd_release-sdk.yml`/`deploy-javascript-sdk` action against a scratch/test scope to confirm the written `package.json` contents before any real publish is risked; a new Jest unit test for the version-normalization logic and for `compareVersions()` (AC-008); manual scaffold-and-install verification per AC-003b/AC-004/AC-005/AC-006 across npm/yarn/pnpm; a new CI job/script test for AC-007 (e.g. run the guardrail check against fixture `package.json` files that intentionally contain a floating `peerDependencies` value or a reintroduced `dependencies` entry, and confirm each fails); a manual read-through of the three corrected READMEs for AC-009. +- **Verification method**: A dry-run (`workflow_dispatch` with `dry-run: true`) of the updated `cicd_release-sdk.yml`/`deploy-javascript-sdk` action against a scratch/test scope to confirm the written `package.json` contents before any real publish is risked; a new Jest unit test for the version-normalization logic and for `compareVersions()` (AC-008); manual scaffold-and-install verification per AC-003b/AC-004/AC-005/AC-006 across npm/yarn/pnpm; a new CI job/script test for AC-007 (e.g. run the guardrail check against fixture `package.json` files that intentionally contain a floating `peerDependencies` value or a reintroduced `dependencies` entry, and confirm each fails); a manual read-through of the three corrected READMEs for AC-009; for AC-010, a local reproduction of `cicd_comp_release-prepare-phase.yml`'s new pinning logic against a scratch copy of the real `examples/*/package.json` files (mirroring how AC-001/AC-002 were verified for Defect A), not a real release dispatch. ## Assumptions @@ -151,3 +155,4 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - No separate GitHub issue/spec is being opened for the "keep the LTS example pin in sync with newer compatible releases over time" concern; it is accepted as a manual, as-needed process step, guarded only against the floating-specifier regression (see Non-Goals). This does not apply to `main`'s examples, which cannot go stale since they float by design. - Keeping the standalone `next`-tag publish workflow (`cicd_3-trunk.yml`) active, rather than retiring/disabling it as ADR-0019's Implementation Notes suggest, is a decision the team already made independently of this issue — internal developers need it for testing. This spec does not revisit that decision; it is explicitly out of scope (see Non-Goals). - The `"0.0.0"` sentinel is deliberately scoped to `peerDependencies` only, not `dependencies`/`devDependencies` — confirmed with the developer. Rationale: customers never build from this repo's source, so a `dependencies`/`devDependencies` value that's already masked by the publish-time rewrite has no customer-facing effect, and changing it buys nothing. `peerDependencies` is different because it's where dotCMS's own developers hit real, local pre-publish testing breakage (an npm/yarn/pnpm peer-dependency-satisfaction check can't evaluate `"latest"` as a semver range), so only that field's source value needs to be a real semver. +- **Scope expanded during implementation (2026-09-09)**: the developer raised, correctly, that manually re-pinning the two existing LTS branches does not prevent every *future* release branch (LTS or not) from being cut with the same floating-example defect, since nothing pins examples at branch-creation time. Rather than accept that recurrence and document a manual step for whoever cuts the next LTS, the automated fix (AC-010) was added to this spec's scope on the spot, reasoning from ADR-0019's date-lockstep guarantee (the correct version is knowable with certainty at cut time, so no per-branch manual verification is needed going forward — only the retrofit of the two pre-existing branches needed one). From 74ba3c1b9b7ef19dc4535c55f1ec43f043b54c71 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 11:08:18 -0500 Subject: [PATCH 11/15] Root-cause fix: pin example app deps at release-branch-cut time (AC-010) Tasks T038-T041 for spec specs/37399-sdk-packaging-version-fix. Developer objection during implementation: manually re-pinning the two existing LTS branches (Phase 3) doesn't stop the *next* release branch (LTS or not) from being cut with the same floating-example defect -- main's examples deliberately float on "latest" by design, so any freshly cut branch inherits that as-is. Nothing in cicd_comp_release-prepare-phase.yml (the workflow that cuts every release branch and bumps several other versioned artifacts on it) ever touched examples/*/package.json. Added a new step there, right after the existing branch-creation step: normalizes the release version (same leading-zero-stripping as Defect A's fix) and pins every examples/*/package.json's @dotcms/* dependency to that exact value, committing and pushing to the new branch -- applies to every release branch unconditionally (LTS and non-LTS), never touches main. Correct by construction per ADR-0019's date-lockstep guarantee: the SDK version matching a given release is known with certainty the moment the branch exists, so no per-branch manual verification is needed going forward (unlike the one-time retrofit Phase 3 does for the two branches that predate this step). Verified locally against a scratch copy of the real six examples/* manifests (not a real release-branch cut, for the same reason Defect A's dry-run was replaced with a local reproduction): confirms Red (nothing currently touches examples/) and Green (all six correctly pinned, non-@dotcms/* dependencies untouched, and the shared shape validator passes against the result). Co-Authored-By: Claude Sonnet 5 --- .../cicd_comp_release-prepare-phase.yml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/.github/workflows/cicd_comp_release-prepare-phase.yml b/.github/workflows/cicd_comp_release-prepare-phase.yml index bc8ca4daed30..e936e9e1eb26 100644 --- a/.github/workflows/cicd_comp_release-prepare-phase.yml +++ b/.github/workflows/cicd_comp_release-prepare-phase.yml @@ -248,6 +248,57 @@ jobs: release_commit=$(git log -1 --pretty=%H) echo "release_commit=${release_commit}" >> "$GITHUB_OUTPUT" + # Pin every example app's @dotcms/* dependencies to this exact release's SDK + # version, on every release branch (LTS and non-LTS alike) — the root-cause fix + # for examples/nextjs on release-25.07.10_lts_v12/_v16 having shipped with a + # floating "latest" for months with nothing to catch or correct it (see + # dotCMS/core#36891, specs/37399-sdk-packaging-version-fix). ADR-0019's + # date-lockstep versioning means the SDK version matching this exact release is + # known with certainty the moment the branch is cut — no runtime verification + # needed, unlike the one-time manual fix required to retrofit the two LTS + # branches that predate this step. `main` is never touched here and keeps + # floating on "latest" by design (its examples target Evergreen customers). + - name: Pin example app SDK dependencies to the release version + id: pin-examples + env: + RELEASE_VERSION: ${{ steps.set-version.outputs.release_version }} + RELEASE_BRANCH: ${{ steps.set-version.outputs.release_branch }} + run: | + set -euo pipefail + echo "::group::Pin example app SDK dependencies" + + # Same normalization as deploy-javascript-sdk/action.yml (Defect A): the + # release version is zero-padded CalVer (e.g. 26.08.03-01); npm packages + # publish under the leading-zero-stripped, valid-semver form (26.8.3-1). + NORMALIZED_VERSION=$(echo "$RELEASE_VERSION" | awk -F'[.-]' '{ + for (i = 1; i <= NF; i++) { sub(/^0+/, "", $i); if ($i == "") $i = "0" } + out = $1 "." $2 "." $3 + if (NF >= 4) out = out "-" $4 + print out + }') + echo "Normalized release version: ${NORMALIZED_VERSION}" + + CHANGED=false + for pkg_json in $(find examples -maxdepth 2 -name package.json 2>/dev/null); do + for dep in $(jq -r '.dependencies // {} | keys[] | select(startswith("@dotcms/"))' "$pkg_json"); do + jq --arg dep "$dep" --arg v "$NORMALIZED_VERSION" \ + '.dependencies[$dep] = $v' "$pkg_json" > tmp.$$.json && mv tmp.$$.json "$pkg_json" + echo " ↳ ${pkg_json}: dependencies[\"${dep}\"] -> ${NORMALIZED_VERSION}" + CHANGED=true + done + done + + if [ "$CHANGED" = "true" ]; then + git add examples + git commit -m "🏁 Pin example app SDK dependencies to ${NORMALIZED_VERSION}" + git push origin "${RELEASE_BRANCH}" + else + echo "No @dotcms/* dependencies found in any examples/*/package.json on this branch — nothing to pin." + fi + + echo "::endgroup::" + shell: bash + - name: Create GitHub Release run: | curl -X POST \ From 1bb7cc093f5bece50805a8664fbbe97bbd89c7b8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 11:43:15 -0500 Subject: [PATCH 12/15] Correct LTS pin: 1.2.0, not 26.9.3-1 -- static schema check beats live test The prior "verified against a real server" commit was itself based on an invalid test (a Docker container running trunk, not 25.07.10 LTS -- trunk already has every field the query needs, so nothing could have failed). Static, no-server-needed verification instead: release-25.07.10_lts_v12's GraphQL schema lacks lockedBy, lockedByName, numberContents, styleEditorSchemas, and layout metadata; @dotcms/client's query has requested all five since 2025-11-26 through 2026-05-07 (PRs #33905, #34966, #34173, #35528); @dotcms/client@1.2.0 published 2025-10-24, over a month before any of them, with no other stable release in that gap. 1.2.0 requests none of the five fields -- schema-compatible by construction, and this confirms the original issue's own guess rather than replacing it, as the previous commit's message claimed. PR dotCMS/core#37475 corrected to match (both the pin and its description's test-plan claims). AC-005's verification method updated to prefer this static approach over a live-server test, given how easily the latter produces a false pass against the wrong build. Co-Authored-By: Claude Sonnet 5 --- specs/37399-sdk-packaging-version-fix/spec.md | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md index ad2e22c46952..dc3b98188a06 100644 --- a/specs/37399-sdk-packaging-version-fix/spec.md +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -125,7 +125,7 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) - **AC-003**: In each of the seven `core-web/libs/sdk/*/package.json` files (`analytics`, `angular`, `client`, `experiments`, `react`, `uve`, `vue`), every sibling `@dotcms/*` entry in `peerDependencies` uses the `"0.0.0"` sentinel, never `latest`/`next`/`*`. `dependencies`/`devDependencies` entries are unaffected by this AC (they intentionally keep `"latest"` in source — see AC-002's masking and Fix Scope). - **AC-003b**: `@dotcms/client` and `@dotcms/uve` are declared in `peerDependencies`, not `dependencies`, in `react`, `angular`, `vue`, and `analytics`; installing a pinned version of any of these four under yarn or pnpm no longer produces a second, independently-resolved copy of `@dotcms/client`/`@dotcms/uve` nested in that package's own `node_modules`. - **AC-004**: `examples/nextjs`, `examples/vuejs`, and `examples/nextjs-experiments` on `main` point to `"latest"` (no longer `"next"`), matching `examples/angular`/`examples/astro`/`examples/angular-ssr`; scaffolding each of the six, installing, and running `npm ls @dotcms/client` yields exactly one copy, resolved from the `latest` npm dist-tag (never `next`, never a stale pin). Each example's README carries the documented note (see Fix Scope) for non-Evergreen customers. -- **AC-005**: `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` pins a version verified compatible with that LTS server's GraphQL schema; scaffolding and rendering a page against a 25.07.10 LTS server produces no `FieldUndefined` errors. +- **AC-005**: `examples/nextjs` on `release-25.07.10_lts_v12` and `release-25.07.10_lts_v16` pins a version verified compatible with that LTS server's GraphQL schema. **Verification method note (added after `_v12`'s two false starts)**: a live-server scaffold test is only trustworthy against a genuine build of that exact LTS version — a mismatched local Docker image (e.g. `trunk`) will falsely report no `FieldUndefined` errors regardless of SDK version, since it always has every field. The **static** method that actually caught this — comparing the SDK's hardcoded query fields (`core-web/libs/sdk/client/src/lib/client/page/utils.ts`) against the branch's own GraphQL schema source (`dotCMS/src/main/java/com/dotcms/graphql/`) and the field's first-added date/PR via `git log -S` — is the preferred, more reliable verification for this AC; a live-server render is confirmatory, not a substitute, when it can be arranged against a verified-correct build. - **AC-010**: Cutting a new release branch (via `cicd_comp_release-prepare-phase.yml`, either LTS or non-LTS format) results in every `examples/*/package.json`'s `@dotcms/*` dependencies being pinned to that release's exact, normalized version — never `"latest"`/`"next"`/`"*"` — with no manual step required. `main` is unaffected and continues to float on `"latest"`. - **AC-006**: Installing a pinned `@dotcms/react` under npm 7+ and pnpm each yields exactly one `@dotcms/client` at the pinned version (no silent override). Under yarn classic (1.x) or npm below v7, installing `@dotcms/react` **alone** (without also explicitly installing `@dotcms/client`) is expected to warn about an unmet peer dependency (or fail with a missing-module error at runtime) rather than silently install a mismatched copy — this is the documented behavior change (see AC-003b, Fix Scope). - **AC-009**: `react/README.md`, `angular/README.md`, and `vue/README.md`'s Installation sections no longer claim that installing the main package automatically installs its required dependencies; each explicitly instructs the consumer to also install `@dotcms/client`, `@dotcms/uve`, and `@dotcms/types`, and names yarn classic (1.x) and npm below v7 as the package managers that require this manual step. @@ -135,22 +135,37 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) ## Assumptions -- **Resolved during implementation (2026-09-09)**: the LTS-compatible pin for - `release-25.07.10_lts_v12`'s `examples/nextjs` is **`26.9.3-1`**, not the `1.2.0` this spec - originally assumed. The developer scaffolded the example (in a separate `git worktree`, not - the feature branch) against a real 25.07.10 LTS server using the then-current `"latest"` pin - and found it rendered correctly — including inside UVE — with no `FieldUndefined` errors, - installing `26.9.3-1` (confirmed via the installed tarball's still-malformed `26.09.03-01`, - independent live evidence of Defect A). `1.2.0` was never independently tested and is now - known to be far behind the SDK's current feature set; `26.9.3-1` is preferred because it is - developer-verified-working today, not a guess. Likely explanation for why `"latest"` no longer - reproduces the original failure: this LTS branch has received schema-compatible patches since - the original issue was filed (2026-08-05) that closed the specific field gap. This does **not** - change the conclusion that an exact pin is still required (a floating `"latest"` could break - again on some future SDK release) — only which version to pin. Backport PR: - [dotCMS/core#37475](https://github.com/dotCMS/core/pull/37475) against - `release-25.07.10_lts_v12`. `release-25.07.10_lts_v16` must be verified independently — not - assumed identical — before its own backport PR is opened. +- **Resolved during implementation (2026-09-09), corrected same day**: the LTS-compatible pin + for `release-25.07.10_lts_v12`'s `examples/nextjs` is **`1.2.0`** — confirming, not + overturning, this spec's original assumption. Two prior attempts at this line were wrong and + are recorded here for the trail: + 1. A live scaffold test against a locally-run Docker container reported no `FieldUndefined` + errors with the then-current `"latest"` pin (resolving to `26.9.3-1`), so `26.9.3-1` was + pinned instead of `1.2.0`. **This test was invalid**: the container was running + `dotcms/dotcms:trunk`, not a real 25.07.10 LTS build — trunk already has every field the + SDK's query needs, so the test could not have failed regardless of the SDK version used. + 2. Once the Docker mismatch surfaced, the correct version was instead determined **statically, + without a running server**: `release-25.07.10_lts_v12`'s GraphQL schema + (`PageAPIGraphQLTypesProvider.java` and related files) does not define `lockedBy`, + `lockedByName`, `numberContents`, `styleEditorSchemas`, or a layout `metadata` field, but + `@dotcms/client`'s `DotCMSPage` query has requested all five unconditionally since + 2025-11-26 through 2026-05-07 (added across PRs #33905, #34966, #34173, #35528 — traced via + `git log -S` on `core-web/libs/sdk/client/src/lib/client/page/utils.ts`). `26.9.3-1` + was published long after all four dates, so it requests fields this schema lacks — + confirming it would in fact `FieldUndefined` against a real LTS server, i.e. attempt 1's + "no failure" result was an artifact of testing against the wrong server, not evidence + `26.9.3-1` is actually compatible. `@dotcms/client@1.2.0` published 2025-10-24 (per npm + registry metadata), over a month before the first field addition, with no other stable + version published in that gap (only `1.2.0-next.*` prereleases) — its query requests none + of the five fields, making it schema-compatible with this branch by construction, and + confirmed published for all five `@dotcms/*` packages this example depends on. + This does **not** change the conclusion that an exact pin is still required (a floating + `"latest"` could break again on some future SDK release) — only which version to pin, twice. + Backport PR: [dotCMS/core#37475](https://github.com/dotCMS/core/pull/37475) against + `release-25.07.10_lts_v12` (description corrected to match). `release-25.07.10_lts_v16` must + be verified independently — not assumed identical — before its own backport PR is opened; + given the unreliability of a live-server test exposed here, prefer the same static + schema-inspection method over attempting another Docker-based live test. - `main`'s six examples deliberately keep using the floating `"latest"` npm dist-tag (not an exact pin) going forward — confirmed with the developer as an intentional decision, grounded in ADR-0019's own Evergreen-convergence reasoning (see Scope of Investigation): a customer scaffolding from `main` is expected to be on dotCMS Evergreen (always the current release), for whom `"latest"` and "the version matching my CMS" are the same thing by construction. A customer on an older/non-Evergreen server is documented (Fix Scope) as needing to manually pin the version matching their own instance. This is the one deliberate exception to "no floating `@dotcms/*` specifiers" elsewhere in this spec, and the CI guardrail (AC-007) must not flag it. - No separate GitHub issue/spec is being opened for the "keep the LTS example pin in sync with newer compatible releases over time" concern; it is accepted as a manual, as-needed process step, guarded only against the floating-specifier regression (see Non-Goals). This does not apply to `main`'s examples, which cannot go stale since they float by design. - Keeping the standalone `next`-tag publish workflow (`cicd_3-trunk.yml`) active, rather than retiring/disabling it as ADR-0019's Implementation Notes suggest, is a decision the team already made independently of this issue — internal developers need it for testing. This spec does not revisit that decision; it is explicitly out of scope (see Non-Goals). From 6eb36c41bb1ec8fb03d98ef3c413df57c8286c9d Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 11:47:36 -0500 Subject: [PATCH 13/15] Close out Defect B3: _v16 backport verified and opened (PR #37476) Verified independently, not assumed identical to _v12 -- same static method (checked all 80 files under dotCMS/src/main/java/com/dotcms/graphql/ on release-25.07.10_lts_v16) confirms the same schema gap, so 1.2.0 applies there too. Both LTS backports (#37475, #37476) are now open. Co-Authored-By: Claude Sonnet 5 --- specs/37399-sdk-packaging-version-fix/spec.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/specs/37399-sdk-packaging-version-fix/spec.md b/specs/37399-sdk-packaging-version-fix/spec.md index dc3b98188a06..852a380e2a77 100644 --- a/specs/37399-sdk-packaging-version-fix/spec.md +++ b/specs/37399-sdk-packaging-version-fix/spec.md @@ -162,10 +162,12 @@ The `@dotcms/*` npm SDK packaging/release mechanism (introduced a few weeks ago) This does **not** change the conclusion that an exact pin is still required (a floating `"latest"` could break again on some future SDK release) — only which version to pin, twice. Backport PR: [dotCMS/core#37475](https://github.com/dotCMS/core/pull/37475) against - `release-25.07.10_lts_v12` (description corrected to match). `release-25.07.10_lts_v16` must - be verified independently — not assumed identical — before its own backport PR is opened; - given the unreliability of a live-server test exposed here, prefer the same static - schema-inspection method over attempting another Docker-based live test. + `release-25.07.10_lts_v12` (description corrected to match). **`release-25.07.10_lts_v16` + resolved the same day, independently verified rather than assumed identical**: the same static + method (all 80 files under `dotCMS/src/main/java/com/dotcms/graphql/` on that branch) found + the identical gap — none of the five fields defined — so `1.2.0` applies there too, for the + same reason. Backport PR: [dotCMS/core#37476](https://github.com/dotCMS/core/pull/37476) + against `release-25.07.10_lts_v16`. Both LTS backports are now complete. - `main`'s six examples deliberately keep using the floating `"latest"` npm dist-tag (not an exact pin) going forward — confirmed with the developer as an intentional decision, grounded in ADR-0019's own Evergreen-convergence reasoning (see Scope of Investigation): a customer scaffolding from `main` is expected to be on dotCMS Evergreen (always the current release), for whom `"latest"` and "the version matching my CMS" are the same thing by construction. A customer on an older/non-Evergreen server is documented (Fix Scope) as needing to manually pin the version matching their own instance. This is the one deliberate exception to "no floating `@dotcms/*` specifiers" elsewhere in this spec, and the CI guardrail (AC-007) must not flag it. - No separate GitHub issue/spec is being opened for the "keep the LTS example pin in sync with newer compatible releases over time" concern; it is accepted as a manual, as-needed process step, guarded only against the floating-specifier regression (see Non-Goals). This does not apply to `main`'s examples, which cannot go stale since they float by design. - Keeping the standalone `next`-tag publish workflow (`cicd_3-trunk.yml`) active, rather than retiring/disabling it as ADR-0019's Implementation Notes suggest, is a decision the team already made independently of this issue — internal developers need it for testing. This spec does not revisit that decision; it is explicitly out of scope (see Non-Goals). From 2047c5ec79cb5026247b64d37c6dc21ec7d403e9 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 9 Sep 2026 12:00:17 -0500 Subject: [PATCH 14/15] Commit the verified package.json shape contract to PR2 data-model.md was a pure N/A placeholder with no content; dropped. contracts/package-json-shape.md documents the shape the CI guardrail enforces, updated to reflect the LTS pins as verified (1.2.0, PRs #37475/#37476) rather than pending. Co-Authored-By: Claude Sonnet 5 --- .../contracts/package-json-shape.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md diff --git a/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md b/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md new file mode 100644 index 000000000000..c9ab2eab4ae0 --- /dev/null +++ b/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md @@ -0,0 +1,46 @@ +# Contract: `package.json` shape after this fix + +The "interface" this fix changes is the shape of published `package.json` manifests and example +pins — what every `npm install` reads. This is the durable contract other devs/tools should be +able to check the built result against. + +## SDK libraries — `core-web/libs/sdk/*/package.json` (source, pre-publish) + +| Package | `dependencies` (`@dotcms/*`) | `peerDependencies` (`@dotcms/*`) | `devDependencies` (`@dotcms/*`) | +|---|---|---|---| +| `analytics` | — (moved out) | `@dotcms/uve: "0.0.0"` | `@dotcms/types: "latest"` (unchanged) | +| `angular` | — (moved out) | `@dotcms/client: "0.0.0"`, `@dotcms/uve: "0.0.0"` | `@dotcms/types: "latest"` (unchanged) | +| `client` | — | — | `@dotcms/types: "latest"` (unchanged) | +| `experiments` | — | `@dotcms/client: "0.0.0"`, `@dotcms/react: "0.0.0"`, `@dotcms/uve: "0.0.0"`, `@dotcms/types: "0.0.0"` | — | +| `react` | — (moved out) | `@dotcms/client: "0.0.0"`, `@dotcms/uve: "0.0.0"` | `@dotcms/types: "latest"` (unchanged) | +| `uve` | — | — | `@dotcms/types: "latest"` (unchanged) | +| `vue` | — (moved out) | `@dotcms/client: "0.0.0"`, `@dotcms/uve: "0.0.0"` | `@dotcms/types: "latest"` (unchanged) | + +Non-`@dotcms/*` dependencies (`@tinymce/*`, `react`/`react-dom`, `@angular/*`, `rxjs`, etc.) are +untouched by this fix. + +## SDK libraries — published tarball (post-publish, at release version `X`) + +Every `@dotcms/*` entry in `dependencies`, `peerDependencies`, **and `devDependencies`** (all +three, once the rewrite loop is extended per research.md §3) is rewritten to the exact normalized +release version `X` (e.g. `26.8.3-1`, never `26.08.03-01` or `latest`). The package's own +`version` field is also `X`. A strict (non-loose) semver parse of every one of these strings must +succeed. + +## Example apps — `examples/*/package.json` + +| Location | `@dotcms/*` pin | Notes | +|---|---|---| +| `examples/nextjs` on `main` | `"latest"` | changed from `"next"` | +| `examples/vuejs` on `main` | `"latest"` | changed from `"next"` | +| `examples/angular` on `main` | `"latest"` | unchanged (already correct) | +| `examples/astro` on `main` | `"latest"` | unchanged (already correct) | +| `examples/nextjs` on `release-25.07.10_lts_v12` | `"1.2.0"` | changed from `"latest"`; verified via static GraphQL schema analysis (PR #37475) | +| `examples/nextjs` on `release-25.07.10_lts_v16` | `"1.2.0"` | changed from `"latest"`; verified via static GraphQL schema analysis (PR #37476) | + +## CI guardrail (the enforcement of this contract) + +A new check in `cicd_1-pr.yml` fails the build if any file above is edited to violate this table +— specifically: a floating (`latest`/`next`/`*`) `peerDependencies` entry in any SDK lib; a +reintroduced `@dotcms/client`/`@dotcms/uve` in `dependencies` of `react`/`angular`/`vue`/`analytics`; +or a floating example pin anywhere except `"latest"` on `main`. From dc1ac042207d54b374f30b35b56039c5896dbfa7 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Sep 2026 08:56:13 -0500 Subject: [PATCH 15/15] fix(cicd): address CI/CD review on SDK packaging fix (#36891) Resolves the CHANGES_REQUESTED review on PR #37477. The three blocking findings made the SDK release pipeline deterministically broken. deploy-javascript-sdk/action.yml - Restore the sibling-name resolution deleted in the peerDependencies commit. `$dep_name` was still referenced by the echo at line 104 with no assignment left, and under `set -euo pipefail` that aborted the step on the first package carrying a sibling dep -- every SDK release, always. Resolution is now hoisted into an `sdk_package_names` array built once, instead of a jq call per package per field. - Stop assuming the @dotcms/ scope. libs/sdk/cli publishes as the UNSCOPED `dotcms`, and hardcoding the scope reverts a fix the same file documents 100 lines below; a sibling depending on it would have shipped a literal "latest" (Defect B1). - Normalize versions by walking segments instead of rebuilding from $1..$4. The old form truncated the `-next.` suffix that cicd_3-trunk.yml appends (26.9.3-1-next.2632 -> 26.9.3-1), which would have made every `next` publish collide with the stable release version. Verified byte-identical on release-shaped inputs under mawk (the ubuntu-24.04 default), gawk and gawk --posix. - Write example pins exact, without the caret. The pipeline must not emit a shape its own guardrail rejects. cicd_comp_release-prepare-phase.yml - Fold the example pin into the branch-cut commit and drop the separate pin-examples step. As a separate commit+push it left the release tag's target_commitish pointing at a pre-pin tree, re-triggered cicd_5-lts.yml (cancelling the run the first push started), and could abort on "nothing to commit" when the pin was a no-op. - Preserve each manifest's indentation. jq reprints the whole document, so without this a four-value pin landed as a 121-line whole-file whitespace diff; examples/ mixes 2- and 4-space manifests. validate-sdk-package-shapes - Treat master as trunk: cicd_1-pr.yml accepts PRs to main or master, and the validator only allowed main. - Reject ranges on release branches. AC-005/AC-010 require an exact pin, and ^/~/>=/1.2.x drift forward just as "latest" does. Implemented as an allow-list of exact versions rather than a list of operators, so unanticipated range syntax cannot slip through. - 11 new tests (21 total). cicd_1-pr.yml / cicd_5-lts.yml - Run the guardrail's own tests. Without `npm test` a broken validator was a green build. - New sdk_package_shapes filter so editing the validator triggers the job that runs it. Kept separate from sdk_libs, which also gates the trunk npm publish. - permissions: contents: read, timeout-minutes, npm cache, and `npm ci --ignore-scripts` -- on cicd_1-pr.yml that manifest is PR-controlled and lifecycle scripts would run unmerged code. - Iterate find output with while-read (actionlint SC2044). contracts/package-json-shape.md - Record that libs/sdk holds eleven directories, that the other four declare no @dotcms/* dependency, and that cli publishes unscoped. Co-Authored-By: Claude Opus 5 (1M context) --- .../deploy-javascript-sdk/action.yml | 50 ++++++-- .github/filters.yaml | 8 ++ .../src/validate.test.ts | 39 +++++++ .../src/validate.ts | 30 ++++- .github/workflows/cicd_1-pr.yml | 33 ++++-- .github/workflows/cicd_5-lts.yml | 26 +++-- .../workflows/cicd_comp_initialize-phase.yml | 2 +- .../cicd_comp_release-prepare-phase.yml | 109 ++++++++++-------- .../contracts/package-json-shape.md | 19 ++- 9 files changed, 229 insertions(+), 87 deletions(-) diff --git a/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml b/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml index f7eb5940decf..9a2fe5fbff59 100644 --- a/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml +++ b/.github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml @@ -73,11 +73,23 @@ runs: # segment before writing it anywhere. This is the single point of normalization; # every jq call below, and the idempotency check in the publish step, must use # this normalized value, not the raw $RELEASE_VERSION input. - RELEASE_VERSION=$(echo "$RELEASE_VERSION" | awk -F'[.-]' '{ - for (i = 1; i <= NF; i++) { sub(/^0+/, "", $i); if ($i == "") $i = "0" } - out = $1 "." $2 "." $3 - if (NF >= 4) out = out "-" $4 - print out + # + # Walk the string segment by segment, PRESERVING the original separators and every + # trailing segment. Splitting on [.-] and rebuilding from only $1..$4 silently + # truncates the `-next.` suffix cicd_3-trunk.yml appends to the version + # it passes in (26.9.3-1-next.2632 -> 26.9.3-1), which would make every `next` + # publish collide with the stable release version. Non-numeric segments (`next`) + # pass through untouched. POSIX awk, no gawk dependency. + RELEASE_VERSION=$(echo "$RELEASE_VERSION" | awk '{ + s = $0; out = "" + while (match(s, /[.-]/)) { + seg = substr(s, 1, RSTART - 1); sep = substr(s, RSTART, 1) + if (seg ~ /^[0-9]+$/) { sub(/^0+/, "", seg); if (seg == "") seg = "0" } + out = out seg sep + s = substr(s, RSTART + 1) + } + if (s ~ /^[0-9]+$/) { sub(/^0+/, "", s); if (s == "") s = "0" } + print out s }') echo "Normalized release version: $RELEASE_VERSION" echo "normalized_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT" @@ -85,6 +97,17 @@ runs: sdk_packages=($(find . -maxdepth 1 -mindepth 1 -type d -exec basename {} \;)) echo "Found SDK packages: ${sdk_packages[*]}" + # Each sibling's OWN published name, resolved once up front. Never + # "@dotcms/": libs/sdk/cli publishes as the UNSCOPED `dotcms`, so a + # hardcoded scope can never match it and would silently leave a literal "latest" in + # a published manifest — Defect B1 all over again. Same reason the publish step + # below reads .name rather than assuming the scope. + sdk_package_names=() + for dep in "${sdk_packages[@]}"; do + sdk_package_names+=("$(jq -r '.name' "$dep/package.json" 2>/dev/null || echo "@dotcms/$dep")") + done + echo "Resolved SDK package names: ${sdk_package_names[*]}" + for sdk in "${sdk_packages[@]}"; do pkg="$sdk/package.json" if [ -f "$pkg" ]; then @@ -96,10 +119,10 @@ runs: # lockstep at the same version, so a range only reintroduces the ambiguity # ADR-0019 removes. devDependencies is included so @dotcms/types (a dev-only # dependency in every SDK lib) no longer ships with an unmasked "latest". - for dep in "${sdk_packages[@]}"; do + for dep_name in "${sdk_package_names[@]}"; do for field in dependencies peerDependencies devDependencies; do - if jq -e ".${field}[\"@dotcms/$dep\"]" "$pkg" >/dev/null 2>&1; then - jq --arg field "$field" --arg dep "@dotcms/$dep" --arg v "$RELEASE_VERSION" \ + if jq -e ".${field}[\"$dep_name\"]" "$pkg" >/dev/null 2>&1; then + jq --arg field "$field" --arg dep "$dep_name" --arg v "$RELEASE_VERSION" \ '.[$field][$dep] = $v' "$pkg" > tmp.$$.json && mv tmp.$$.json "$pkg" echo " ↳ $field $dep_name -> $RELEASE_VERSION" fi @@ -108,12 +131,15 @@ runs: fi done - # Update example projects + # Update example projects. Exact pin, no caret: a `^` range drifts forward exactly + # the way Defect B3 did on the LTS branches, and validate-sdk-package-shapes rejects + # ranges on any non-trunk branch — the release pipeline must never emit a shape its + # own guardrail would fail. if [ -d "$EXAMPLES_PATH" ]; then while IFS= read -r pkg_json; do - for dep in "${sdk_packages[@]}"; do - if jq -e ".dependencies[\"@dotcms/$dep\"]" "$pkg_json" >/dev/null 2>&1; then - jq --arg dep "@dotcms/$dep" --arg v "^$RELEASE_VERSION" \ + for dep_name in "${sdk_package_names[@]}"; do + if jq -e ".dependencies[\"$dep_name\"]" "$pkg_json" >/dev/null 2>&1; then + jq --arg dep "$dep_name" --arg v "$RELEASE_VERSION" \ '.dependencies[$dep] = $v' "$pkg_json" > tmp.$$.json && mv tmp.$$.json "$pkg_json" fi done diff --git a/.github/filters.yaml b/.github/filters.yaml index 89d8627e148d..def307f1925e 100644 --- a/.github/filters.yaml +++ b/.github/filters.yaml @@ -53,6 +53,14 @@ sdk_libs: examples: - 'examples/**' +# The package.json shape guardrail's own source. Deliberately NOT folded into sdk_libs: +# that filter also gates the trunk npm publish job (cicd_3-trunk.yml), and touching the +# validator must not trigger an SDK release. Without this key the job that runs the +# validator is not triggered by changes to the validator — you could break the guardrail +# and keep a green build. +sdk_package_shapes: + - '.github/scripts/validate-sdk-package-shapes/**' + # PR area labeling filters # These are separate from test-triggering filters to avoid false positives # (e.g., workflow changes should trigger backend tests but shouldn't label PR as "Area : Backend") diff --git a/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts b/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts index a8099b74f7c6..48df3df59438 100644 --- a/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts +++ b/.github/scripts/validate-sdk-package-shapes/src/validate.test.ts @@ -114,4 +114,43 @@ describe('validateExamplePackageJson', () => { expect(validateExamplePackageJson(pkg, 'release-25.07.10_lts_v12')).toEqual([]); expect(validateExamplePackageJson(pkg, 'main')).toEqual([]); }); + + it('does NOT flag "latest" on master — cicd_1-pr.yml accepts PRs to main OR master', () => { + const pkg = { dependencies: { '@dotcms/client': 'latest' } }; + + expect(validateExamplePackageJson(pkg, 'master')).toEqual([]); + }); + + it.each(['^26.9.3-1', '~1.2.0', '>=1.0.0', '<2.0.0', '1.2.x', '1.0.0 - 2.0.0', '^1 || ^2'])( + 'flags the range "%s" on a release branch — a range drifts forward just like "latest"', + (range) => { + const pkg = { dependencies: { '@dotcms/client': range } }; + + const violations = validateExamplePackageJson(pkg, 'release-25.07.10_lts_v12'); + + expect(violations).toEqual([expect.stringContaining(range)]); + } + ); + + it('passes an exact prerelease pin on a release branch', () => { + // The shape deploy-javascript-sdk/action.yml writes: normalized CalVer, and the + // `-next.` form cicd_3-trunk.yml produces. + const pkg = { + dependencies: { '@dotcms/client': '26.8.3-1', '@dotcms/uve': '1.2.0-next.2632' } + }; + + expect(validateExamplePackageJson(pkg, 'release-26.08.03_lts_v1')).toEqual([]); + }); + + it('still allows ranges on trunk — only release branches require an exact pin', () => { + const pkg = { dependencies: { '@dotcms/client': '^1.2.0' } }; + + expect(validateExamplePackageJson(pkg, 'main')).toEqual([]); + }); + + it('ignores non-@dotcms dependencies on release branches', () => { + const pkg = { dependencies: { next: '^14.0.0', react: '18.2.0' } }; + + expect(validateExamplePackageJson(pkg, 'release-25.07.10_lts_v12')).toEqual([]); + }); }); diff --git a/.github/scripts/validate-sdk-package-shapes/src/validate.ts b/.github/scripts/validate-sdk-package-shapes/src/validate.ts index fa8a5de5ec13..c88a624226e5 100644 --- a/.github/scripts/validate-sdk-package-shapes/src/validate.ts +++ b/.github/scripts/validate-sdk-package-shapes/src/validate.ts @@ -9,6 +9,24 @@ const FLOATING_SPECIFIERS = new Set(['latest', 'next', '*']); const SIBLING_DEPENDENCY_GUARD_PACKAGES = new Set(['react', 'angular', 'vue', 'analytics']); const GUARDED_SIBLING_DEPS = ['@dotcms/client', '@dotcms/uve']; +/** + * cicd_1-pr.yml triggers on PRs targeting `main` OR `master`, so both have to count as the + * trunk here — otherwise a `master`-targeted PR fails the guardrail for a shape that is + * deliberately correct on trunk. + */ +const FLOATING_ALLOWED_BRANCHES = new Set(['main', 'master']); + +/** + * An exact, non-range pin: `1.2.0`, `26.8.3-1`, `1.2.0-next.2632`. + * + * Deliberately an allow-list rather than a list of range operators to reject: AC-005/AC-010 + * require an *exact* version on release branches, and `^1.2.0` / `~1.2.0` / `>=1.0.0` / + * `1.2.x` / `^1 || ^2` all drift forward past the server the branch was cut for — which is + * exactly the failure mode behind Defect B3 (Freshdesk #38677). Enumerating operators would + * leave whichever syntax nobody thought of silently passing. + */ +const EXACT_VERSION = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/; + type PackageJson = { name?: string; dependencies?: Record; @@ -60,9 +78,11 @@ export function validateSdkLibPackageJson(pkg: PackageJson, pkgName: string): st * Validates an examples//package.json for the given branch. * * - `next`/`*` are never valid, on any branch. - * - `latest` is valid ONLY on `main` (the deliberate Evergreen-alignment exception — see - * ADR-0019's "How this plays with Evergreen" note and this spec's Fix Scope). Every other - * branch (LTS release branches) must pin an exact version. + * - `latest` and version ranges are valid ONLY on `main`/`master` (the deliberate + * Evergreen-alignment exception — see ADR-0019's "How this plays with Evergreen" note and + * this spec's Fix Scope). Every other branch (LTS release branches) must pin an exact + * version; a range drifts forward past the server the branch was cut for just as `latest` + * does, only more quietly. */ export function validateExamplePackageJson(pkg: PackageJson, branch: string): string[] { const violations: string[] = []; @@ -74,9 +94,9 @@ export function validateExamplePackageJson(pkg: PackageJson, branch: string): st if (version === 'next' || version === '*') { violations.push(`examples: dependencies["${dep}"] is "${version}" on branch "${branch}" — never valid, on any branch`); - } else if (version === 'latest' && branch !== 'main') { + } else if (!FLOATING_ALLOWED_BRANCHES.has(branch) && !EXACT_VERSION.test(version)) { violations.push( - `examples: dependencies["${dep}"] is "latest" on branch "${branch}" — only "main" may float; this branch must pin an exact version` + `examples: dependencies["${dep}"] is "${version}" on branch "${branch}" — only main/master may float; this branch must pin an exact version (no "latest", no ranges)` ); } } diff --git a/.github/workflows/cicd_1-pr.yml b/.github/workflows/cicd_1-pr.yml index 1ae1f8bce743..72a7e8d77128 100644 --- a/.github/workflows/cicd_1-pr.yml +++ b/.github/workflows/cicd_1-pr.yml @@ -138,8 +138,12 @@ jobs: if: >- always() && !cancelled() && (fromJSON(needs.initialize.outputs.filters).sdk_libs == 'true' || - fromJSON(needs.initialize.outputs.filters).examples == 'true') + fromJSON(needs.initialize.outputs.filters).examples == 'true' || + fromJSON(needs.initialize.outputs.filters).sdk_package_shapes == 'true') runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} + timeout-minutes: 10 + permissions: + contents: read steps: - name: 'Checkout' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -148,32 +152,41 @@ jobs: uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: 'core-web/.nvmrc' + cache: npm + cache-dependency-path: .github/scripts/validate-sdk-package-shapes/package-lock.json - - name: 'Build validator' + - name: 'Build and test validator' working-directory: .github/scripts/validate-sdk-package-shapes run: | - npm ci + # --ignore-scripts: this manifest is PR-controlled on cicd_1-pr.yml, and npm + # lifecycle scripts would execute arbitrary code from an unmerged branch — see + # that workflow's own "PR checks are run on code that is not yet merged" header. + npm ci --ignore-scripts npm run build + # The guardrail's own tests. Without this a broken validator is a green build: + # it would still exit 0 on manifests it should reject. + npm test shell: bash - name: 'Validate SDK lib and example manifests' working-directory: .github/scripts/validate-sdk-package-shapes env: - # This workflow only triggers for PRs targeting main/master (see the `on:` - # trigger above), so "main" is always the correct branch context here - - # examples on main are allowed to float on "latest" (see file header). + # This workflow only triggers for PRs targeting main or master (see the `on:` + # trigger above), and the validator treats BOTH as trunk - examples there are + # allowed to float on "latest" (see file header). Passing the real base ref + # rather than a hardcoded "main" keeps this honest if the trigger list changes. TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} run: | set -euo pipefail FAIL=0 - for pkg_json in $(find ../../../core-web/libs/sdk -maxdepth 2 -name package.json); do + while IFS= read -r pkg_json; do node dist/index.js "$pkg_json" || FAIL=1 - done + done < <(find ../../../core-web/libs/sdk -maxdepth 2 -name package.json) - for pkg_json in $(find ../../../examples -maxdepth 2 -name package.json); do + while IFS= read -r pkg_json; do node dist/index.js "$pkg_json" --branch "$TARGET_BRANCH" || FAIL=1 - done + done < <(find ../../../examples -maxdepth 2 -name package.json) exit $FAIL shell: bash diff --git a/.github/workflows/cicd_5-lts.yml b/.github/workflows/cicd_5-lts.yml index 86e069e9f81a..905937e223bd 100644 --- a/.github/workflows/cicd_5-lts.yml +++ b/.github/workflows/cicd_5-lts.yml @@ -105,8 +105,12 @@ jobs: if: >- always() && !cancelled() && (fromJSON(needs.initialize.outputs.filters).sdk_libs == 'true' || - fromJSON(needs.initialize.outputs.filters).examples == 'true') + fromJSON(needs.initialize.outputs.filters).examples == 'true' || + fromJSON(needs.initialize.outputs.filters).sdk_package_shapes == 'true') runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} + timeout-minutes: 10 + permissions: + contents: read steps: - name: 'Checkout' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -115,12 +119,20 @@ jobs: uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: 'core-web/.nvmrc' + cache: npm + cache-dependency-path: .github/scripts/validate-sdk-package-shapes/package-lock.json - - name: 'Build validator' + - name: 'Build and test validator' working-directory: .github/scripts/validate-sdk-package-shapes run: | - npm ci + # --ignore-scripts: this manifest is PR-controlled on cicd_1-pr.yml, and npm + # lifecycle scripts would execute arbitrary code from an unmerged branch — see + # that workflow's own "PR checks are run on code that is not yet merged" header. + npm ci --ignore-scripts npm run build + # The guardrail's own tests. Without this a broken validator is a green build: + # it would still exit 0 on manifests it should reject. + npm test shell: bash - name: 'Validate SDK lib and example manifests' @@ -134,13 +146,13 @@ jobs: set -euo pipefail FAIL=0 - for pkg_json in $(find ../../../core-web/libs/sdk -maxdepth 2 -name package.json); do + while IFS= read -r pkg_json; do node dist/index.js "$pkg_json" || FAIL=1 - done + done < <(find ../../../core-web/libs/sdk -maxdepth 2 -name package.json) - for pkg_json in $(find ../../../examples -maxdepth 2 -name package.json); do + while IFS= read -r pkg_json; do node dist/index.js "$pkg_json" --branch "$TARGET_BRANCH" || FAIL=1 - done + done < <(find ../../../examples -maxdepth 2 -name package.json) exit $FAIL shell: bash diff --git a/.github/workflows/cicd_comp_initialize-phase.yml b/.github/workflows/cicd_comp_initialize-phase.yml index 314b9c638235..30f4ba056428 100644 --- a/.github/workflows/cicd_comp_initialize-phase.yml +++ b/.github/workflows/cicd_comp_initialize-phase.yml @@ -164,7 +164,7 @@ jobs: # test_filters: Subset of build_test_filters affected by CICD_SKIP_TESTS # ============================================================ build_test_filters="frontend cli backend build jvm_unit_test" - info_filters="sdk_libs examples documentation cicd" + info_filters="sdk_libs examples sdk_package_shapes documentation cicd" test_filters="frontend cli backend jvm_unit_test" declare -A results diff --git a/.github/workflows/cicd_comp_release-prepare-phase.yml b/.github/workflows/cicd_comp_release-prepare-phase.yml index e936e9e1eb26..59805f849f70 100644 --- a/.github/workflows/cicd_comp_release-prepare-phase.yml +++ b/.github/workflows/cicd_comp_release-prepare-phase.yml @@ -241,6 +241,64 @@ jobs: git add "${MIN_SDK_FILE}" fi + # Pin every example app's @dotcms/* dependencies to this exact release's SDK + # version, on every release branch (LTS and non-LTS alike) — the root-cause fix + # for examples/nextjs on release-25.07.10_lts_v12/_v16 having shipped with a + # floating "latest" for months with nothing to catch or correct it (see + # dotCMS/core#36891, specs/37399-sdk-packaging-version-fix). ADR-0019's + # date-lockstep versioning means the SDK version matching this exact release is + # known with certainty the moment the branch is cut — no runtime verification + # needed, unlike the one-time manual fix required to retrofit the two LTS + # branches that predate this step. `main` is never touched here and keeps + # floating on "latest" by design (its examples target Evergreen customers). + # + # Folded into THIS commit on purpose rather than a separate commit+push: + # - `release_commit` below is the release tag's target_commitish, so a later + # commit would leave the tag pointing at a tree with no pin. + # - a second push to release-* re-triggers cicd_5-lts.yml, whose + # `cancel-in-progress: true` would cancel the run the first push just started. + # - `git commit -a` picks these rewrites up, so there is no separate commit to + # fail with "nothing to commit" when the pin is a no-op on a re-run. + # + # Same normalization as deploy-javascript-sdk/action.yml (Defect A): the release + # version is zero-padded CalVer (e.g. 26.08.03-01); npm packages publish under the + # leading-zero-stripped, valid-semver form (26.8.3-1). Separators and any trailing + # segments are preserved — see that file for why rebuilding from $1..$4 is wrong. + normalized_version=$(echo "${release_version}" | awk '{ + s = $0; out = "" + while (match(s, /[.-]/)) { + seg = substr(s, 1, RSTART - 1); sep = substr(s, RSTART, 1) + if (seg ~ /^[0-9]+$/) { sub(/^0+/, "", seg); if (seg == "") seg = "0" } + out = out seg sep + s = substr(s, RSTART + 1) + } + if (s ~ /^[0-9]+$/) { sub(/^0+/, "", s); if (s == "") s = "0" } + print out s + }') + echo "Pinning example app SDK dependencies to ${normalized_version}" + + while IFS= read -r pkg_json; do + # jq reprints the whole document, so match each manifest's existing indentation. + # Without this, a pin that changes four values lands as a ~120-line whole-file + # whitespace diff in the release commit: examples/ mixes 2-space (angular, + # angular-ssr, astro) and 4-space (nextjs, nextjs-experiments, vuejs) manifests, + # so no single fixed --indent leaves them all alone. + if awk 'NR>1 && /^\t/ { found = 1; exit } END { exit !found }' "${pkg_json}"; then + indent_flag=(--tab) + else + indent=$(awk 'NR>1 && /^ +/ { match($0, /^ +/); print RLENGTH; exit }' "${pkg_json}") + # jq rejects --indent above 7; fall back to 2 for anything unexpected. + [[ "${indent}" =~ ^[1-7]$ ]] || indent=2 + indent_flag=(--indent "${indent}") + fi + + while IFS= read -r dep; do + jq "${indent_flag[@]}" --arg dep "${dep}" --arg v "${normalized_version}" \ + '.dependencies[$dep] = $v' "${pkg_json}" > tmp.$$.json && mv tmp.$$.json "${pkg_json}" + echo " ↳ ${pkg_json}: dependencies[\"${dep}\"] -> ${normalized_version}" + done < <(jq -r '.dependencies // {} | keys[] | select(startswith("@dotcms/"))' "${pkg_json}") + done < <(find examples -maxdepth 2 -name package.json -not -path "*/node_modules/*" 2>/dev/null) + git status git commit -a -m "🏁 Publishing release version [${release_version}]" git push origin ${release_branch} @@ -248,57 +306,6 @@ jobs: release_commit=$(git log -1 --pretty=%H) echo "release_commit=${release_commit}" >> "$GITHUB_OUTPUT" - # Pin every example app's @dotcms/* dependencies to this exact release's SDK - # version, on every release branch (LTS and non-LTS alike) — the root-cause fix - # for examples/nextjs on release-25.07.10_lts_v12/_v16 having shipped with a - # floating "latest" for months with nothing to catch or correct it (see - # dotCMS/core#36891, specs/37399-sdk-packaging-version-fix). ADR-0019's - # date-lockstep versioning means the SDK version matching this exact release is - # known with certainty the moment the branch is cut — no runtime verification - # needed, unlike the one-time manual fix required to retrofit the two LTS - # branches that predate this step. `main` is never touched here and keeps - # floating on "latest" by design (its examples target Evergreen customers). - - name: Pin example app SDK dependencies to the release version - id: pin-examples - env: - RELEASE_VERSION: ${{ steps.set-version.outputs.release_version }} - RELEASE_BRANCH: ${{ steps.set-version.outputs.release_branch }} - run: | - set -euo pipefail - echo "::group::Pin example app SDK dependencies" - - # Same normalization as deploy-javascript-sdk/action.yml (Defect A): the - # release version is zero-padded CalVer (e.g. 26.08.03-01); npm packages - # publish under the leading-zero-stripped, valid-semver form (26.8.3-1). - NORMALIZED_VERSION=$(echo "$RELEASE_VERSION" | awk -F'[.-]' '{ - for (i = 1; i <= NF; i++) { sub(/^0+/, "", $i); if ($i == "") $i = "0" } - out = $1 "." $2 "." $3 - if (NF >= 4) out = out "-" $4 - print out - }') - echo "Normalized release version: ${NORMALIZED_VERSION}" - - CHANGED=false - for pkg_json in $(find examples -maxdepth 2 -name package.json 2>/dev/null); do - for dep in $(jq -r '.dependencies // {} | keys[] | select(startswith("@dotcms/"))' "$pkg_json"); do - jq --arg dep "$dep" --arg v "$NORMALIZED_VERSION" \ - '.dependencies[$dep] = $v' "$pkg_json" > tmp.$$.json && mv tmp.$$.json "$pkg_json" - echo " ↳ ${pkg_json}: dependencies[\"${dep}\"] -> ${NORMALIZED_VERSION}" - CHANGED=true - done - done - - if [ "$CHANGED" = "true" ]; then - git add examples - git commit -m "🏁 Pin example app SDK dependencies to ${NORMALIZED_VERSION}" - git push origin "${RELEASE_BRANCH}" - else - echo "No @dotcms/* dependencies found in any examples/*/package.json on this branch — nothing to pin." - fi - - echo "::endgroup::" - shell: bash - - name: Create GitHub Release run: | curl -X POST \ diff --git a/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md b/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md index c9ab2eab4ae0..9d04137b81f0 100644 --- a/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md +++ b/specs/37399-sdk-packaging-version-fix/contracts/package-json-shape.md @@ -19,6 +19,17 @@ able to check the built result against. Non-`@dotcms/*` dependencies (`@tinymce/*`, `react`/`react-dom`, `@angular/*`, `rxjs`, etc.) are untouched by this fix. +`core-web/libs/sdk/` holds **eleven** directories, not the seven listed above. The other four — +`ai`, `cli`, `create-app`, `types` — declare **no `@dotcms/*` entry in any dependency field**, so +this table has nothing to say about them and the fix had nothing to change in them. They are not +exempt from the contract: the CI validator and the release pipeline's rewrite loop both walk all +eleven, and the moment one of them takes a sibling dependency the rows above apply to it too. + +One of those four matters for the release pipeline in particular: **`cli` publishes as the +unscoped `dotcms`**, not `@dotcms/cli`. Any code that resolves a sibling by name must read the +sibling's own `.name` field rather than assuming an `@dotcms/` prefix — see the comments in +`deploy-javascript-sdk/action.yml`. + ## SDK libraries — published tarball (post-publish, at release version `X`) Every `@dotcms/*` entry in `dependencies`, `peerDependencies`, **and `devDependencies`** (all @@ -43,4 +54,10 @@ succeed. A new check in `cicd_1-pr.yml` fails the build if any file above is edited to violate this table — specifically: a floating (`latest`/`next`/`*`) `peerDependencies` entry in any SDK lib; a reintroduced `@dotcms/client`/`@dotcms/uve` in `dependencies` of `react`/`angular`/`vue`/`analytics`; -or a floating example pin anywhere except `"latest"` on `main`. +or an example pin that is not an exact version on any branch other than `main`/`master` +(`latest`, `next`, `*` and ranges such as `^1.2.0`/`~1.2.0`/`1.2.x` all fail there — a range +drifts forward past the server the release branch was cut for exactly as `latest` does). + +`main` and `master` are both treated as trunk because `cicd_1-pr.yml` accepts PRs targeting +either. The same check also runs on `cicd_5-lts.yml` (`push:` to `release-*`), where the branch +context is the pushed ref rather than a PR base.