@openlup/core 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,83 @@
1
+ # `@openlup/core` for agents
2
+
3
+ ## Purpose and kind
4
+
5
+ Kind: `kernel`.
6
+
7
+ `@openlup/core` holds the I/O-free contracts and logic that more than one
8
+ `@openlup/*` package, or an application's composition, reads. Its only runtime
9
+ dependency is `zod`. It performs no I/O, reads no environment and starts no
10
+ process: an application binds its ports and composes it.
11
+
12
+ ## Subpath maturity
13
+
14
+ | Export | Role | Maturity |
15
+ | --- | --- | --- |
16
+ | `./bundle` | kernel | candidate |
17
+ | `./catalog` | kernel | candidate |
18
+ | `./company-identity` | kernel | experimental |
19
+ | `./fulfillment` | kernel | experimental |
20
+ | `./inventory` | kernel | candidate |
21
+ | `./marketing/research` | kernel | experimental |
22
+ | `./partners` | kernel | experimental |
23
+ | `./payment` | kernel | experimental |
24
+ | `./platform-runtime` | kernel | experimental |
25
+ | `./pricing` | kernel | candidate |
26
+ | `./promo` | kernel | candidate |
27
+ | `./risk` | kernel | candidate |
28
+ | `./shipping` | kernel | candidate |
29
+ | `./subscription` | kernel | candidate |
30
+ | `./testing` | testing | testing |
31
+
32
+ Any version may change an export, and each change is recorded in
33
+ [the changelog](CHANGELOG.md).
34
+
35
+ ## Wiring example
36
+
37
+ The application owns its composition. This example compiles in OpenLup's CI:
38
+
39
+ ```ts
40
+ import {
41
+ createPlatformBundleRegistry,
42
+ platformEnvSchema,
43
+ type PlatformBundleReadiness,
44
+ type PlatformEnvInput,
45
+ } from "@openlup/core/platform-runtime";
46
+
47
+ // Application composition: the application, not the package, owns its bundles.
48
+ const registry = createPlatformBundleRegistry({
49
+ defaultBundleId: "self-host",
50
+ descriptors: [
51
+ {
52
+ id: "self-host",
53
+ capabilities: {
54
+ http: "node-http",
55
+ scheduler: "cron",
56
+ blob: "filesystem",
57
+ data: "postgres",
58
+ migrations: "sql-files",
59
+ analytics: "none",
60
+ transactional: "smtp",
61
+ },
62
+ readReadiness: (env) =>
63
+ env.APP_BASE_URL ? { ok: true } : { ok: false, error: "APP_BASE_URL is not set" },
64
+ },
65
+ ] as const,
66
+ });
67
+
68
+ export function readPlatformReadiness(input: PlatformEnvInput): PlatformBundleReadiness {
69
+ const env = platformEnvSchema.parse(input);
70
+ return registry.getBundleDescriptor(registry.resolveBundleId(env)).readReadiness(input);
71
+ }
72
+ ```
73
+
74
+ ## Sources and declarations
75
+
76
+ The tarball ships its TypeScript sources under `src/` and its JavaScript and
77
+ declarations under `dist/`. Each export's `types` condition names its
78
+ declaration file, and its `core-source` condition names its source file. Read
79
+ those before guessing at a contract.
80
+
81
+ ## Readiness codes
82
+
83
+ This version raises no `OPENLUP_E_*` readiness code.
package/CHANGELOG.md CHANGED
@@ -6,13 +6,24 @@ source previews may already ship some changes, and their release notes say
6
6
  which. Declaration snapshots track candidate drift, not a stable API or
7
7
  SemVer promise.
8
8
 
9
- ## Unreleased — checkout recovery contracts
9
+ ## [Unreleased]
10
10
 
11
- - Add neutral checkout recovery evidence, method, operation and action contracts
12
- to the existing payment surface. They describe guidance only and do not change
13
- payment admission, renewal classification or enabled payment methods.
11
+ ## [0.12.0]
14
12
 
15
- ## [Unreleased]
13
+ ### Removed
14
+
15
+ - **API REMOVAL: the `./checkout` subpath.** It held only an
16
+ `Idempotency-Key` header-shape helper that no checkout path used.
17
+
18
+ Migration: replace
19
+
20
+ ```ts
21
+ import { validateCheckoutIdempotencyHeader } from "@openlup/core/checkout";
22
+ ```
23
+
24
+ with an application-owned validator, or delete the call if, like the
25
+ reference checkout, your checkout carries its idempotency key in the
26
+ request body.
16
27
 
17
28
  ### Changed
18
29
 
@@ -36,6 +47,17 @@ SemVer promise.
36
47
  `dunning_not_exhausted`. An exhaustive `switch` over the union must handle
37
48
  both.
38
49
 
50
+ - `release-gates.json` sets `packageRelease.versionRule` to `0.N.P`. Below
51
+ 1.0 every `@openlup/*` package carries one set version: a minor set for any
52
+ API, behaviour or schema change, a patch set for a fix only.
53
+
54
+ Migration: a tool that reads `versionRule` expects the set rule. Before:
55
+ `"versionRule": "0.<n>.0"`. After: `"versionRule": "0.N.P"`.
56
+
57
+ - Publishable on the npm `latest` dist-tag as `0.12.0`, from tag
58
+ `openlup-core-v0.12.0`.
59
+ - Publishable on the npm `preview` dist-tag as `0.11.0`, for source preview
60
+ `openlup-source-preview/11`.
39
61
  - Publishable on the npm `preview` dist-tag as `0.10.0`, for source preview
40
62
  `openlup-source-preview/10`.
41
63
  - Publishable on the npm `preview` dist-tag as `0.9.0`, for source preview
@@ -63,6 +85,22 @@ SemVer promise.
63
85
 
64
86
  ### Added
65
87
 
88
+ - `AGENTS.md` ships in the tarball. It states the package's kind and each
89
+ subpath's maturity, gives one wiring example that compiles in CI, points to
90
+ the shipped sources and declarations, and lists the readiness codes the
91
+ package raises (none yet).
92
+ - `release-gates.json` schema 5 declares the package kind (`"kind": "kernel"`)
93
+ and splits the pack budget. Code files (dist JavaScript, declarations and
94
+ shipped sources) and root documentation and metadata files each have their
95
+ own caps on packed bytes and files; the tarball total stays as a backstop.
96
+
97
+ Migration: a tool that reads `release-gates.json` checks `schemaVersion: 5`.
98
+ Before: `"pack": { "maxPackedBytes": …, "maxFiles": … }`. After: `"pack": {
99
+ "code": { "maxPackedBytes": …, "maxFiles": … }, "docs": { … },
100
+ "maxPackedBytes": …, "maxFiles": … }`.
101
+ - Add neutral checkout recovery evidence, method, operation and action contracts
102
+ to the existing payment surface. They describe guidance only and do not change
103
+ payment admission, renewal classification or enabled payment methods.
66
104
  - Export neutral lookup normalization from `./company-identity` for extensions.
67
105
 
68
106
  - `SUBSCRIPTION_STATUS_TRANSITIONS`, the frozen status matrix, and
@@ -224,6 +262,9 @@ SemVer promise.
224
262
 
225
263
  ## Release Status
226
264
 
227
- No npm version has been released yet. A `preview` version establishes no
228
- stable package API or supported upgrade channel. Add a dated section only
229
- after a version is published.
265
+ Versions up to `0.11.0` were published on the npm `preview` dist-tag without
266
+ sections of their own, so the `0.12.0` section also lists the changes they
267
+ carried. A release-preparation pull request turns the Unreleased section into
268
+ the next version's section; its release tag and npm record when it is
269
+ published. No version establishes a stable package API or supported upgrade
270
+ channel.
package/CONTRIBUTING.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Contributing
2
2
 
3
3
  Commerce Core is public source for framework-independent commerce kernels.
4
- Its npm versions ride on the source previews (`preview` dist-tag only) and
5
- activate no separate package product or stable API.
4
+ Below 1.0 its npm versions are set versions `0.N.P`, shared by every
5
+ `@openlup/*` package and published on the `latest` dist-tag; versions up to
6
+ `0.11.0` rode on the source previews (`preview` dist-tag only). No version
7
+ activates a separate package product or stable API.
6
8
 
7
9
  ## Set up and verify
8
10
 
@@ -15,7 +17,7 @@ npm run ci
15
17
 
16
18
  The gate builds every export, runs standalone tests and isolated package smoke,
17
19
  verifies declaration snapshots, and checks license, SBOM, audit, pack, and
18
- publish controls. Neither the root test script nor root CI runs it.
20
+ publish controls. Root Vitest excludes this suite; CI's required test job runs it separately.
19
21
 
20
22
  ## Scope
21
23
 
package/MAINTAINERS.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Maintainers
2
2
 
3
3
  `@openlup/core` is maintained in the phase-5 platform monorepo. It is not an
4
- independent product; its versions ride on the source previews.
4
+ independent product. Below 1.0 its versions are set versions `0.N.P`, shared by
5
+ every `@openlup/*` package; versions up to `0.11.0` rode on the source previews.
5
6
 
6
7
  ## Responsibilities
7
8
 
package/README.md CHANGED
@@ -1,14 +1,22 @@
1
1
  # Commerce Core
2
2
 
3
- `@openlup/core` is a framework-independent TypeScript workspace package of
4
- subscription and bundle commerce kernels, not a separate library product.
5
-
6
- > [!IMPORTANT]
7
- > Version `0.<n>.0` is packed from source preview `openlup-source-preview/<n>`,
8
- > staged with provenance and approved with 2FA, on the `preview` dist-tag only.
9
- > `npm publish` from this directory is refused. No version implies a stable
10
- > API or supported consumer path. Build, pack, and isolated-import checks are
11
- > package smoke evidence only.
3
+ `@openlup/core` is the kernel of the `@openlup/*` package family: the
4
+ framework-independent TypeScript contracts and logic for subscription and
5
+ bundle commerce that the other packages and an application's composition read.
6
+ Agents start with [AGENTS.md](AGENTS.md).
7
+
8
+ ## Channel and stability
9
+
10
+ - **Channel:** npm, on the `latest` dist-tag. Each version is published with
11
+ provenance from its release tag `openlup-core-v<version>` after a protected
12
+ release approval. Versions up to `0.11.0` were published on the `preview`
13
+ dist-tag only. `npm publish` from this directory is refused.
14
+ - **Versions:** below 1.0 every `@openlup/*` package shares one set version
15
+ `0.N.P`: a minor set for any API, behaviour or schema change, a patch set for
16
+ a fix only.
17
+ - **Stability:** development preview. Any version may change an export, and
18
+ each change is recorded in [the changelog](CHANGELOG.md). Pin an exact
19
+ version and read the changelog when you upgrade.
12
20
 
13
21
  ## Package Surface Maturity
14
22
 
@@ -20,7 +28,6 @@ promise.
20
28
  | --- | --- | --- | --- |
21
29
  | `./bundle` | kernel | candidate | `smoke/subscriptionBundleStandalone.test.ts` |
22
30
  | `./catalog` | kernel | candidate | `smoke/catalogStandalone.test.ts` |
23
- | `./checkout` | kernel | experimental | `smoke/checkoutStandalone.test.ts` |
24
31
  | `./company-identity` | kernel | experimental | `smoke/companyIdentityStandalone.test.ts` |
25
32
  | `./fulfillment` | kernel | experimental | `smoke/fulfillmentStandalone.test.ts` |
26
33
  | `./inventory` | kernel | candidate | `smoke/inventoryStandalone.test.ts` |
@@ -55,8 +62,7 @@ npm run ci
55
62
  ```
56
63
 
57
64
  Focused proofs are `npm run api:check`, `npm run release:check`, and
58
- `npm run test:consumer`. The root test script skips this suite. A `preview`
59
- install is evaluation, not a supported install.
65
+ `npm run test:consumer`. The root test script skips this suite.
60
66
 
61
67
  Repository neutrality checks reuse the existing source scanner through
62
68
  [the tree counting interface](./scripts/neutrality-tree-counts.ts). This
@@ -88,5 +94,3 @@ neutrality evidence, not package activation or consumer compatibility evidence.
88
94
  `./subscription` kernel owns, its deterministic-clock contract, the
89
95
  late-payment cycle-shift rule and its monotonic clamp, and how a host
90
96
  application consumes it.
91
-
92
- <!-- openlup-doc-impact {"unit":"core","digest":"sha256-074e25741bf9bc6c0c2717eaa0af20b26a5d219c4a082397b702bfc12e2e245f","reason":"Against the integrated main base, the core manifest and lockfile still advance only the lockstep version from 0.9.0 to 0.10.0. Exports, package smoke, publication settings, and development-preview maturity remain unchanged."} -->
package/SECURITY.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  ## Release Support
4
4
 
5
- No package version is supported. A `0.<n>.0` version on the `preview`
6
- dist-tag is development-preview evaluation, not a supported public release. A
7
- future support policy belongs to phase-5 public platform governance, not to a
8
- separate package release channel.
5
+ No package version is supported. A `0.N.P` version, on the `preview` dist-tag
6
+ up to `0.11.0` or on `latest` after it, is development-preview evaluation, not a
7
+ supported public release. A future support policy belongs to phase-5 public
8
+ platform governance, not to a separate package release channel.
9
9
 
10
10
  ## Vulnerability Reporting
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlup/core",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Neutral D2C subscription and bundle commerce kernels; a development preview with no stable API.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -22,6 +22,7 @@
22
22
  "files": [
23
23
  "dist/**",
24
24
  "src/**",
25
+ "AGENTS.md",
25
26
  "CHANGELOG.md",
26
27
  "CODE_OF_CONDUCT.md",
27
28
  "CONTRIBUTING.md",
@@ -74,16 +75,6 @@
74
75
  "import": "./dist/bundle/contracts.js",
75
76
  "default": "./dist/bundle/contracts.js"
76
77
  },
77
- "./checkout": {
78
- "core-source": "./src/checkout/index.ts",
79
- "types": "./dist/checkout/index.d.ts",
80
- "module": "./dist/checkout/index.js",
81
- "node": "./dist/checkout/index.js",
82
- "development": "./dist/checkout/index.js",
83
- "production": "./dist/checkout/index.js",
84
- "import": "./dist/checkout/index.js",
85
- "default": "./dist/checkout/index.js"
86
- },
87
78
  "./company-identity": {
88
79
  "core-source": "./src/company-identity/index.ts",
89
80
  "types": "./dist/company-identity/index.d.ts",
@@ -1,5 +1,6 @@
1
1
  {
2
- "schemaVersion": 4,
2
+ "schemaVersion": 5,
3
+ "kind": "kernel",
3
4
  "compatibilityPolicies": {
4
5
  "internal-candidate": "Candidate package surfaces may change until the platform phase-5 activation explicitly freezes a public API.",
5
6
  "testing-internal": "Test-support surfaces may change and are not runtime compatibility promises."
@@ -8,7 +9,7 @@
8
9
  "phase": "platform-monorepo-phase-5",
9
10
  "publicStability": "not-claimed",
10
11
  "artifactChannel": "npm-staged-preview",
11
- "versionRule": "0.<n>.0"
12
+ "versionRule": "0.N.P"
12
13
  },
13
14
  "evidence": {
14
15
  "packageSmokeEvidence": {
@@ -53,14 +54,6 @@
53
54
  "entrypoint": "dist/pricing/index.d.ts",
54
55
  "snapshot": "api/pricing.api.md"
55
56
  },
56
- "./checkout": {
57
- "role": "kernel",
58
- "maturity": "experimental",
59
- "compatibilityPolicy": "internal-candidate",
60
- "packageSmokeEvidence": ["smoke/checkoutStandalone.test.ts"],
61
- "entrypoint": "dist/checkout/index.d.ts",
62
- "snapshot": "api/checkout.api.md"
63
- },
64
57
  "./company-identity": {
65
58
  "role": "kernel",
66
59
  "maturity": "experimental",
@@ -209,11 +202,20 @@
209
202
  }
210
203
  },
211
204
  "pack": {
212
- "$raiseReason": "2026-09-27, owner-approved: the subscription status matrix, the win-back and the exhausted-ladder pause and resume add four functions, one predicate and two constants with their JSDoc, including the host duties the managed SQL restarts keep, JavaScript and declaration outputs, plus their changelog entry. Measured, not estimated: clean origin/main packs 122809 packed bytes, 534452 unpacked bytes and 192 files; this change packs about 130.3 thousand packed and 569.7 thousand unpacked bytes (rounded, because this note's own text moves the last digits) and 192 files. Packed allowance increases by 8192 bytes and unpacked by 38912 bytes; maxFiles stays 192. The package export map, dependencies and all other release gates are unchanged; the ./subscription entry gains named exports. The previous raise, 2026-09-11 and owner-approved, moved packed by 2048 and unpacked by 6144 bytes for checkout refusal recovery.",
213
- "maxPackedBytes": 131072,
214
- "maxUnpackedBytes": 573440,
215
- "maxFiles": 192,
205
+ "$budgetReason": "2026-10-02, owner-approved at the patch read: the pack budget splits into classes so that documentation and metadata cannot crowd out code, and code growth stays visible. Code counts dist JavaScript, declarations and src TypeScript; docs counts the documentation and metadata files at the package root; anything else counts only toward the total. A class's packed bytes are its files gzipped together. Measured, not estimated: clean origin/main packs 131062 packed bytes, 572032 unpacked bytes and 192 files against caps of 131072, 573440 and 192. With AGENTS.md this change packs about 132.1 thousand packed and 575.7 thousand unpacked bytes and 193 files: code exactly 113573 packed bytes in 183 files, docs about 17.9 thousand packed bytes in 10 files (rounded, because this note's own text moves the last digits). The code caps of 114688 bytes and 183 files admit no new code file without a raise. The docs caps are 24576 bytes and 12 files. The total backstop is the sum of the class caps, 139264 packed bytes and 195 files, with 614400 unpacked bytes.",
206
+ "code": {
207
+ "maxPackedBytes": 114688,
208
+ "maxFiles": 183
209
+ },
210
+ "docs": {
211
+ "maxPackedBytes": 24576,
212
+ "maxFiles": 12
213
+ },
214
+ "maxPackedBytes": 139264,
215
+ "maxUnpackedBytes": 614400,
216
+ "maxFiles": 195,
216
217
  "requiredFiles": [
218
+ "AGENTS.md",
217
219
  "CHANGELOG.md",
218
220
  "CODE_OF_CONDUCT.md",
219
221
  "CONTRIBUTING.md",
@@ -1,29 +0,0 @@
1
- /**
2
- * Reference checkout idempotency-key contract.
3
- *
4
- * This module validates the shape of a header-bound idempotency key only. The
5
- * current product checkout validates body `intent.idempotencyKey`, not this
6
- * header contract; downstream checkout runtimes must explicitly wire this
7
- * helper before treating the header as authoritative.
8
- *
9
- * Callers own any persistence workflow for inserting, replaying, or completing
10
- * a checkout idempotency record.
11
- */
12
- /** @beta */
13
- export declare const CHECKOUT_IDEMPOTENCY_KEY_HEADER = "idempotency-key";
14
- /** @beta */
15
- export type CheckoutIdempotencyRejection = {
16
- reason: "missing";
17
- message: string;
18
- } | {
19
- reason: "malformed";
20
- message: string;
21
- };
22
- /** @beta */
23
- export declare function validateCheckoutIdempotencyHeader(headerValue: string | undefined | null): {
24
- ok: true;
25
- key: string;
26
- } | {
27
- ok: false;
28
- rejection: CheckoutIdempotencyRejection;
29
- };
@@ -1,37 +0,0 @@
1
- /**
2
- * Reference checkout idempotency-key contract.
3
- *
4
- * This module validates the shape of a header-bound idempotency key only. The
5
- * current product checkout validates body `intent.idempotencyKey`, not this
6
- * header contract; downstream checkout runtimes must explicitly wire this
7
- * helper before treating the header as authoritative.
8
- *
9
- * Callers own any persistence workflow for inserting, replaying, or completing
10
- * a checkout idempotency record.
11
- */
12
- /** @beta */
13
- export const CHECKOUT_IDEMPOTENCY_KEY_HEADER = "idempotency-key";
14
- const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
15
- /** @beta */
16
- export function validateCheckoutIdempotencyHeader(headerValue) {
17
- if (headerValue === undefined || headerValue === null || headerValue.trim().length === 0) {
18
- return {
19
- ok: false,
20
- rejection: {
21
- reason: "missing",
22
- message: `Missing required ${CHECKOUT_IDEMPOTENCY_KEY_HEADER} header.`,
23
- },
24
- };
25
- }
26
- const trimmed = headerValue.trim();
27
- if (!UUID_V4_PATTERN.test(trimmed)) {
28
- return {
29
- ok: false,
30
- rejection: {
31
- reason: "malformed",
32
- message: `${CHECKOUT_IDEMPOTENCY_KEY_HEADER} must be a UUID v4.`,
33
- },
34
- };
35
- }
36
- return { ok: true, key: trimmed };
37
- }
@@ -1,2 +0,0 @@
1
- /** @beta */
2
- export * from "./idempotency.js";
@@ -1,2 +0,0 @@
1
- /** @beta */
2
- export * from "./idempotency.js";
@@ -1,49 +0,0 @@
1
- /**
2
- * Reference checkout idempotency-key contract.
3
- *
4
- * This module validates the shape of a header-bound idempotency key only. The
5
- * current product checkout validates body `intent.idempotencyKey`, not this
6
- * header contract; downstream checkout runtimes must explicitly wire this
7
- * helper before treating the header as authoritative.
8
- *
9
- * Callers own any persistence workflow for inserting, replaying, or completing
10
- * a checkout idempotency record.
11
- */
12
-
13
- /** @beta */
14
- export const CHECKOUT_IDEMPOTENCY_KEY_HEADER = "idempotency-key";
15
-
16
- const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
17
-
18
- /** @beta */
19
- export type CheckoutIdempotencyRejection =
20
- | { reason: "missing"; message: string }
21
- | { reason: "malformed"; message: string };
22
-
23
- /** @beta */
24
- export function validateCheckoutIdempotencyHeader(
25
- headerValue: string | undefined | null,
26
- ): { ok: true; key: string } | { ok: false; rejection: CheckoutIdempotencyRejection } {
27
- if (headerValue === undefined || headerValue === null || headerValue.trim().length === 0) {
28
- return {
29
- ok: false,
30
- rejection: {
31
- reason: "missing",
32
- message: `Missing required ${CHECKOUT_IDEMPOTENCY_KEY_HEADER} header.`,
33
- },
34
- };
35
- }
36
-
37
- const trimmed = headerValue.trim();
38
- if (!UUID_V4_PATTERN.test(trimmed)) {
39
- return {
40
- ok: false,
41
- rejection: {
42
- reason: "malformed",
43
- message: `${CHECKOUT_IDEMPOTENCY_KEY_HEADER} must be a UUID v4.`,
44
- },
45
- };
46
- }
47
-
48
- return { ok: true, key: trimmed };
49
- }
@@ -1,2 +0,0 @@
1
- /** @beta */
2
- export * from "./idempotency.js";