@openlup/core 0.0.0 → 0.7.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 +225 -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 +85 -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 +167 -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 +95 -0
  113. package/dist/subscription/cycleHardening.js +118 -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 +150 -0
  119. package/dist/subscription/subscriptionEngineLifecycle.js +433 -0
  120. package/dist/subscription/subscriptionEnginePayment.d.ts +25 -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 +208 -0
  139. package/src/company-identity/index.ts +28 -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 +174 -0
  185. package/src/subscription/index.ts +14 -0
  186. package/src/subscription/subscriptionEngineCore.ts +181 -0
  187. package/src/subscription/subscriptionEngineLifecycle.ts +574 -0
  188. package/src/subscription/subscriptionEnginePayment.ts +179 -0
  189. package/src/subscription/subscriptionEngineTypes.ts +114 -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,32 @@
1
+ import type { SubscriptionEngineEventType, SubscriptionStatus } from "./types.js";
2
+ import type { EngineCycle, EngineEvent, EngineSubscription, SubscriptionEngineErrorCode, SubscriptionEngineResult } from "./subscriptionEngineTypes.js";
3
+ import type { SubscriptionTemplateSnapshot } from "./types.js";
4
+ /** @beta */
5
+ export declare function touchSubscription(subscription: EngineSubscription, updatedAt: string, patch: Partial<EngineSubscription>): EngineSubscription;
6
+ /** @beta */
7
+ export declare function makeEvent(input: {
8
+ eventType: SubscriptionEngineEventType;
9
+ subscription: EngineSubscription;
10
+ cycleNumber?: number;
11
+ idempotencyKey: string;
12
+ occurredAt: string;
13
+ payload: Record<string, unknown>;
14
+ }): EngineEvent;
15
+ /** @beta */
16
+ export declare function addCadenceDays(iso: string, cadenceDays: number): string | null;
17
+ /** @beta */
18
+ export declare function parseIso(iso: string): Date | null;
19
+ /** @beta */
20
+ export declare function validateTemplate(template: SubscriptionTemplateSnapshot): SubscriptionEngineResult<never> | null;
21
+ /** @beta */
22
+ export declare function isTerminalCycle(cycle: EngineCycle): boolean;
23
+ /** @beta */
24
+ export declare function cloneTemplate(template: SubscriptionTemplateSnapshot): SubscriptionTemplateSnapshot;
25
+ /** @beta */
26
+ export declare function cloneRecord<T extends Record<string, unknown>>(record: T): T;
27
+ /** @beta */
28
+ export declare function success<T>(value: T): SubscriptionEngineResult<T>;
29
+ /** @beta */
30
+ export declare function failure(code: SubscriptionEngineErrorCode, message: string): SubscriptionEngineResult<never>;
31
+ /** @beta */
32
+ export declare function statusPatch(status: SubscriptionStatus): Partial<EngineSubscription>;
@@ -0,0 +1,74 @@
1
+ /** @beta */
2
+ export function touchSubscription(subscription, updatedAt, patch) {
3
+ return {
4
+ ...subscription,
5
+ ...patch,
6
+ template: patch.template ? cloneTemplate(patch.template) : cloneTemplate(subscription.template),
7
+ updatedAt,
8
+ };
9
+ }
10
+ /** @beta */
11
+ export function makeEvent(input) {
12
+ return {
13
+ eventType: input.eventType,
14
+ subscriptionId: input.subscription.id,
15
+ cycleNumber: input.cycleNumber,
16
+ idempotencyKey: input.idempotencyKey,
17
+ occurredAt: input.occurredAt,
18
+ payload: input.payload,
19
+ };
20
+ }
21
+ /** @beta */
22
+ export function addCadenceDays(iso, cadenceDays) {
23
+ const date = parseIso(iso);
24
+ if (!date || cadenceDays < 1)
25
+ return null;
26
+ return new Date(date.getTime() + cadenceDays * 24 * 60 * 60 * 1000).toJSON();
27
+ }
28
+ /** @beta */
29
+ export function parseIso(iso) {
30
+ const date = new Date(iso);
31
+ if (Number.isNaN(date.getTime()))
32
+ return null;
33
+ return date;
34
+ }
35
+ /** @beta */
36
+ export function validateTemplate(template) {
37
+ if (template.cadence_days < 1 || template.lines.length === 0) {
38
+ return failure("invalid_template", "template must have a positive cadence and at least one line");
39
+ }
40
+ for (const line of template.lines) {
41
+ if (line.qty < 1) {
42
+ return failure("invalid_template", "template lines must have positive quantities");
43
+ }
44
+ }
45
+ return null;
46
+ }
47
+ /** @beta */
48
+ export function isTerminalCycle(cycle) {
49
+ return cycle.status === "paid" || cycle.status === "skipped" || cycle.status === "cancelled";
50
+ }
51
+ /** @beta */
52
+ export function cloneTemplate(template) {
53
+ return {
54
+ ...template,
55
+ size_constraint: template.size_constraint ? cloneRecord(template.size_constraint) : null,
56
+ lines: template.lines.map((line) => ({ ...line })),
57
+ };
58
+ }
59
+ /** @beta */
60
+ export function cloneRecord(record) {
61
+ return JSON.parse(JSON.stringify(record));
62
+ }
63
+ /** @beta */
64
+ export function success(value) {
65
+ return { ok: true, value };
66
+ }
67
+ /** @beta */
68
+ export function failure(code, message) {
69
+ return { ok: false, error: { code, message } };
70
+ }
71
+ /** @beta */
72
+ export function statusPatch(status) {
73
+ return { status };
74
+ }
@@ -0,0 +1,58 @@
1
+ /** @beta */
2
+ export declare const SUBSCRIPTION_STATUSES: readonly ["active", "paused", "cancelled", "completed"];
3
+ /** @beta */
4
+ export type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];
5
+ /** @beta */
6
+ export declare const SUBSCRIPTION_RECORD_STATUSES: readonly ["pending_activation", "active", "paused", "cancelled", "completed", "activation_failed"];
7
+ /** @beta */
8
+ export type SubscriptionRecordStatus = (typeof SUBSCRIPTION_RECORD_STATUSES)[number];
9
+ /** @beta */
10
+ export declare const SUBSCRIPTION_CYCLE_STATUSES: readonly ["planned", "payment_pending", "paid", "payment_failed", "retry_scheduled", "skipped", "cancelled"];
11
+ /** @beta */
12
+ export type SubscriptionCycleStatus = (typeof SUBSCRIPTION_CYCLE_STATUSES)[number];
13
+ /** @beta */
14
+ export declare const SUBSCRIPTION_ENGINE_EVENT_TYPES: readonly ["subscription.created", "subscription.template_updated", "subscription.edit_window_opened", "subscription.next_cycle_at_updated", "subscription.cycle_planned", "subscription.payment_requested", "subscription.payment_failed", "subscription.retry_scheduled", "subscription.payment_recovered", "subscription.cycle_paid", "subscription.cycle_skipped", "subscription.cycle_cancelled", "subscription.paused", "subscription.resumed", "subscription.cancelled", "subscription.completed"];
15
+ /** @beta */
16
+ export type SubscriptionEngineEventType = (typeof SUBSCRIPTION_ENGINE_EVENT_TYPES)[number];
17
+ /** @beta */
18
+ export interface SubscriptionLineSnapshot {
19
+ variant_id: string;
20
+ qty: number;
21
+ sort_order: number;
22
+ is_addon: boolean;
23
+ }
24
+ /** @beta */
25
+ export interface SubscriptionTemplateSnapshot {
26
+ cadence_days: number;
27
+ size_constraint: Record<string, unknown> | null;
28
+ currency: string;
29
+ region_code: string;
30
+ edit_window_hours?: number | null;
31
+ lines: SubscriptionLineSnapshot[];
32
+ }
33
+ /** @beta */
34
+ export interface SubscriptionEngineEvent {
35
+ event_id: string;
36
+ event_type: SubscriptionEngineEventType;
37
+ subscription_id: string;
38
+ cycle_number?: number;
39
+ template?: SubscriptionTemplateSnapshot;
40
+ next_cycle_at?: string;
41
+ payment_payload?: {
42
+ payment_id?: string;
43
+ amount_minor: number;
44
+ currency: string;
45
+ order_lines: Array<{
46
+ variant_id: string;
47
+ qty: number;
48
+ unit_price_minor: number;
49
+ }>;
50
+ };
51
+ raw_payload: Record<string, unknown>;
52
+ }
53
+ /** @beta */
54
+ export interface CreateSubscriptionInput {
55
+ template: SubscriptionTemplateSnapshot;
56
+ client_ref: string;
57
+ payment_method_ref: string;
58
+ }
@@ -0,0 +1,47 @@
1
+ /** @beta */
2
+ export const SUBSCRIPTION_STATUSES = ["active", "paused", "cancelled", "completed"];
3
+ // Full set of `subscriptions.status` values persisted in the DB (see the
4
+ // `subscriptions.status` CHECK constraint). This is a SUPERSET of the
5
+ // engine-managed lifecycle states above: it also includes the activation-flow
6
+ // states (`pending_activation`, `activation_failed`) that the recurring engine
7
+ // never transitions between. Read/display contracts that echo a stored row MUST
8
+ // accept all of these; the engine transition logic stays scoped to
9
+ // SUBSCRIPTION_STATUSES.
10
+ /** @beta */
11
+ export const SUBSCRIPTION_RECORD_STATUSES = [
12
+ "pending_activation",
13
+ "active",
14
+ "paused",
15
+ "cancelled",
16
+ "completed",
17
+ "activation_failed",
18
+ ];
19
+ /** @beta */
20
+ export const SUBSCRIPTION_CYCLE_STATUSES = [
21
+ "planned",
22
+ "payment_pending",
23
+ "paid",
24
+ "payment_failed",
25
+ "retry_scheduled",
26
+ "skipped",
27
+ "cancelled",
28
+ ];
29
+ /** @beta */
30
+ export const SUBSCRIPTION_ENGINE_EVENT_TYPES = [
31
+ "subscription.created",
32
+ "subscription.template_updated",
33
+ "subscription.edit_window_opened",
34
+ "subscription.next_cycle_at_updated",
35
+ "subscription.cycle_planned",
36
+ "subscription.payment_requested",
37
+ "subscription.payment_failed",
38
+ "subscription.retry_scheduled",
39
+ "subscription.payment_recovered",
40
+ "subscription.cycle_paid",
41
+ "subscription.cycle_skipped",
42
+ "subscription.cycle_cancelled",
43
+ "subscription.paused",
44
+ "subscription.resumed",
45
+ "subscription.cancelled",
46
+ "subscription.completed",
47
+ ];
@@ -0,0 +1,48 @@
1
+ import { type PaymentExecutionBaseInput, type PaymentExecutionBaseResult } from "./payment/index.js";
2
+ /** @internal */
3
+ export type { PaymentExecutionBaseInput } from "./payment/index.js";
4
+ /** @internal */
5
+ export declare const neutralBrandFixture: {
6
+ readonly displayName: "Example Store";
7
+ readonly publicOrigin: "https://example.test";
8
+ };
9
+ /** @internal */
10
+ export declare const paymentExecutionBaseContractInput: {
11
+ idempotencyKey: string;
12
+ providerIdempotencyKey: string;
13
+ providerRequestFingerprint: string;
14
+ paymentIntentId: string;
15
+ amountMinor: number;
16
+ currency: string;
17
+ mode: "one_time";
18
+ };
19
+ /** @internal */
20
+ export type PaymentExecutionContractResult = PaymentExecutionBaseResult & {
21
+ rawProviderPayload?: Record<string, unknown> | null;
22
+ };
23
+ /** @internal */
24
+ export type PaymentExecutionContractValidationIssueCode = "provider_mismatch" | "invalid_provider_attempt_id" | "invalid_provider_session_id" | "invalid_attempt_status" | "invalid_next_action_kind" | "invalid_request_payload" | "invalid_response_payload" | "missing_provider_idempotency_key" | "missing_provider_request_fingerprint" | "sensitive_value_leaked";
25
+ /** @internal */
26
+ export interface PaymentExecutionContractValidationIssue {
27
+ code: PaymentExecutionContractValidationIssueCode;
28
+ message: string;
29
+ path: string;
30
+ }
31
+ /** @internal */
32
+ export type PaymentExecutionContractValidationResult = {
33
+ ok: true;
34
+ issues: [];
35
+ } | {
36
+ ok: false;
37
+ issues: PaymentExecutionContractValidationIssue[];
38
+ };
39
+ /** @internal */
40
+ export interface PaymentExecutionContractValidationInput<TInput extends PaymentExecutionBaseInput = PaymentExecutionBaseInput, TResult extends PaymentExecutionContractResult = PaymentExecutionContractResult> {
41
+ input: TInput;
42
+ result: TResult;
43
+ expectedProvider: string;
44
+ allowedNextActionKinds?: readonly string[];
45
+ sensitiveValues?: readonly string[];
46
+ }
47
+ /** @internal */
48
+ export declare function validatePaymentExecutionContractResult<TInput extends PaymentExecutionBaseInput = PaymentExecutionBaseInput, TResult extends PaymentExecutionContractResult = PaymentExecutionContractResult>(context: PaymentExecutionContractValidationInput<TInput, TResult>): PaymentExecutionContractValidationResult;
@@ -0,0 +1,105 @@
1
+ import { PAYMENT_EXECUTION_ATTEMPT_STATUSES, } from "./payment/index.js";
2
+ /** @internal */
3
+ export const neutralBrandFixture = {
4
+ displayName: "Example Store",
5
+ publicOrigin: "https://example.test",
6
+ };
7
+ /** @internal */
8
+ export const paymentExecutionBaseContractInput = {
9
+ idempotencyKey: "checkout-contract:payment-execution",
10
+ providerIdempotencyKey: "contract:example-pay:payment-intent-contract:checkout-payment-execution",
11
+ providerRequestFingerprint: "example-pay|payment-intent-contract|4200|USD|one_time|order_contract",
12
+ paymentIntentId: "11111111-1111-4111-8111-111111111111",
13
+ amountMinor: 4200,
14
+ currency: "USD",
15
+ mode: "one_time",
16
+ };
17
+ /** @internal */
18
+ export function validatePaymentExecutionContractResult(context) {
19
+ const issues = [];
20
+ const { input, result } = context;
21
+ if (result.provider !== context.expectedProvider) {
22
+ issues.push({
23
+ code: "provider_mismatch",
24
+ path: "provider",
25
+ message: `Expected provider ${context.expectedProvider}.`,
26
+ });
27
+ }
28
+ if (result.providerAttemptId !== null && typeof result.providerAttemptId !== "string") {
29
+ issues.push({
30
+ code: "invalid_provider_attempt_id",
31
+ path: "providerAttemptId",
32
+ message: "Expected providerAttemptId to be null or a string.",
33
+ });
34
+ }
35
+ if (result.providerSessionId !== null && typeof result.providerSessionId !== "string") {
36
+ issues.push({
37
+ code: "invalid_provider_session_id",
38
+ path: "providerSessionId",
39
+ message: "Expected providerSessionId to be null or a string.",
40
+ });
41
+ }
42
+ if (!PAYMENT_EXECUTION_ATTEMPT_STATUSES.includes(result.attemptStatus)) {
43
+ issues.push({
44
+ code: "invalid_attempt_status",
45
+ path: "attemptStatus",
46
+ message: "Expected attemptStatus to be a provider-execution attempt status.",
47
+ });
48
+ }
49
+ if (result.nextActionKind !== null
50
+ && !(context.allowedNextActionKinds ?? []).includes(result.nextActionKind)) {
51
+ issues.push({
52
+ code: "invalid_next_action_kind",
53
+ path: "nextActionKind",
54
+ message: "Expected nextActionKind to be null or listed in allowedNextActionKinds.",
55
+ });
56
+ }
57
+ if (!isRecordPayload(result.requestPayload)) {
58
+ issues.push({
59
+ code: "invalid_request_payload",
60
+ path: "requestPayload",
61
+ message: "Expected requestPayload to be a non-array object.",
62
+ });
63
+ }
64
+ else {
65
+ if (result.requestPayload.providerIdempotencyKey !== input.providerIdempotencyKey) {
66
+ issues.push({
67
+ code: "missing_provider_idempotency_key",
68
+ path: "requestPayload.providerIdempotencyKey",
69
+ message: "Expected requestPayload to persist providerIdempotencyKey.",
70
+ });
71
+ }
72
+ if (result.requestPayload.providerRequestFingerprint !== input.providerRequestFingerprint) {
73
+ issues.push({
74
+ code: "missing_provider_request_fingerprint",
75
+ path: "requestPayload.providerRequestFingerprint",
76
+ message: "Expected requestPayload to persist providerRequestFingerprint.",
77
+ });
78
+ }
79
+ }
80
+ if (!isRecordPayload(result.responsePayload)) {
81
+ issues.push({
82
+ code: "invalid_response_payload",
83
+ path: "responsePayload",
84
+ message: "Expected responsePayload to be a non-array object.",
85
+ });
86
+ }
87
+ const persistedPayloadJson = JSON.stringify({
88
+ requestPayload: result.requestPayload,
89
+ responsePayload: result.responsePayload,
90
+ rawProviderPayload: result.rawProviderPayload ?? null,
91
+ });
92
+ for (const sensitiveValue of context.sensitiveValues ?? []) {
93
+ if (persistedPayloadJson.includes(sensitiveValue)) {
94
+ issues.push({
95
+ code: "sensitive_value_leaked",
96
+ path: "requestPayload|responsePayload|rawProviderPayload",
97
+ message: "Expected persisted provider payloads not to contain sensitive values.",
98
+ });
99
+ }
100
+ }
101
+ return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
102
+ }
103
+ function isRecordPayload(payload) {
104
+ return Boolean(payload) && typeof payload === "object" && !Array.isArray(payload);
105
+ }
package/package.json CHANGED
@@ -1,17 +1,228 @@
1
1
  {
2
2
  "name": "@openlup/core",
3
- "version": "0.0.0",
4
- "description": "Placeholder that reserves the package name; the first real version is staged by the OpenLup release workflow.",
3
+ "version": "0.7.0",
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": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/openlup/openlup.git",
9
9
  "directory": "packages/core"
10
10
  },
11
- "homepage": "https://github.com/openlup/openlup/tree/main/packages/core",
12
- "files": ["README.md"],
11
+ "type": "module",
12
+ "engines": {
13
+ "node": "24.x"
14
+ },
15
+ "packageManager": "npm@11.19.0",
13
16
  "publishConfig": {
14
17
  "access": "public",
18
+ "provenance": true,
15
19
  "tag": "preview"
20
+ },
21
+ "sideEffects": false,
22
+ "files": [
23
+ "dist/**",
24
+ "src/**",
25
+ "CHANGELOG.md",
26
+ "CODE_OF_CONDUCT.md",
27
+ "CONTRIBUTING.md",
28
+ "LICENSE",
29
+ "MAINTAINERS.md",
30
+ "README.md",
31
+ "SECURITY.md",
32
+ "release-gates.json"
33
+ ],
34
+ "scripts": {
35
+ "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json",
36
+ "api:check": "node --experimental-strip-types ./scripts/api-contract.ts",
37
+ "api:update": "npm run build && node --experimental-strip-types ./scripts/api-contract.ts --update",
38
+ "ci": "npm run test:coverage && npm run typecheck:smoke && npm run api:check && npm run docs:check && npm run release:check && npm run test:consumer",
39
+ "docs:check": "node --experimental-strip-types ./scripts/documentation-contract.ts",
40
+ "prepack": "npm run build",
41
+ "prepublishOnly": "node --experimental-strip-types ./scripts/refuse-publish.ts",
42
+ "release:audit": "node --experimental-strip-types ./scripts/release-check.ts audit",
43
+ "release:bundle": "node --experimental-strip-types ./scripts/release-bundle.ts",
44
+ "release:check": "npm run build && node --experimental-strip-types ./scripts/release-check.ts all",
45
+ "release:licenses": "node --experimental-strip-types ./scripts/release-check.ts licenses",
46
+ "release:lock": "node --experimental-strip-types ./scripts/release-check.ts lock",
47
+ "release:pack": "node --experimental-strip-types ./scripts/release-check.ts pack",
48
+ "release:publish-block": "node --experimental-strip-types ./scripts/release-check.ts publish",
49
+ "release:sbom": "node --experimental-strip-types ./scripts/release-check.ts sbom",
50
+ "test:consumer": "node --experimental-strip-types ./scripts/core-package-consumer-smoke.ts",
51
+ "test:coverage": "npm run build && npm run test:runtime-import && vitest run --config vitest.config.ts --coverage && node --experimental-strip-types ./test/assertNoZeroCoverage.ts",
52
+ "test:runtime-import": "node -e \"const p=require('./package.json'); Promise.all(Object.keys(p.exports).map((s)=>import(s==='.'?p.name:p.name+'/'+s.slice(2)))).then((m)=>console.log('core runtime import ok', m.length))\"",
53
+ "test:smoke": "npm run build && npm run test:runtime-import && vitest run --config vitest.config.ts",
54
+ "typecheck:smoke": "tsc -p tsconfig.smoke.json --noEmit"
55
+ },
56
+ "dependencies": {
57
+ "zod": "^4.4.3"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^24.13.3",
61
+ "@vitest/coverage-v8": "^4.1.11",
62
+ "typescript": "^6.0.3",
63
+ "vite": "^8.1.5",
64
+ "vitest": "^4.1.11"
65
+ },
66
+ "exports": {
67
+ "./bundle": {
68
+ "core-source": "./src/bundle/contracts.ts",
69
+ "types": "./dist/bundle/contracts.d.ts",
70
+ "module": "./dist/bundle/contracts.js",
71
+ "node": "./dist/bundle/contracts.js",
72
+ "development": "./dist/bundle/contracts.js",
73
+ "production": "./dist/bundle/contracts.js",
74
+ "import": "./dist/bundle/contracts.js",
75
+ "default": "./dist/bundle/contracts.js"
76
+ },
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
+ "./company-identity": {
88
+ "core-source": "./src/company-identity/index.ts",
89
+ "types": "./dist/company-identity/index.d.ts",
90
+ "module": "./dist/company-identity/index.js",
91
+ "node": "./dist/company-identity/index.js",
92
+ "development": "./dist/company-identity/index.js",
93
+ "production": "./dist/company-identity/index.js",
94
+ "import": "./dist/company-identity/index.js",
95
+ "default": "./dist/company-identity/index.js"
96
+ },
97
+ "./fulfillment": {
98
+ "core-source": "./src/fulfillment/index.ts",
99
+ "types": "./dist/fulfillment/index.d.ts",
100
+ "module": "./dist/fulfillment/index.js",
101
+ "node": "./dist/fulfillment/index.js",
102
+ "development": "./dist/fulfillment/index.js",
103
+ "production": "./dist/fulfillment/index.js",
104
+ "import": "./dist/fulfillment/index.js",
105
+ "default": "./dist/fulfillment/index.js"
106
+ },
107
+ "./inventory": {
108
+ "core-source": "./src/inventory/index.ts",
109
+ "types": "./dist/inventory/index.d.ts",
110
+ "module": "./dist/inventory/index.js",
111
+ "node": "./dist/inventory/index.js",
112
+ "development": "./dist/inventory/index.js",
113
+ "production": "./dist/inventory/index.js",
114
+ "import": "./dist/inventory/index.js",
115
+ "default": "./dist/inventory/index.js"
116
+ },
117
+ "./shipping": {
118
+ "core-source": "./src/shipping/index.ts",
119
+ "types": "./dist/shipping/index.d.ts",
120
+ "module": "./dist/shipping/index.js",
121
+ "node": "./dist/shipping/index.js",
122
+ "development": "./dist/shipping/index.js",
123
+ "production": "./dist/shipping/index.js",
124
+ "import": "./dist/shipping/index.js",
125
+ "default": "./dist/shipping/index.js"
126
+ },
127
+ "./catalog": {
128
+ "core-source": "./src/catalog/index.ts",
129
+ "types": "./dist/catalog/index.d.ts",
130
+ "module": "./dist/catalog/index.js",
131
+ "node": "./dist/catalog/index.js",
132
+ "development": "./dist/catalog/index.js",
133
+ "production": "./dist/catalog/index.js",
134
+ "import": "./dist/catalog/index.js",
135
+ "default": "./dist/catalog/index.js"
136
+ },
137
+ "./pricing": {
138
+ "core-source": "./src/pricing/index.ts",
139
+ "types": "./dist/pricing/index.d.ts",
140
+ "module": "./dist/pricing/index.js",
141
+ "node": "./dist/pricing/index.js",
142
+ "development": "./dist/pricing/index.js",
143
+ "production": "./dist/pricing/index.js",
144
+ "import": "./dist/pricing/index.js",
145
+ "default": "./dist/pricing/index.js"
146
+ },
147
+ "./promo": {
148
+ "core-source": "./src/promo/index.ts",
149
+ "types": "./dist/promo/index.d.ts",
150
+ "module": "./dist/promo/index.js",
151
+ "node": "./dist/promo/index.js",
152
+ "development": "./dist/promo/index.js",
153
+ "production": "./dist/promo/index.js",
154
+ "import": "./dist/promo/index.js",
155
+ "default": "./dist/promo/index.js"
156
+ },
157
+ "./risk": {
158
+ "core-source": "./src/risk/index.ts",
159
+ "types": "./dist/risk/index.d.ts",
160
+ "module": "./dist/risk/index.js",
161
+ "node": "./dist/risk/index.js",
162
+ "development": "./dist/risk/index.js",
163
+ "production": "./dist/risk/index.js",
164
+ "import": "./dist/risk/index.js",
165
+ "default": "./dist/risk/index.js"
166
+ },
167
+ "./marketing/research": {
168
+ "core-source": "./src/marketing/research/index.ts",
169
+ "types": "./dist/marketing/research/index.d.ts",
170
+ "module": "./dist/marketing/research/index.js",
171
+ "node": "./dist/marketing/research/index.js",
172
+ "development": "./dist/marketing/research/index.js",
173
+ "production": "./dist/marketing/research/index.js",
174
+ "import": "./dist/marketing/research/index.js",
175
+ "default": "./dist/marketing/research/index.js"
176
+ },
177
+ "./partners": {
178
+ "core-source": "./src/partners/index.ts",
179
+ "types": "./dist/partners/index.d.ts",
180
+ "module": "./dist/partners/index.js",
181
+ "node": "./dist/partners/index.js",
182
+ "development": "./dist/partners/index.js",
183
+ "production": "./dist/partners/index.js",
184
+ "import": "./dist/partners/index.js",
185
+ "default": "./dist/partners/index.js"
186
+ },
187
+ "./payment": {
188
+ "core-source": "./src/payment/index.ts",
189
+ "types": "./dist/payment/index.d.ts",
190
+ "module": "./dist/payment/index.js",
191
+ "node": "./dist/payment/index.js",
192
+ "development": "./dist/payment/index.js",
193
+ "production": "./dist/payment/index.js",
194
+ "import": "./dist/payment/index.js",
195
+ "default": "./dist/payment/index.js"
196
+ },
197
+ "./platform-runtime": {
198
+ "core-source": "./src/platform-runtime/index.ts",
199
+ "types": "./dist/platform-runtime/index.d.ts",
200
+ "module": "./dist/platform-runtime/index.js",
201
+ "node": "./dist/platform-runtime/index.js",
202
+ "development": "./dist/platform-runtime/index.js",
203
+ "production": "./dist/platform-runtime/index.js",
204
+ "import": "./dist/platform-runtime/index.js",
205
+ "default": "./dist/platform-runtime/index.js"
206
+ },
207
+ "./subscription": {
208
+ "core-source": "./src/subscription/index.ts",
209
+ "types": "./dist/subscription/index.d.ts",
210
+ "module": "./dist/subscription/index.js",
211
+ "node": "./dist/subscription/index.js",
212
+ "development": "./dist/subscription/index.js",
213
+ "production": "./dist/subscription/index.js",
214
+ "import": "./dist/subscription/index.js",
215
+ "default": "./dist/subscription/index.js"
216
+ },
217
+ "./testing": {
218
+ "core-source": "./src/testing.ts",
219
+ "types": "./dist/testing.d.ts",
220
+ "module": "./dist/testing.js",
221
+ "node": "./dist/testing.js",
222
+ "development": "./dist/testing.js",
223
+ "production": "./dist/testing.js",
224
+ "import": "./dist/testing.js",
225
+ "default": "./dist/testing.js"
226
+ }
16
227
  }
17
228
  }