@openlup/core 0.0.0 → 0.6.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.
Files changed (192) hide show
  1. package/CHANGELOG.md +184 -0
  2. package/CODE_OF_CONDUCT.md +39 -0
  3. package/CONTRIBUTING.md +34 -0
  4. package/LICENSE +204 -0
  5. package/MAINTAINERS.md +33 -0
  6. package/README.md +64 -3
  7. package/SECURITY.md +29 -0
  8. package/dist/bundle/contracts.d.ts +9 -0
  9. package/dist/bundle/contracts.js +1 -0
  10. package/dist/bundle/ports.d.ts +41 -0
  11. package/dist/bundle/ports.js +1 -0
  12. package/dist/catalog/identifiers.d.ts +31 -0
  13. package/dist/catalog/identifiers.js +46 -0
  14. package/dist/catalog/index.d.ts +2 -0
  15. package/dist/catalog/index.js +2 -0
  16. package/dist/checkout/idempotency.d.ts +29 -0
  17. package/dist/checkout/idempotency.js +37 -0
  18. package/dist/checkout/index.d.ts +2 -0
  19. package/dist/checkout/index.js +2 -0
  20. package/dist/company-identity/contracts.d.ts +220 -0
  21. package/dist/company-identity/contracts.js +168 -0
  22. package/dist/company-identity/index.d.ts +6 -0
  23. package/dist/company-identity/index.js +2 -0
  24. package/dist/company-identity/ports.d.ts +13 -0
  25. package/dist/company-identity/ports.js +1 -0
  26. package/dist/fulfillment/facts.d.ts +103 -0
  27. package/dist/fulfillment/facts.js +195 -0
  28. package/dist/fulfillment/index.d.ts +4 -0
  29. package/dist/fulfillment/index.js +2 -0
  30. package/dist/inventory/atp.d.ts +172 -0
  31. package/dist/inventory/atp.js +211 -0
  32. package/dist/inventory/index.d.ts +2 -0
  33. package/dist/inventory/index.js +2 -0
  34. package/dist/marketing/research/contracts.d.ts +75 -0
  35. package/dist/marketing/research/contracts.js +60 -0
  36. package/dist/marketing/research/index.d.ts +4 -0
  37. package/dist/marketing/research/index.js +4 -0
  38. package/dist/marketing/research/ports.d.ts +15 -0
  39. package/dist/marketing/research/ports.js +1 -0
  40. package/dist/partners/index.d.ts +4 -0
  41. package/dist/partners/index.js +4 -0
  42. package/dist/partners/ports.d.ts +12 -0
  43. package/dist/partners/ports.js +1 -0
  44. package/dist/partners/status.d.ts +48 -0
  45. package/dist/partners/status.js +35 -0
  46. package/dist/payment/executionBaseContracts.d.ts +81 -0
  47. package/dist/payment/executionBaseContracts.js +8 -0
  48. package/dist/payment/index.d.ts +36 -0
  49. package/dist/payment/index.js +18 -0
  50. package/dist/payment/methodLifecycle.d.ts +91 -0
  51. package/dist/payment/methodLifecycle.js +76 -0
  52. package/dist/payment/paymentControlTypes.d.ts +135 -0
  53. package/dist/payment/paymentControlTypes.js +110 -0
  54. package/dist/payment/paymentFailureTaxonomy.d.ts +80 -0
  55. package/dist/payment/paymentFailureTaxonomy.js +155 -0
  56. package/dist/payment/paymentFailureTaxonomyContracts.d.ts +106 -0
  57. package/dist/payment/paymentFailureTaxonomyContracts.js +177 -0
  58. package/dist/payment/paymentRecovery.d.ts +26 -0
  59. package/dist/payment/paymentRecovery.js +73 -0
  60. package/dist/payment/paymentRecoveryContracts.d.ts +53 -0
  61. package/dist/payment/paymentRecoveryContracts.js +10 -0
  62. package/dist/payment/paymentStateMachine.d.ts +28 -0
  63. package/dist/payment/paymentStateMachine.js +184 -0
  64. package/dist/payment/providerAttemptIdempotency.d.ts +23 -0
  65. package/dist/payment/providerAttemptIdempotency.js +31 -0
  66. package/dist/payment/providerCapability.d.ts +148 -0
  67. package/dist/payment/providerCapability.js +13 -0
  68. package/dist/platform-runtime/contracts.d.ts +9 -0
  69. package/dist/platform-runtime/contracts.js +13 -0
  70. package/dist/platform-runtime/index.d.ts +10 -0
  71. package/dist/platform-runtime/index.js +4 -0
  72. package/dist/platform-runtime/platformKernel.d.ts +44 -0
  73. package/dist/platform-runtime/platformKernel.js +39 -0
  74. package/dist/platform-runtime/ports.d.ts +100 -0
  75. package/dist/platform-runtime/ports.js +1 -0
  76. package/dist/pricing/index.d.ts +8 -0
  77. package/dist/pricing/index.js +8 -0
  78. package/dist/pricing/ports.d.ts +22 -0
  79. package/dist/pricing/ports.js +7 -0
  80. package/dist/pricing/pricingBreakdown.d.ts +86 -0
  81. package/dist/pricing/pricingBreakdown.js +155 -0
  82. package/dist/pricing/targetPriceAllocation.d.ts +97 -0
  83. package/dist/pricing/targetPriceAllocation.js +202 -0
  84. package/dist/pricing/types.d.ts +32 -0
  85. package/dist/pricing/types.js +4 -0
  86. package/dist/promo/adjustmentEngine.d.ts +4 -0
  87. package/dist/promo/adjustmentEngine.js +194 -0
  88. package/dist/promo/adjustmentTypes.d.ts +103 -0
  89. package/dist/promo/adjustmentTypes.js +13 -0
  90. package/dist/promo/index.d.ts +8 -0
  91. package/dist/promo/index.js +8 -0
  92. package/dist/promo/promoEvaluator.d.ts +25 -0
  93. package/dist/promo/promoEvaluator.js +135 -0
  94. package/dist/promo/types.d.ts +86 -0
  95. package/dist/promo/types.js +12 -0
  96. package/dist/risk/core/evaluator.d.ts +3 -0
  97. package/dist/risk/core/evaluator.js +32 -0
  98. package/dist/risk/core/rules.d.ts +3 -0
  99. package/dist/risk/core/rules.js +118 -0
  100. package/dist/risk/core/types.d.ts +65 -0
  101. package/dist/risk/core/types.js +1 -0
  102. package/dist/risk/index.d.ts +8 -0
  103. package/dist/risk/index.js +8 -0
  104. package/dist/risk/types.d.ts +32 -0
  105. package/dist/risk/types.js +43 -0
  106. package/dist/shipping/index.d.ts +4 -0
  107. package/dist/shipping/index.js +4 -0
  108. package/dist/shipping/shippingResolver.d.ts +26 -0
  109. package/dist/shipping/shippingResolver.js +88 -0
  110. package/dist/shipping/types.d.ts +45 -0
  111. package/dist/shipping/types.js +4 -0
  112. package/dist/subscription/cycleHardening.d.ts +72 -0
  113. package/dist/subscription/cycleHardening.js +95 -0
  114. package/dist/subscription/index.d.ts +14 -0
  115. package/dist/subscription/index.js +14 -0
  116. package/dist/subscription/subscriptionEngineCore.d.ts +13 -0
  117. package/dist/subscription/subscriptionEngineCore.js +145 -0
  118. package/dist/subscription/subscriptionEngineLifecycle.d.ts +52 -0
  119. package/dist/subscription/subscriptionEngineLifecycle.js +196 -0
  120. package/dist/subscription/subscriptionEnginePayment.d.ts +19 -0
  121. package/dist/subscription/subscriptionEnginePayment.js +127 -0
  122. package/dist/subscription/subscriptionEngineTypes.d.ts +94 -0
  123. package/dist/subscription/subscriptionEngineTypes.js +4 -0
  124. package/dist/subscription/subscriptionEngineUtils.d.ts +32 -0
  125. package/dist/subscription/subscriptionEngineUtils.js +74 -0
  126. package/dist/subscription/types.d.ts +58 -0
  127. package/dist/subscription/types.js +47 -0
  128. package/dist/testing.d.ts +48 -0
  129. package/dist/testing.js +105 -0
  130. package/package.json +215 -4
  131. package/release-gates.json +232 -0
  132. package/src/bundle/contracts.ts +15 -0
  133. package/src/bundle/ports.ts +32 -0
  134. package/src/catalog/identifiers.ts +50 -0
  135. package/src/catalog/index.ts +2 -0
  136. package/src/checkout/idempotency.ts +49 -0
  137. package/src/checkout/index.ts +2 -0
  138. package/src/company-identity/contracts.ts +209 -0
  139. package/src/company-identity/index.ts +27 -0
  140. package/src/company-identity/ports.ts +19 -0
  141. package/src/fulfillment/facts.ts +300 -0
  142. package/src/fulfillment/index.ts +34 -0
  143. package/src/inventory/atp.ts +249 -0
  144. package/src/inventory/index.ts +2 -0
  145. package/src/marketing/research/contracts.ts +91 -0
  146. package/src/marketing/research/index.ts +4 -0
  147. package/src/marketing/research/ports.ts +26 -0
  148. package/src/partners/index.ts +4 -0
  149. package/src/partners/ports.ts +18 -0
  150. package/src/partners/status.ts +49 -0
  151. package/src/payment/executionBaseContracts.ts +93 -0
  152. package/src/payment/index.ts +128 -0
  153. package/src/payment/methodLifecycle.ts +130 -0
  154. package/src/payment/paymentControlTypes.ts +197 -0
  155. package/src/payment/paymentFailureTaxonomy.ts +201 -0
  156. package/src/payment/paymentFailureTaxonomyContracts.ts +228 -0
  157. package/src/payment/paymentRecovery.ts +95 -0
  158. package/src/payment/paymentRecoveryContracts.ts +61 -0
  159. package/src/payment/paymentStateMachine.ts +263 -0
  160. package/src/payment/providerAttemptIdempotency.ts +57 -0
  161. package/src/payment/providerCapability.ts +172 -0
  162. package/src/platform-runtime/contracts.ts +33 -0
  163. package/src/platform-runtime/index.ts +36 -0
  164. package/src/platform-runtime/platformKernel.ts +86 -0
  165. package/src/platform-runtime/ports.ts +88 -0
  166. package/src/pricing/index.ts +8 -0
  167. package/src/pricing/ports.ts +27 -0
  168. package/src/pricing/pricingBreakdown.ts +243 -0
  169. package/src/pricing/targetPriceAllocation.ts +293 -0
  170. package/src/pricing/types.ts +36 -0
  171. package/src/promo/adjustmentEngine.ts +253 -0
  172. package/src/promo/adjustmentTypes.ts +94 -0
  173. package/src/promo/index.ts +8 -0
  174. package/src/promo/promoEvaluator.ts +137 -0
  175. package/src/promo/types.ts +97 -0
  176. package/src/risk/core/evaluator.ts +41 -0
  177. package/src/risk/core/rules.ts +128 -0
  178. package/src/risk/core/types.ts +73 -0
  179. package/src/risk/index.ts +8 -0
  180. package/src/risk/types.ts +60 -0
  181. package/src/shipping/index.ts +4 -0
  182. package/src/shipping/shippingResolver.ts +101 -0
  183. package/src/shipping/types.ts +49 -0
  184. package/src/subscription/cycleHardening.ts +141 -0
  185. package/src/subscription/index.ts +14 -0
  186. package/src/subscription/subscriptionEngineCore.ts +181 -0
  187. package/src/subscription/subscriptionEngineLifecycle.ts +282 -0
  188. package/src/subscription/subscriptionEnginePayment.ts +174 -0
  189. package/src/subscription/subscriptionEngineTypes.ts +112 -0
  190. package/src/subscription/subscriptionEngineUtils.ts +110 -0
  191. package/src/subscription/types.ts +104 -0
  192. package/src/testing.ts +169 -0
@@ -0,0 +1,232 @@
1
+ {
2
+ "schemaVersion": 4,
3
+ "compatibilityPolicies": {
4
+ "internal-candidate": "Candidate package surfaces may change until the platform phase-5 activation explicitly freezes a public API.",
5
+ "testing-internal": "Test-support surfaces may change and are not runtime compatibility promises."
6
+ },
7
+ "packageRelease": {
8
+ "phase": "platform-monorepo-phase-5",
9
+ "publicStability": "not-claimed",
10
+ "artifactChannel": "npm-staged-preview",
11
+ "versionRule": "0.<n>.0"
12
+ },
13
+ "evidence": {
14
+ "packageSmokeEvidence": {
15
+ "state": "required",
16
+ "meaning": "Hermetic build, import, pack and directory publish-refusal proof for this package."
17
+ },
18
+ "conformanceEvidence": {
19
+ "state": "required-for-declared-ports",
20
+ "meaning": "A framework-free port/adapter suite is required only when this package declares that seam."
21
+ },
22
+ "dogfoodEvidence": {
23
+ "state": "private-current-seams",
24
+ "meaning": "A private product currently imports candidate package seams; this does not establish a public platform adopter seam."
25
+ },
26
+ "externalConsumerEvidence": {
27
+ "state": "not-evaluated",
28
+ "meaning": "Requires a real independent adopter or public platform artifact in phase 4 or 5; first-party packed consumers remain package smoke evidence."
29
+ }
30
+ },
31
+ "packageSurface": {
32
+ "./subscription": {
33
+ "role": "kernel",
34
+ "maturity": "candidate",
35
+ "compatibilityPolicy": "internal-candidate",
36
+ "packageSmokeEvidence": ["smoke/subscriptionBundleStandalone.test.ts"],
37
+ "entrypoint": "dist/subscription/index.d.ts",
38
+ "snapshot": "api/subscription.api.md"
39
+ },
40
+ "./bundle": {
41
+ "role": "kernel",
42
+ "maturity": "candidate",
43
+ "compatibilityPolicy": "internal-candidate",
44
+ "packageSmokeEvidence": ["smoke/subscriptionBundleStandalone.test.ts"],
45
+ "entrypoint": "dist/bundle/contracts.d.ts",
46
+ "snapshot": "api/bundle.api.md"
47
+ },
48
+ "./pricing": {
49
+ "role": "kernel",
50
+ "maturity": "candidate",
51
+ "compatibilityPolicy": "internal-candidate",
52
+ "packageSmokeEvidence": ["smoke/pricingStandalone.test.ts"],
53
+ "entrypoint": "dist/pricing/index.d.ts",
54
+ "snapshot": "api/pricing.api.md"
55
+ },
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
+ "./company-identity": {
65
+ "role": "kernel",
66
+ "maturity": "experimental",
67
+ "compatibilityPolicy": "internal-candidate",
68
+ "packageSmokeEvidence": ["smoke/companyIdentityStandalone.test.ts"],
69
+ "entrypoint": "dist/company-identity/index.d.ts",
70
+ "snapshot": "api/company-identity.api.md"
71
+ },
72
+ "./fulfillment": {
73
+ "role": "kernel",
74
+ "maturity": "experimental",
75
+ "compatibilityPolicy": "internal-candidate",
76
+ "packageSmokeEvidence": ["smoke/fulfillmentStandalone.test.ts"],
77
+ "entrypoint": "dist/fulfillment/index.d.ts",
78
+ "snapshot": "api/fulfillment.api.md"
79
+ },
80
+ "./inventory": {
81
+ "role": "kernel",
82
+ "maturity": "candidate",
83
+ "compatibilityPolicy": "internal-candidate",
84
+ "packageSmokeEvidence": ["smoke/inventoryStandalone.test.ts"],
85
+ "entrypoint": "dist/inventory/index.d.ts",
86
+ "snapshot": "api/inventory.api.md"
87
+ },
88
+ "./shipping": {
89
+ "role": "kernel",
90
+ "maturity": "candidate",
91
+ "compatibilityPolicy": "internal-candidate",
92
+ "packageSmokeEvidence": ["smoke/shippingStandalone.test.ts"],
93
+ "entrypoint": "dist/shipping/index.d.ts",
94
+ "snapshot": "api/shipping.api.md"
95
+ },
96
+ "./catalog": {
97
+ "role": "kernel",
98
+ "maturity": "candidate",
99
+ "compatibilityPolicy": "internal-candidate",
100
+ "packageSmokeEvidence": ["smoke/catalogStandalone.test.ts"],
101
+ "entrypoint": "dist/catalog/index.d.ts",
102
+ "snapshot": "api/catalog.api.md"
103
+ },
104
+ "./promo": {
105
+ "role": "kernel",
106
+ "maturity": "candidate",
107
+ "compatibilityPolicy": "internal-candidate",
108
+ "packageSmokeEvidence": ["smoke/promoStandalone.test.ts"],
109
+ "entrypoint": "dist/promo/index.d.ts",
110
+ "snapshot": "api/promo.api.md"
111
+ },
112
+ "./risk": {
113
+ "role": "kernel",
114
+ "maturity": "candidate",
115
+ "compatibilityPolicy": "internal-candidate",
116
+ "packageSmokeEvidence": ["smoke/riskStandalone.test.ts"],
117
+ "entrypoint": "dist/risk/index.d.ts",
118
+ "snapshot": "api/risk.api.md"
119
+ },
120
+ "./marketing/research": {
121
+ "role": "kernel",
122
+ "maturity": "experimental",
123
+ "compatibilityPolicy": "internal-candidate",
124
+ "packageSmokeEvidence": ["smoke/marketingResearchStandalone.test.ts"],
125
+ "entrypoint": "dist/marketing/research/index.d.ts",
126
+ "snapshot": "api/marketing-research.api.md"
127
+ },
128
+ "./partners": {
129
+ "role": "kernel",
130
+ "maturity": "experimental",
131
+ "compatibilityPolicy": "internal-candidate",
132
+ "packageSmokeEvidence": ["smoke/partnersStandalone.test.ts"],
133
+ "entrypoint": "dist/partners/index.d.ts",
134
+ "snapshot": "api/partners.api.md"
135
+ },
136
+ "./payment": {
137
+ "role": "kernel",
138
+ "maturity": "experimental",
139
+ "compatibilityPolicy": "internal-candidate",
140
+ "packageSmokeEvidence": ["smoke/paymentStandalone.test.ts"],
141
+ "entrypoint": "dist/payment/index.d.ts",
142
+ "snapshot": "api/payment.api.md"
143
+ },
144
+ "./platform-runtime": {
145
+ "role": "kernel",
146
+ "maturity": "experimental",
147
+ "compatibilityPolicy": "internal-candidate",
148
+ "packageSmokeEvidence": ["smoke/platformRuntimeStandalone.test.ts"],
149
+ "entrypoint": "dist/platform-runtime/index.d.ts",
150
+ "snapshot": "api/platform-runtime.api.md"
151
+ },
152
+ "./testing": {
153
+ "role": "testing",
154
+ "maturity": "testing",
155
+ "compatibilityPolicy": "testing-internal",
156
+ "packageSmokeEvidence": ["smoke/testingStandalone.test.ts"],
157
+ "entrypoint": "dist/testing.d.ts",
158
+ "snapshot": "api/testing.api.md"
159
+ }
160
+ },
161
+ "requiredRepositoryArtifacts": [
162
+ ".github/CODEOWNERS",
163
+ ".github/dependabot.yml",
164
+ ".github/workflows/ci.yml",
165
+ "CHANGELOG.md",
166
+ "CODE_OF_CONDUCT.md",
167
+ "CONTRIBUTING.md",
168
+ "LICENSE",
169
+ "MAINTAINERS.md",
170
+ "README.md",
171
+ "SECURITY.md",
172
+ "package-lock.json",
173
+ "package.json",
174
+ "release-gates.json",
175
+ "repository-policy.json",
176
+ "scripts/api-contract.ts",
177
+ "scripts/core-package-consumer-audit.ts",
178
+ "scripts/core-package-consumer-fixed-box-fixture.ts",
179
+ "scripts/core-package-consumer-fixed-catalog-subscription-fixture.ts",
180
+ "scripts/core-package-consumer-smoke.ts",
181
+ "scripts/documentation-contract.ts",
182
+ "scripts/neutrality-shell-fold.ts",
183
+ "scripts/neutrality-source-scanner.ts",
184
+ "scripts/public-api-config.ts",
185
+ "scripts/refuse-publish.ts",
186
+ "scripts/release-bundle.ts",
187
+ "scripts/release-check.ts",
188
+ "scripts/release-npm-checks.ts",
189
+ "scripts/repository-policy-check.ts",
190
+ "test/assertNoZeroCoverage.ts",
191
+ "test/documentationContract.test.ts",
192
+ "test/publicApiConfig.test.ts",
193
+ "test/releaseBundle.test.ts",
194
+ "test/repositoryPolicy.test.ts"
195
+ ],
196
+ "productionLicenses": {
197
+ "allow": [
198
+ "Apache-2.0",
199
+ "MIT"
200
+ ]
201
+ },
202
+ "audit": {
203
+ "maximumVulnerabilities": {
204
+ "critical": 0,
205
+ "high": 0,
206
+ "moderate": 0,
207
+ "low": 0,
208
+ "info": 0
209
+ }
210
+ },
211
+ "pack": {
212
+ "$raiseReason": "2026-09-11, owner-approved: checkout refusal recovery adds two neutral contract and policy modules with their normal source, JavaScript and declaration outputs. The reviewed artifact measured 122504 packed bytes, 533693 unpacked bytes and 192 files before this note. Packed allowance increases by 2048 bytes and unpacked by 6144 bytes; maxFiles stays 192. Exports, dependencies, runtime behavior and all other release gates remain unchanged by this exception.",
213
+ "maxPackedBytes": 122880,
214
+ "maxUnpackedBytes": 534528,
215
+ "maxFiles": 192,
216
+ "requiredFiles": [
217
+ "CHANGELOG.md",
218
+ "CODE_OF_CONDUCT.md",
219
+ "CONTRIBUTING.md",
220
+ "LICENSE",
221
+ "MAINTAINERS.md",
222
+ "README.md",
223
+ "SECURITY.md",
224
+ "package.json",
225
+ "release-gates.json"
226
+ ]
227
+ },
228
+ "publish": {
229
+ "prepublishOnly": "node --experimental-strip-types ./scripts/refuse-publish.ts",
230
+ "refusalMarker": "CORE_DIRECTORY_PUBLISH_REFUSED"
231
+ }
232
+ }
@@ -0,0 +1,15 @@
1
+ import type { BundleLineRef, CompositionConstraint } from "./ports.js";
2
+
3
+ /** @beta */
4
+ export type {
5
+ BundleLineRef,
6
+ CompositionConstraint,
7
+ CompositionRulesPort,
8
+ } from "./ports.js";
9
+
10
+ /** @beta */
11
+ export interface Bundle {
12
+ coreLines: BundleLineRef[];
13
+ addonLines: BundleLineRef[];
14
+ constraint: CompositionConstraint;
15
+ }
@@ -0,0 +1,32 @@
1
+ /** Opaque, adopter-defined sizing anchor with a minimal discriminator. */
2
+ /** @beta */
3
+ export interface CompositionConstraint {
4
+ /** Adapter-owned constraint family, for example "fixed.catalog". */
5
+ kind: string;
6
+ /** Adapter-owned schema version for data. */
7
+ version: number;
8
+ /** Adapter-owned payload; core passes it through without inspecting it. */
9
+ data: Record<string, unknown>;
10
+ }
11
+
12
+ /** @beta */
13
+ export interface BundleLineRef {
14
+ variantId: string;
15
+ qty: number;
16
+ isAddon: boolean;
17
+ }
18
+
19
+ /** @beta */
20
+ export interface CompositionRulesPort {
21
+ validateComposition(input: {
22
+ coreLines: BundleLineRef[];
23
+ addonLines: BundleLineRef[];
24
+ constraint: CompositionConstraint;
25
+ }): Promise<{ ok: true } | { ok: false; code: string; details?: Record<string, unknown> }>;
26
+
27
+ resizeComposition(input: {
28
+ coreLines: BundleLineRef[];
29
+ constraint: CompositionConstraint;
30
+ lever: { kind: string; value: unknown };
31
+ }): Promise<{ coreLines: BundleLineRef[]; constraint: CompositionConstraint } | null>;
32
+ }
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Open-but-validated catalog identifier primitives.
5
+ *
6
+ * These schemas validate shape only; membership belongs to the host
7
+ * application's catalog read port. A runtime-editable catalog must accept new
8
+ * slugs, SKUs, product ids, and variant ids without requiring a package release.
9
+ * Keep these primitives format-bound, not enum-bound.
10
+ */
11
+ /** @beta */
12
+ export const catalogSlugFormat = /^[a-z0-9][a-z0-9_-]*$/;
13
+ /** @beta */
14
+ export const slugSchema = z.string().min(1).max(64).regex(catalogSlugFormat);
15
+
16
+ /**
17
+ * A purchasable SKU. Charset and length are bounded, but the schema does not
18
+ * encode packaging, weight, brand, or merchant-specific prefixes.
19
+ */
20
+ /** @beta */
21
+ export const skuSchema = z
22
+ .string()
23
+ .trim()
24
+ .min(1)
25
+ .max(64)
26
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
27
+
28
+ /**
29
+ * A catalog product id. Accepts static ids and database ids as long as their
30
+ * shape is bounded; existence is checked by the host catalog data source.
31
+ */
32
+ /** @beta */
33
+ export const catalogProductIdSchema = z
34
+ .string()
35
+ .trim()
36
+ .min(1)
37
+ .max(64)
38
+ .regex(/^[A-Za-z0-9][A-Za-z0-9_-]*$/);
39
+
40
+ /**
41
+ * A variant id. Accepts static ids and database ids without requiring a literal
42
+ * prefix owned by the package.
43
+ */
44
+ /** @beta */
45
+ export const variantIdSchema = z
46
+ .string()
47
+ .trim()
48
+ .min(1)
49
+ .max(64)
50
+ .regex(/^[A-Za-z0-9][A-Za-z0-9_-]*$/);
@@ -0,0 +1,2 @@
1
+ /** @beta */
2
+ export * from "./identifiers.js";
@@ -0,0 +1,49 @@
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
+ }
@@ -0,0 +1,2 @@
1
+ /** @beta */
2
+ export * from "./idempotency.js";
@@ -0,0 +1,209 @@
1
+ import { z } from "zod";
2
+
3
+ /** @beta */
4
+ export const COMPANY_IDENTITY_CONTRACT_VERSION = "company_identity.lookup.v1" as const;
5
+
6
+ /** Why the lookup is being performed — lets an adapter apply purpose-specific
7
+ * policy (e.g. stricter verification for a checkout invoice than for a saved
8
+ * billing profile). */
9
+ /** @beta */
10
+ export const companyIdentityPurposeSchema = z.enum([
11
+ "checkout_invoice",
12
+ "customer_billing_profile",
13
+ ]);
14
+
15
+ /**
16
+ * How much the returned company data can be trusted:
17
+ * - `registry_verified` — confirmed against an authoritative registry;
18
+ * - `provider_verified` — confirmed via a commercial data provider (weaker);
19
+ * - `manual_unverified` — supplied by the user, not checked;
20
+ * - `invalid` — the identifier failed format/existence checks.
21
+ */
22
+ /** @beta */
23
+ export const companyIdentityVerificationLevelSchema = z.enum([
24
+ "registry_verified",
25
+ "provider_verified",
26
+ "manual_unverified",
27
+ "invalid",
28
+ ]);
29
+
30
+ /**
31
+ * Outcome of a lookup. Distinguishes "no data" reasons so callers branch on
32
+ * data, not exceptions:
33
+ * - `found` — a single company resolved;
34
+ * - `not_found` — identifier valid but no record;
35
+ * - `partial` — some fields resolved, others missing;
36
+ * - `invalid` — identifier failed format/checksum;
37
+ * - `ambiguous` — more than one candidate;
38
+ * - `unsupported_country` — no adapter for this country;
39
+ * - `provider_unavailable` — transient upstream failure (retryable).
40
+ */
41
+ /** @beta */
42
+ export const companyIdentityLookupStatusSchema = z.enum([
43
+ "found",
44
+ "not_found",
45
+ "partial",
46
+ "invalid",
47
+ "ambiguous",
48
+ "unsupported_country",
49
+ "provider_unavailable",
50
+ ]);
51
+
52
+ /** Per-source outcome inside a lookup (a lookup may consult several sources).
53
+ * Same vocabulary as the overall status minus the request-level
54
+ * `unsupported_country`, which is decided before any source is queried. */
55
+ /** @beta */
56
+ export const companyIdentitySourceStatusSchema = z.enum([
57
+ "found",
58
+ "not_found",
59
+ "partial",
60
+ "invalid",
61
+ "ambiguous",
62
+ "provider_unavailable",
63
+ ]);
64
+
65
+ /** Open, format-validated identifier kind (e.g. a national tax-id scheme key).
66
+ * Deliberately NOT a closed enum: the neutral core does not enumerate
67
+ * country-specific schemes — the host adapter decides which kinds it accepts. */
68
+ /** @beta */
69
+ export const companyIdentityIdentifierKindSchema = z
70
+ .string()
71
+ .trim()
72
+ .toLowerCase()
73
+ .min(1)
74
+ .max(80)
75
+ .regex(/^[a-z0-9_:-]+$/);
76
+
77
+ /** @beta */
78
+ export const companyIdentityAddressSchema = z
79
+ .object({
80
+ line1: z.string().trim().min(1).max(240),
81
+ postalCode: z.string().trim().min(1).max(16),
82
+ city: z.string().trim().min(1).max(120),
83
+ country: z.string().trim().toUpperCase().length(2),
84
+ })
85
+ .strict();
86
+
87
+ /** @beta */
88
+ export const companyIdentityCompanySchema = z
89
+ .object({
90
+ country: z.string().trim().toUpperCase().length(2),
91
+ identifierKind: companyIdentityIdentifierKindSchema,
92
+ identifierValue: z.string().trim().min(1).max(80),
93
+ legalName: z.string().trim().min(1).max(240).nullable(),
94
+ registeredAddress: companyIdentityAddressSchema.nullable(),
95
+ registryStatus: z.string().trim().min(1).max(80).nullable(),
96
+ })
97
+ .strict();
98
+
99
+ /** @beta */
100
+ export const companyIdentitySourceSchema = z
101
+ .object({
102
+ providerKind: z.string().trim().min(1).max(120),
103
+ status: companyIdentitySourceStatusSchema,
104
+ observedAt: z.string().datetime({ offset: true }),
105
+ evidenceHash: z.string().trim().min(8).max(160).nullable(),
106
+ })
107
+ .strict();
108
+
109
+ /** A lookup request. `country` is an open ISO-3166 alpha-2 code (not restricted
110
+ * to any single country); `identifierKind`/`identifierValue` name and carry the
111
+ * tax/registry id; `manualCompany` optionally seeds user-entered data an adapter
112
+ * may reconcile against a registry. Parsing normalizes country to upper-case and
113
+ * identifier kind to lower-case. */
114
+ /** @beta */
115
+ export const companyIdentityLookupRequestSchema = z
116
+ .object({
117
+ country: z.string().trim().toUpperCase().length(2),
118
+ identifierKind: companyIdentityIdentifierKindSchema,
119
+ identifierValue: z.string().trim().min(1).max(120),
120
+ purpose: companyIdentityPurposeSchema,
121
+ manualCompany: companyIdentityCompanySchema.partial({
122
+ identifierKind: true,
123
+ identifierValue: true,
124
+ country: true,
125
+ registryStatus: true,
126
+ }).optional(),
127
+ })
128
+ .strict()
129
+ .transform((request) => normalizeCompanyIdentityLookupRequest(request as NormalizableCompanyIdentityLookupRequest));
130
+
131
+ /** A lookup result. `status` + `verificationLevel` say what happened and how
132
+ * trustworthy it is; `company` is null unless resolved; `sources` records each
133
+ * consulted provider's per-source outcome; `warnings` carries non-fatal notes. */
134
+ /** @beta */
135
+ export const companyIdentityLookupResponseSchema = z
136
+ .object({
137
+ contractVersion: z.literal(COMPANY_IDENTITY_CONTRACT_VERSION),
138
+ status: companyIdentityLookupStatusSchema,
139
+ verificationLevel: companyIdentityVerificationLevelSchema,
140
+ company: companyIdentityCompanySchema.nullable(),
141
+ sources: z.array(companyIdentitySourceSchema),
142
+ warnings: z.array(z.string().trim().min(1).max(120)).default([]),
143
+ })
144
+ .strict();
145
+
146
+ /** @beta */
147
+ export type CompanyIdentityLookupRequest = z.infer<typeof companyIdentityLookupRequestSchema>;
148
+ /** @beta */
149
+ export type CompanyIdentityLookupResponse = z.infer<typeof companyIdentityLookupResponseSchema>;
150
+ /** @beta */
151
+ export type CompanyIdentityCompany = z.infer<typeof companyIdentityCompanySchema>;
152
+ /** @beta */
153
+ export type CompanyIdentityPurpose = z.infer<typeof companyIdentityPurposeSchema>;
154
+ /** @beta */
155
+ export type CompanyIdentitySource = z.infer<typeof companyIdentitySourceSchema>;
156
+ /** @beta */
157
+ export type CompanyIdentitySourceStatus = z.infer<typeof companyIdentitySourceStatusSchema>;
158
+ /** @beta */
159
+ export type CompanyIdentityVerificationLevel = z.infer<typeof companyIdentityVerificationLevelSchema>;
160
+
161
+ interface NormalizableCompanyIdentityLookupRequest {
162
+ country: string;
163
+ identifierKind: string;
164
+ identifierValue: string;
165
+ purpose: CompanyIdentityPurpose;
166
+ manualCompany?: Partial<CompanyIdentityCompany>;
167
+ }
168
+
169
+ function normalizeCompanyIdentityLookupRequest(
170
+ request: NormalizableCompanyIdentityLookupRequest,
171
+ ): NormalizableCompanyIdentityLookupRequest {
172
+ const country = String(request.country).trim().toUpperCase();
173
+ const identifierKind = String(request.identifierKind).trim().toLowerCase();
174
+ const identifierValue = request.identifierValue.trim();
175
+
176
+ const normalized = {
177
+ country,
178
+ identifierKind,
179
+ identifierValue,
180
+ purpose: request.purpose,
181
+ };
182
+ if (request.manualCompany) {
183
+ return {
184
+ ...normalized,
185
+ manualCompany: {
186
+ ...request.manualCompany,
187
+ country,
188
+ identifierKind,
189
+ identifierValue,
190
+ registryStatus: request.manualCompany.registryStatus ?? "manual",
191
+ },
192
+ };
193
+ }
194
+ return normalized;
195
+ }
196
+
197
+ /** True when the company has the minimum fields needed to render/persist a full
198
+ * billing identity: legal name plus a complete registered address. Used to gate
199
+ * whether a `partial` lookup is usable for its purpose. */
200
+ /** @beta */
201
+ export function isCompleteCompanyIdentity(company: CompanyIdentityCompany | null): boolean {
202
+ return Boolean(
203
+ company?.legalName &&
204
+ company.registeredAddress?.line1 &&
205
+ company.registeredAddress.postalCode &&
206
+ company.registeredAddress.city &&
207
+ company.registeredAddress.country,
208
+ );
209
+ }
@@ -0,0 +1,27 @@
1
+ /** @beta */
2
+ export {
3
+ COMPANY_IDENTITY_CONTRACT_VERSION,
4
+ companyIdentityAddressSchema,
5
+ companyIdentityCompanySchema,
6
+ companyIdentityIdentifierKindSchema,
7
+ companyIdentityLookupRequestSchema,
8
+ companyIdentityLookupResponseSchema,
9
+ companyIdentityLookupStatusSchema,
10
+ companyIdentityPurposeSchema,
11
+ companyIdentitySourceSchema,
12
+ companyIdentitySourceStatusSchema,
13
+ companyIdentityVerificationLevelSchema,
14
+ isCompleteCompanyIdentity,
15
+ } from "./contracts.js";
16
+ /** @beta */
17
+ export type {
18
+ CompanyIdentityCompany,
19
+ CompanyIdentityLookupRequest,
20
+ CompanyIdentityLookupResponse,
21
+ CompanyIdentityPurpose,
22
+ CompanyIdentitySource,
23
+ CompanyIdentitySourceStatus,
24
+ CompanyIdentityVerificationLevel,
25
+ } from "./contracts.js";
26
+ /** @beta */
27
+ export type { CompanyIdentityLookupPort } from "./ports.js";
@@ -0,0 +1,19 @@
1
+ import type {
2
+ CompanyIdentityLookupRequest,
3
+ CompanyIdentityLookupResponse,
4
+ } from "./contracts.js";
5
+
6
+ /**
7
+ * Provider seam for resolving a company's registry identity from a tax/registry
8
+ * identifier. The host application supplies the adapter (a national registry
9
+ * client, a commercial data provider, or a manual store); the core owns only
10
+ * this request/response contract. Adapters must not throw on a "not found" or
11
+ * "unsupported country" outcome — those are represented in the response
12
+ * `status`, so callers can branch on data rather than exceptions.
13
+ */
14
+ /** @beta */
15
+ export interface CompanyIdentityLookupPort {
16
+ lookupCompanyIdentity(
17
+ request: CompanyIdentityLookupRequest,
18
+ ): Promise<CompanyIdentityLookupResponse>;
19
+ }