@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.
- package/CHANGELOG.md +184 -0
- package/CODE_OF_CONDUCT.md +39 -0
- package/CONTRIBUTING.md +34 -0
- package/LICENSE +204 -0
- package/MAINTAINERS.md +33 -0
- package/README.md +64 -3
- package/SECURITY.md +29 -0
- package/dist/bundle/contracts.d.ts +9 -0
- package/dist/bundle/contracts.js +1 -0
- package/dist/bundle/ports.d.ts +41 -0
- package/dist/bundle/ports.js +1 -0
- package/dist/catalog/identifiers.d.ts +31 -0
- package/dist/catalog/identifiers.js +46 -0
- package/dist/catalog/index.d.ts +2 -0
- package/dist/catalog/index.js +2 -0
- package/dist/checkout/idempotency.d.ts +29 -0
- package/dist/checkout/idempotency.js +37 -0
- package/dist/checkout/index.d.ts +2 -0
- package/dist/checkout/index.js +2 -0
- package/dist/company-identity/contracts.d.ts +220 -0
- package/dist/company-identity/contracts.js +168 -0
- package/dist/company-identity/index.d.ts +6 -0
- package/dist/company-identity/index.js +2 -0
- package/dist/company-identity/ports.d.ts +13 -0
- package/dist/company-identity/ports.js +1 -0
- package/dist/fulfillment/facts.d.ts +103 -0
- package/dist/fulfillment/facts.js +195 -0
- package/dist/fulfillment/index.d.ts +4 -0
- package/dist/fulfillment/index.js +2 -0
- package/dist/inventory/atp.d.ts +172 -0
- package/dist/inventory/atp.js +211 -0
- package/dist/inventory/index.d.ts +2 -0
- package/dist/inventory/index.js +2 -0
- package/dist/marketing/research/contracts.d.ts +75 -0
- package/dist/marketing/research/contracts.js +60 -0
- package/dist/marketing/research/index.d.ts +4 -0
- package/dist/marketing/research/index.js +4 -0
- package/dist/marketing/research/ports.d.ts +15 -0
- package/dist/marketing/research/ports.js +1 -0
- package/dist/partners/index.d.ts +4 -0
- package/dist/partners/index.js +4 -0
- package/dist/partners/ports.d.ts +12 -0
- package/dist/partners/ports.js +1 -0
- package/dist/partners/status.d.ts +48 -0
- package/dist/partners/status.js +35 -0
- package/dist/payment/executionBaseContracts.d.ts +81 -0
- package/dist/payment/executionBaseContracts.js +8 -0
- package/dist/payment/index.d.ts +36 -0
- package/dist/payment/index.js +18 -0
- package/dist/payment/methodLifecycle.d.ts +91 -0
- package/dist/payment/methodLifecycle.js +76 -0
- package/dist/payment/paymentControlTypes.d.ts +135 -0
- package/dist/payment/paymentControlTypes.js +110 -0
- package/dist/payment/paymentFailureTaxonomy.d.ts +80 -0
- package/dist/payment/paymentFailureTaxonomy.js +155 -0
- package/dist/payment/paymentFailureTaxonomyContracts.d.ts +106 -0
- package/dist/payment/paymentFailureTaxonomyContracts.js +177 -0
- package/dist/payment/paymentRecovery.d.ts +26 -0
- package/dist/payment/paymentRecovery.js +73 -0
- package/dist/payment/paymentRecoveryContracts.d.ts +53 -0
- package/dist/payment/paymentRecoveryContracts.js +10 -0
- package/dist/payment/paymentStateMachine.d.ts +28 -0
- package/dist/payment/paymentStateMachine.js +184 -0
- package/dist/payment/providerAttemptIdempotency.d.ts +23 -0
- package/dist/payment/providerAttemptIdempotency.js +31 -0
- package/dist/payment/providerCapability.d.ts +148 -0
- package/dist/payment/providerCapability.js +13 -0
- package/dist/platform-runtime/contracts.d.ts +9 -0
- package/dist/platform-runtime/contracts.js +13 -0
- package/dist/platform-runtime/index.d.ts +10 -0
- package/dist/platform-runtime/index.js +4 -0
- package/dist/platform-runtime/platformKernel.d.ts +44 -0
- package/dist/platform-runtime/platformKernel.js +39 -0
- package/dist/platform-runtime/ports.d.ts +100 -0
- package/dist/platform-runtime/ports.js +1 -0
- package/dist/pricing/index.d.ts +8 -0
- package/dist/pricing/index.js +8 -0
- package/dist/pricing/ports.d.ts +22 -0
- package/dist/pricing/ports.js +7 -0
- package/dist/pricing/pricingBreakdown.d.ts +86 -0
- package/dist/pricing/pricingBreakdown.js +155 -0
- package/dist/pricing/targetPriceAllocation.d.ts +97 -0
- package/dist/pricing/targetPriceAllocation.js +202 -0
- package/dist/pricing/types.d.ts +32 -0
- package/dist/pricing/types.js +4 -0
- package/dist/promo/adjustmentEngine.d.ts +4 -0
- package/dist/promo/adjustmentEngine.js +194 -0
- package/dist/promo/adjustmentTypes.d.ts +103 -0
- package/dist/promo/adjustmentTypes.js +13 -0
- package/dist/promo/index.d.ts +8 -0
- package/dist/promo/index.js +8 -0
- package/dist/promo/promoEvaluator.d.ts +25 -0
- package/dist/promo/promoEvaluator.js +135 -0
- package/dist/promo/types.d.ts +86 -0
- package/dist/promo/types.js +12 -0
- package/dist/risk/core/evaluator.d.ts +3 -0
- package/dist/risk/core/evaluator.js +32 -0
- package/dist/risk/core/rules.d.ts +3 -0
- package/dist/risk/core/rules.js +118 -0
- package/dist/risk/core/types.d.ts +65 -0
- package/dist/risk/core/types.js +1 -0
- package/dist/risk/index.d.ts +8 -0
- package/dist/risk/index.js +8 -0
- package/dist/risk/types.d.ts +32 -0
- package/dist/risk/types.js +43 -0
- package/dist/shipping/index.d.ts +4 -0
- package/dist/shipping/index.js +4 -0
- package/dist/shipping/shippingResolver.d.ts +26 -0
- package/dist/shipping/shippingResolver.js +88 -0
- package/dist/shipping/types.d.ts +45 -0
- package/dist/shipping/types.js +4 -0
- package/dist/subscription/cycleHardening.d.ts +72 -0
- package/dist/subscription/cycleHardening.js +95 -0
- package/dist/subscription/index.d.ts +14 -0
- package/dist/subscription/index.js +14 -0
- package/dist/subscription/subscriptionEngineCore.d.ts +13 -0
- package/dist/subscription/subscriptionEngineCore.js +145 -0
- package/dist/subscription/subscriptionEngineLifecycle.d.ts +52 -0
- package/dist/subscription/subscriptionEngineLifecycle.js +196 -0
- package/dist/subscription/subscriptionEnginePayment.d.ts +19 -0
- package/dist/subscription/subscriptionEnginePayment.js +127 -0
- package/dist/subscription/subscriptionEngineTypes.d.ts +94 -0
- package/dist/subscription/subscriptionEngineTypes.js +4 -0
- package/dist/subscription/subscriptionEngineUtils.d.ts +32 -0
- package/dist/subscription/subscriptionEngineUtils.js +74 -0
- package/dist/subscription/types.d.ts +58 -0
- package/dist/subscription/types.js +47 -0
- package/dist/testing.d.ts +48 -0
- package/dist/testing.js +105 -0
- package/package.json +215 -4
- package/release-gates.json +232 -0
- package/src/bundle/contracts.ts +15 -0
- package/src/bundle/ports.ts +32 -0
- package/src/catalog/identifiers.ts +50 -0
- package/src/catalog/index.ts +2 -0
- package/src/checkout/idempotency.ts +49 -0
- package/src/checkout/index.ts +2 -0
- package/src/company-identity/contracts.ts +209 -0
- package/src/company-identity/index.ts +27 -0
- package/src/company-identity/ports.ts +19 -0
- package/src/fulfillment/facts.ts +300 -0
- package/src/fulfillment/index.ts +34 -0
- package/src/inventory/atp.ts +249 -0
- package/src/inventory/index.ts +2 -0
- package/src/marketing/research/contracts.ts +91 -0
- package/src/marketing/research/index.ts +4 -0
- package/src/marketing/research/ports.ts +26 -0
- package/src/partners/index.ts +4 -0
- package/src/partners/ports.ts +18 -0
- package/src/partners/status.ts +49 -0
- package/src/payment/executionBaseContracts.ts +93 -0
- package/src/payment/index.ts +128 -0
- package/src/payment/methodLifecycle.ts +130 -0
- package/src/payment/paymentControlTypes.ts +197 -0
- package/src/payment/paymentFailureTaxonomy.ts +201 -0
- package/src/payment/paymentFailureTaxonomyContracts.ts +228 -0
- package/src/payment/paymentRecovery.ts +95 -0
- package/src/payment/paymentRecoveryContracts.ts +61 -0
- package/src/payment/paymentStateMachine.ts +263 -0
- package/src/payment/providerAttemptIdempotency.ts +57 -0
- package/src/payment/providerCapability.ts +172 -0
- package/src/platform-runtime/contracts.ts +33 -0
- package/src/platform-runtime/index.ts +36 -0
- package/src/platform-runtime/platformKernel.ts +86 -0
- package/src/platform-runtime/ports.ts +88 -0
- package/src/pricing/index.ts +8 -0
- package/src/pricing/ports.ts +27 -0
- package/src/pricing/pricingBreakdown.ts +243 -0
- package/src/pricing/targetPriceAllocation.ts +293 -0
- package/src/pricing/types.ts +36 -0
- package/src/promo/adjustmentEngine.ts +253 -0
- package/src/promo/adjustmentTypes.ts +94 -0
- package/src/promo/index.ts +8 -0
- package/src/promo/promoEvaluator.ts +137 -0
- package/src/promo/types.ts +97 -0
- package/src/risk/core/evaluator.ts +41 -0
- package/src/risk/core/rules.ts +128 -0
- package/src/risk/core/types.ts +73 -0
- package/src/risk/index.ts +8 -0
- package/src/risk/types.ts +60 -0
- package/src/shipping/index.ts +4 -0
- package/src/shipping/shippingResolver.ts +101 -0
- package/src/shipping/types.ts +49 -0
- package/src/subscription/cycleHardening.ts +141 -0
- package/src/subscription/index.ts +14 -0
- package/src/subscription/subscriptionEngineCore.ts +181 -0
- package/src/subscription/subscriptionEngineLifecycle.ts +282 -0
- package/src/subscription/subscriptionEnginePayment.ts +174 -0
- package/src/subscription/subscriptionEngineTypes.ts +112 -0
- package/src/subscription/subscriptionEngineUtils.ts +110 -0
- package/src/subscription/types.ts +104 -0
- package/src/testing.ts +169 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** @beta */ export declare const NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION: 1;
|
|
2
|
+
/** @beta */ export declare const NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION: 0;
|
|
3
|
+
/** @beta */ export declare const PROVIDER_FULFILLMENT_FACT_SOURCES: readonly ["provider_webhook", "provider_reconciliation", "provider_response"];
|
|
4
|
+
/** @beta */ export declare const FULFILLMENT_FACT_SOURCES: readonly ["provider_webhook", "provider_reconciliation", "provider_response", "operator_repair"];
|
|
5
|
+
/** @beta */ export declare const FULFILLMENT_FACT_SOURCE_AUTHORITIES: readonly ["provider_asserted", "operator_asserted"];
|
|
6
|
+
/** @beta */ export type ProviderFulfillmentFactSource = (typeof PROVIDER_FULFILLMENT_FACT_SOURCES)[number];
|
|
7
|
+
/** @beta */ export type FulfillmentFactSource = (typeof FULFILLMENT_FACT_SOURCES)[number];
|
|
8
|
+
/** @beta */ export type FulfillmentFactSourceAuthority = (typeof FULFILLMENT_FACT_SOURCE_AUTHORITIES)[number];
|
|
9
|
+
/** @beta */ export type FulfillmentFactProvenance = {
|
|
10
|
+
readonly source: ProviderFulfillmentFactSource;
|
|
11
|
+
readonly authority: "provider_asserted";
|
|
12
|
+
} | {
|
|
13
|
+
readonly source: "operator_repair";
|
|
14
|
+
readonly authority: "operator_asserted";
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Pointer to separately governed raw evidence. The reference is minted by the
|
|
18
|
+
* adopting application (`evidence:<uuid>`), never copied from provider,
|
|
19
|
+
* tracking, customer, or order data. PII safety still belongs to that minting
|
|
20
|
+
* boundary and the referenced evidence store; this contract only enforces the
|
|
21
|
+
* namespace and identifier shape.
|
|
22
|
+
*/
|
|
23
|
+
/** @beta */ export interface FulfillmentRawEventReference {
|
|
24
|
+
readonly reference: `evidence:${string}`;
|
|
25
|
+
readonly digestAlgorithm: "sha256";
|
|
26
|
+
readonly digest: string;
|
|
27
|
+
}
|
|
28
|
+
/** Comparable only within one provider + fulfillment reference sequence. */
|
|
29
|
+
/** @beta */ export interface FulfillmentProviderSequence {
|
|
30
|
+
readonly kind: "monotonic_ordinal";
|
|
31
|
+
readonly ordinal: number;
|
|
32
|
+
}
|
|
33
|
+
interface NormalizedFulfillmentFactFields<TCanonicalStatus extends string> {
|
|
34
|
+
readonly mapperVersion: string;
|
|
35
|
+
readonly factId: string;
|
|
36
|
+
readonly providerKind: string;
|
|
37
|
+
readonly fulfillmentReference: string;
|
|
38
|
+
readonly canonicalStatus: TCanonicalStatus;
|
|
39
|
+
readonly provenance: FulfillmentFactProvenance;
|
|
40
|
+
readonly occurredAt: string | null;
|
|
41
|
+
readonly receivedAt: string;
|
|
42
|
+
readonly providerSequence?: FulfillmentProviderSequence;
|
|
43
|
+
}
|
|
44
|
+
/** Provider-neutral evidence; it authorizes no transition or external effect. */
|
|
45
|
+
/** @beta */ export interface NormalizedFulfillmentFactV1<TCanonicalStatus extends string = string> extends NormalizedFulfillmentFactFields<TCanonicalStatus> {
|
|
46
|
+
readonly schemaVersion: typeof NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION;
|
|
47
|
+
readonly rawEvent: FulfillmentRawEventReference;
|
|
48
|
+
}
|
|
49
|
+
/** @beta */ export type NormalizedFulfillmentFact<TCanonicalStatus extends string = string> = NormalizedFulfillmentFactV1<TCanonicalStatus>;
|
|
50
|
+
/** Narrow legacy wire shape supported only for deterministic N-1 ingestion. */
|
|
51
|
+
/** @beta */ export interface NormalizedFulfillmentFactV0<TCanonicalStatus extends string = string> extends NormalizedFulfillmentFactFields<TCanonicalStatus> {
|
|
52
|
+
readonly schemaVersion: typeof NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION;
|
|
53
|
+
readonly rawEventReference: `evidence:${string}`;
|
|
54
|
+
readonly rawEventSha256: string;
|
|
55
|
+
}
|
|
56
|
+
/** @beta */ export type CompatibleNormalizedFulfillmentFact<TCanonicalStatus extends string = string> = NormalizedFulfillmentFactV0<TCanonicalStatus> | NormalizedFulfillmentFactV1<TCanonicalStatus>;
|
|
57
|
+
/** @beta */ export interface FulfillmentFactParserOptions<TCanonicalStatus extends string> {
|
|
58
|
+
isCanonicalStatus(value: string): value is TCanonicalStatus;
|
|
59
|
+
}
|
|
60
|
+
/** @beta */ export declare class InvalidFulfillmentFactError extends Error {
|
|
61
|
+
readonly reason: string;
|
|
62
|
+
constructor(reason: string);
|
|
63
|
+
}
|
|
64
|
+
/** @beta */ export declare class UnsupportedFulfillmentFactSchemaError extends Error {
|
|
65
|
+
readonly schemaVersion: unknown;
|
|
66
|
+
constructor(schemaVersion: unknown);
|
|
67
|
+
}
|
|
68
|
+
/** Strict public unknown-input boundary for current N and supported N-1. */
|
|
69
|
+
/** @beta */ export declare function parseNormalizedFulfillmentFact<TCanonicalStatus extends string>(value: unknown, options: FulfillmentFactParserOptions<TCanonicalStatus>): NormalizedFulfillmentFact<TCanonicalStatus>;
|
|
70
|
+
/** Stable property-order-independent identity of one already parsed fact. */
|
|
71
|
+
/** @beta */ export declare function canonicalFulfillmentFactFingerprint(fact: NormalizedFulfillmentFact): string;
|
|
72
|
+
/** @beta */ export declare class FulfillmentFactCollisionError extends Error {
|
|
73
|
+
readonly factId: string;
|
|
74
|
+
constructor(factId: string);
|
|
75
|
+
}
|
|
76
|
+
/** Deterministic exact-duplicate collapse; conflicting reuse fails closed. */
|
|
77
|
+
/** @beta */ export declare function canonicalizeFulfillmentFacts<TCanonicalStatus extends string>(facts: readonly NormalizedFulfillmentFact<TCanonicalStatus>[]): NormalizedFulfillmentFact<TCanonicalStatus>[];
|
|
78
|
+
/** @beta */ export declare const TRANSITION_CONFLICT_REASONS: readonly ["status_regression", "terminal_status_disagreement", "provider_sequence_regression", "source_authority_disagreement"];
|
|
79
|
+
/** @beta */ export type TransitionConflictReason = (typeof TRANSITION_CONFLICT_REASONS)[number];
|
|
80
|
+
/** @beta */ export interface FulfillmentTransitionSnapshot<TCanonicalStatus extends string = string> {
|
|
81
|
+
readonly fact: NormalizedFulfillmentFact<TCanonicalStatus>;
|
|
82
|
+
}
|
|
83
|
+
/** @beta */ export type TransitionConflictDecision = {
|
|
84
|
+
readonly kind: "selected";
|
|
85
|
+
readonly selected: "current" | "incoming";
|
|
86
|
+
readonly reasonCode: string;
|
|
87
|
+
} | {
|
|
88
|
+
readonly kind: "hold";
|
|
89
|
+
readonly reasonCode: string;
|
|
90
|
+
};
|
|
91
|
+
/** Self-contained immutable diagnostic emitted by a future consumer reducer. */
|
|
92
|
+
/** @beta */ export interface TransitionConflict<TCanonicalStatus extends string = string> {
|
|
93
|
+
readonly conflictId: `conflict:${string}`;
|
|
94
|
+
readonly policyVersion: string;
|
|
95
|
+
readonly reason: TransitionConflictReason;
|
|
96
|
+
readonly fulfillmentReference: string;
|
|
97
|
+
readonly current: FulfillmentTransitionSnapshot<TCanonicalStatus>;
|
|
98
|
+
readonly incoming: FulfillmentTransitionSnapshot<TCanonicalStatus>;
|
|
99
|
+
readonly decision: TransitionConflictDecision;
|
|
100
|
+
readonly detectedAt: string;
|
|
101
|
+
}
|
|
102
|
+
/** @beta */ export declare function parseTransitionConflict<TCanonicalStatus extends string>(value: unknown, options: FulfillmentFactParserOptions<TCanonicalStatus>): TransitionConflict<TCanonicalStatus>;
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** @beta */ export const NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION = 1;
|
|
3
|
+
/** @beta */ export const NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION = 0;
|
|
4
|
+
/** @beta */ export const PROVIDER_FULFILLMENT_FACT_SOURCES = [
|
|
5
|
+
"provider_webhook",
|
|
6
|
+
"provider_reconciliation",
|
|
7
|
+
"provider_response",
|
|
8
|
+
];
|
|
9
|
+
/** @beta */ export const FULFILLMENT_FACT_SOURCES = [
|
|
10
|
+
...PROVIDER_FULFILLMENT_FACT_SOURCES,
|
|
11
|
+
"operator_repair",
|
|
12
|
+
];
|
|
13
|
+
/** @beta */ export const FULFILLMENT_FACT_SOURCE_AUTHORITIES = [
|
|
14
|
+
"provider_asserted",
|
|
15
|
+
"operator_asserted",
|
|
16
|
+
];
|
|
17
|
+
const nonEmpty = z.string().trim().min(1);
|
|
18
|
+
const opaqueId = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u);
|
|
19
|
+
const evidenceReference = z.string().regex(/^evidence:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u, "must be an application-minted evidence:<uuid> reference");
|
|
20
|
+
const sha256 = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
21
|
+
const rfc3339 = z.iso.datetime({ offset: true });
|
|
22
|
+
const providerProvenance = z.object({
|
|
23
|
+
source: z.enum(PROVIDER_FULFILLMENT_FACT_SOURCES),
|
|
24
|
+
authority: z.literal("provider_asserted"),
|
|
25
|
+
}).strict();
|
|
26
|
+
const operatorProvenance = z.object({
|
|
27
|
+
source: z.literal("operator_repair"),
|
|
28
|
+
authority: z.literal("operator_asserted"),
|
|
29
|
+
}).strict();
|
|
30
|
+
const provenance = z.union([providerProvenance, operatorProvenance]);
|
|
31
|
+
const providerSequence = z.object({
|
|
32
|
+
kind: z.literal("monotonic_ordinal"),
|
|
33
|
+
ordinal: z.number().int().nonnegative().safe(),
|
|
34
|
+
}).strict();
|
|
35
|
+
const commonFields = {
|
|
36
|
+
mapperVersion: nonEmpty,
|
|
37
|
+
factId: opaqueId,
|
|
38
|
+
providerKind: opaqueId,
|
|
39
|
+
fulfillmentReference: opaqueId,
|
|
40
|
+
canonicalStatus: nonEmpty,
|
|
41
|
+
provenance,
|
|
42
|
+
occurredAt: rfc3339.nullable(),
|
|
43
|
+
receivedAt: rfc3339,
|
|
44
|
+
providerSequence: providerSequence.optional(),
|
|
45
|
+
};
|
|
46
|
+
const v1Schema = z.object({
|
|
47
|
+
schemaVersion: z.literal(NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION),
|
|
48
|
+
...commonFields,
|
|
49
|
+
rawEvent: z.object({
|
|
50
|
+
reference: evidenceReference,
|
|
51
|
+
digestAlgorithm: z.literal("sha256"),
|
|
52
|
+
digest: sha256,
|
|
53
|
+
}).strict(),
|
|
54
|
+
}).strict();
|
|
55
|
+
const v0Schema = z.object({
|
|
56
|
+
schemaVersion: z.literal(NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION),
|
|
57
|
+
...commonFields,
|
|
58
|
+
rawEventReference: evidenceReference,
|
|
59
|
+
rawEventSha256: sha256,
|
|
60
|
+
}).strict();
|
|
61
|
+
/** @beta */ export class InvalidFulfillmentFactError extends Error {
|
|
62
|
+
reason;
|
|
63
|
+
constructor(reason) {
|
|
64
|
+
super(`invalid_fulfillment_fact:${reason}`);
|
|
65
|
+
this.reason = reason;
|
|
66
|
+
this.name = "InvalidFulfillmentFactError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** @beta */ export class UnsupportedFulfillmentFactSchemaError extends Error {
|
|
70
|
+
schemaVersion;
|
|
71
|
+
constructor(schemaVersion) {
|
|
72
|
+
super(`unsupported_fulfillment_fact_schema:${String(schemaVersion)}`);
|
|
73
|
+
this.schemaVersion = schemaVersion;
|
|
74
|
+
this.name = "UnsupportedFulfillmentFactSchemaError";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Strict public unknown-input boundary for current N and supported N-1. */
|
|
78
|
+
/** @beta */ export function parseNormalizedFulfillmentFact(value, options) {
|
|
79
|
+
const version = value && typeof value === "object"
|
|
80
|
+
? value.schemaVersion
|
|
81
|
+
: undefined;
|
|
82
|
+
const schema = version === NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION
|
|
83
|
+
? v1Schema
|
|
84
|
+
: version === NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION
|
|
85
|
+
? v0Schema
|
|
86
|
+
: null;
|
|
87
|
+
if (!schema)
|
|
88
|
+
throw new UnsupportedFulfillmentFactSchemaError(version);
|
|
89
|
+
const parsed = schema.safeParse(value);
|
|
90
|
+
if (!parsed.success)
|
|
91
|
+
throw new InvalidFulfillmentFactError(parsed.error.issues[0]?.message ?? "schema");
|
|
92
|
+
if (!options.isCanonicalStatus(parsed.data.canonicalStatus)) {
|
|
93
|
+
throw new InvalidFulfillmentFactError("canonical_status_unknown");
|
|
94
|
+
}
|
|
95
|
+
if (parsed.data.schemaVersion === NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION) {
|
|
96
|
+
return parsed.data;
|
|
97
|
+
}
|
|
98
|
+
const { rawEventReference, rawEventSha256, ...shared } = parsed.data;
|
|
99
|
+
return {
|
|
100
|
+
...shared,
|
|
101
|
+
schemaVersion: NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION,
|
|
102
|
+
canonicalStatus: parsed.data.canonicalStatus,
|
|
103
|
+
rawEvent: {
|
|
104
|
+
reference: rawEventReference,
|
|
105
|
+
digestAlgorithm: "sha256",
|
|
106
|
+
digest: rawEventSha256,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Stable property-order-independent identity of one already parsed fact. */
|
|
111
|
+
/** @beta */ export function canonicalFulfillmentFactFingerprint(fact) {
|
|
112
|
+
return JSON.stringify([
|
|
113
|
+
fact.schemaVersion,
|
|
114
|
+
fact.mapperVersion,
|
|
115
|
+
fact.factId,
|
|
116
|
+
fact.providerKind,
|
|
117
|
+
fact.fulfillmentReference,
|
|
118
|
+
fact.canonicalStatus,
|
|
119
|
+
fact.provenance.source,
|
|
120
|
+
fact.provenance.authority,
|
|
121
|
+
fact.occurredAt,
|
|
122
|
+
fact.receivedAt,
|
|
123
|
+
fact.providerSequence?.kind ?? null,
|
|
124
|
+
fact.providerSequence?.ordinal ?? null,
|
|
125
|
+
fact.rawEvent.reference,
|
|
126
|
+
fact.rawEvent.digestAlgorithm,
|
|
127
|
+
fact.rawEvent.digest,
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
/** @beta */ export class FulfillmentFactCollisionError extends Error {
|
|
131
|
+
factId;
|
|
132
|
+
constructor(factId) {
|
|
133
|
+
super(`fulfillment_fact_id_collision:${factId}`);
|
|
134
|
+
this.factId = factId;
|
|
135
|
+
this.name = "FulfillmentFactCollisionError";
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Deterministic exact-duplicate collapse; conflicting reuse fails closed. */
|
|
139
|
+
/** @beta */ export function canonicalizeFulfillmentFacts(facts) {
|
|
140
|
+
const byId = new Map();
|
|
141
|
+
for (const fact of facts) {
|
|
142
|
+
const fingerprint = canonicalFulfillmentFactFingerprint(fact);
|
|
143
|
+
const existing = byId.get(fact.factId);
|
|
144
|
+
if (existing && existing.fingerprint !== fingerprint)
|
|
145
|
+
throw new FulfillmentFactCollisionError(fact.factId);
|
|
146
|
+
byId.set(fact.factId, { fact, fingerprint });
|
|
147
|
+
}
|
|
148
|
+
return [...byId.values()]
|
|
149
|
+
.sort((left, right) => left.fact.factId.localeCompare(right.fact.factId))
|
|
150
|
+
.map(({ fact }) => fact);
|
|
151
|
+
}
|
|
152
|
+
/** @beta */ export const TRANSITION_CONFLICT_REASONS = [
|
|
153
|
+
"status_regression",
|
|
154
|
+
"terminal_status_disagreement",
|
|
155
|
+
"provider_sequence_regression",
|
|
156
|
+
"source_authority_disagreement",
|
|
157
|
+
];
|
|
158
|
+
const conflictReference = z.string().regex(/^conflict:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u, "must be an application-minted conflict:<uuid> reference");
|
|
159
|
+
const transitionConflictEnvelope = z.object({
|
|
160
|
+
conflictId: conflictReference,
|
|
161
|
+
policyVersion: nonEmpty,
|
|
162
|
+
reason: z.enum(TRANSITION_CONFLICT_REASONS),
|
|
163
|
+
fulfillmentReference: opaqueId,
|
|
164
|
+
current: z.object({ fact: z.unknown() }).strict(),
|
|
165
|
+
incoming: z.object({ fact: z.unknown() }).strict(),
|
|
166
|
+
decision: z.discriminatedUnion("kind", [
|
|
167
|
+
z.object({
|
|
168
|
+
kind: z.literal("selected"),
|
|
169
|
+
selected: z.enum(["current", "incoming"]),
|
|
170
|
+
reasonCode: opaqueId,
|
|
171
|
+
}).strict(),
|
|
172
|
+
z.object({ kind: z.literal("hold"), reasonCode: opaqueId }).strict(),
|
|
173
|
+
]),
|
|
174
|
+
detectedAt: rfc3339,
|
|
175
|
+
}).strict();
|
|
176
|
+
/** @beta */ export function parseTransitionConflict(value, options) {
|
|
177
|
+
const envelope = transitionConflictEnvelope.safeParse(value);
|
|
178
|
+
if (!envelope.success) {
|
|
179
|
+
throw new InvalidFulfillmentFactError(`transition_conflict:${envelope.error.issues[0]?.message ?? "schema"}`);
|
|
180
|
+
}
|
|
181
|
+
const current = parseNormalizedFulfillmentFact(envelope.data.current.fact, options);
|
|
182
|
+
const incoming = parseNormalizedFulfillmentFact(envelope.data.incoming.fact, options);
|
|
183
|
+
if (current.providerKind !== incoming.providerKind
|
|
184
|
+
|| current.fulfillmentReference !== incoming.fulfillmentReference
|
|
185
|
+
|| envelope.data.fulfillmentReference !== current.fulfillmentReference) {
|
|
186
|
+
throw new InvalidFulfillmentFactError("transition_conflict_stream_mismatch");
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
...envelope.data,
|
|
190
|
+
conflictId: envelope.data.conflictId,
|
|
191
|
+
decision: envelope.data.decision,
|
|
192
|
+
current: { fact: current },
|
|
193
|
+
incoming: { fact: incoming },
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** @beta */
|
|
2
|
+
export { FULFILLMENT_FACT_SOURCE_AUTHORITIES, FULFILLMENT_FACT_SOURCES, PROVIDER_FULFILLMENT_FACT_SOURCES, NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION, NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION, TRANSITION_CONFLICT_REASONS, InvalidFulfillmentFactError, FulfillmentFactCollisionError, UnsupportedFulfillmentFactSchemaError, canonicalFulfillmentFactFingerprint, canonicalizeFulfillmentFacts, parseNormalizedFulfillmentFact, parseTransitionConflict, } from "./facts.js";
|
|
3
|
+
/** @beta */
|
|
4
|
+
export type { CompatibleNormalizedFulfillmentFact, FulfillmentFactSourceAuthority, FulfillmentFactSource, FulfillmentFactProvenance, FulfillmentRawEventReference, FulfillmentProviderSequence, FulfillmentFactParserOptions, ProviderFulfillmentFactSource, NormalizedFulfillmentFact, NormalizedFulfillmentFactV0, NormalizedFulfillmentFactV1, TransitionConflict, TransitionConflictDecision, FulfillmentTransitionSnapshot, TransitionConflictReason, } from "./facts.js";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** @beta */
|
|
2
|
+
export { FULFILLMENT_FACT_SOURCE_AUTHORITIES, FULFILLMENT_FACT_SOURCES, PROVIDER_FULFILLMENT_FACT_SOURCES, NORMALIZED_FULFILLMENT_FACT_PREVIOUS_SCHEMA_VERSION, NORMALIZED_FULFILLMENT_FACT_SCHEMA_VERSION, TRANSITION_CONFLICT_REASONS, InvalidFulfillmentFactError, FulfillmentFactCollisionError, UnsupportedFulfillmentFactSchemaError, canonicalFulfillmentFactFingerprint, canonicalizeFulfillmentFacts, parseNormalizedFulfillmentFact, parseTransitionConflict, } from "./facts.js";
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** @beta */
|
|
3
|
+
export declare const INVENTORY_CONTRACT_VERSION = "inventory.v0";
|
|
4
|
+
/** @beta */
|
|
5
|
+
export declare const inventoryLocationKindSchema: z.ZodEnum<{
|
|
6
|
+
internal_warehouse: "internal_warehouse";
|
|
7
|
+
third_party_logistics: "third_party_logistics";
|
|
8
|
+
supplier: "supplier";
|
|
9
|
+
quarantine: "quarantine";
|
|
10
|
+
virtual: "virtual";
|
|
11
|
+
}>;
|
|
12
|
+
/** @beta */
|
|
13
|
+
export declare const inventoryLocationStatusSchema: z.ZodEnum<{
|
|
14
|
+
active: "active";
|
|
15
|
+
inactive: "inactive";
|
|
16
|
+
}>;
|
|
17
|
+
/** @beta */
|
|
18
|
+
export declare const inventoryLotStatusSchema: z.ZodEnum<{
|
|
19
|
+
expired: "expired";
|
|
20
|
+
available: "available";
|
|
21
|
+
quarantined: "quarantined";
|
|
22
|
+
recalled: "recalled";
|
|
23
|
+
}>;
|
|
24
|
+
/** @beta */
|
|
25
|
+
export declare const inventoryAtpStatusSchema: z.ZodEnum<{
|
|
26
|
+
unsupported: "unsupported";
|
|
27
|
+
fulfillable: "fulfillable";
|
|
28
|
+
insufficient: "insufficient";
|
|
29
|
+
review_required: "review_required";
|
|
30
|
+
}>;
|
|
31
|
+
/** @beta */
|
|
32
|
+
export declare const inventoryStockLineSchema: z.ZodObject<{
|
|
33
|
+
skuId: z.ZodGUID;
|
|
34
|
+
sku: z.ZodString;
|
|
35
|
+
locationId: z.ZodGUID;
|
|
36
|
+
locationCode: z.ZodString;
|
|
37
|
+
locationKind: z.ZodEnum<{
|
|
38
|
+
internal_warehouse: "internal_warehouse";
|
|
39
|
+
third_party_logistics: "third_party_logistics";
|
|
40
|
+
supplier: "supplier";
|
|
41
|
+
quarantine: "quarantine";
|
|
42
|
+
virtual: "virtual";
|
|
43
|
+
}>;
|
|
44
|
+
locationStatus: z.ZodEnum<{
|
|
45
|
+
active: "active";
|
|
46
|
+
inactive: "inactive";
|
|
47
|
+
}>;
|
|
48
|
+
fulfillable: z.ZodBoolean;
|
|
49
|
+
lotId: z.ZodNullable<z.ZodGUID>;
|
|
50
|
+
lotCode: z.ZodNullable<z.ZodString>;
|
|
51
|
+
lotStatus: z.ZodNullable<z.ZodEnum<{
|
|
52
|
+
expired: "expired";
|
|
53
|
+
available: "available";
|
|
54
|
+
quarantined: "quarantined";
|
|
55
|
+
recalled: "recalled";
|
|
56
|
+
}>>;
|
|
57
|
+
expiresAt: z.ZodNullable<z.ZodString>;
|
|
58
|
+
onHand: z.ZodNumber;
|
|
59
|
+
reserved: z.ZodNumber;
|
|
60
|
+
unavailable: z.ZodNumber;
|
|
61
|
+
incoming: z.ZodNumber;
|
|
62
|
+
safetyStock: z.ZodNumber;
|
|
63
|
+
}, z.core.$strict>;
|
|
64
|
+
/** @beta */
|
|
65
|
+
export declare const inventoryAtpRequestLineSchema: z.ZodObject<{
|
|
66
|
+
skuId: z.ZodGUID;
|
|
67
|
+
sku: z.ZodString;
|
|
68
|
+
quantity: z.ZodNumber;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
/** @beta */
|
|
71
|
+
export declare const inventoryAtpRequestSchema: z.ZodObject<{
|
|
72
|
+
contractVersion: z.ZodLiteral<"inventory.v0">;
|
|
73
|
+
lines: z.ZodArray<z.ZodObject<{
|
|
74
|
+
skuId: z.ZodGUID;
|
|
75
|
+
sku: z.ZodString;
|
|
76
|
+
quantity: z.ZodNumber;
|
|
77
|
+
}, z.core.$strict>>;
|
|
78
|
+
requestedAt: z.ZodString;
|
|
79
|
+
orderMode: z.ZodEnum<{
|
|
80
|
+
subscription_cycle: "subscription_cycle";
|
|
81
|
+
one_time: "one_time";
|
|
82
|
+
}>;
|
|
83
|
+
region: z.ZodOptional<z.ZodString>;
|
|
84
|
+
noSplitShipment: z.ZodDefault<z.ZodBoolean>;
|
|
85
|
+
minShelfLifeDays: z.ZodDefault<z.ZodNumber>;
|
|
86
|
+
}, z.core.$strict>;
|
|
87
|
+
/** @beta */
|
|
88
|
+
export declare const inventoryAllocationSchema: z.ZodObject<{
|
|
89
|
+
skuId: z.ZodGUID;
|
|
90
|
+
sku: z.ZodString;
|
|
91
|
+
locationId: z.ZodGUID;
|
|
92
|
+
locationCode: z.ZodString;
|
|
93
|
+
lotId: z.ZodNullable<z.ZodGUID>;
|
|
94
|
+
lotCode: z.ZodNullable<z.ZodString>;
|
|
95
|
+
quantity: z.ZodNumber;
|
|
96
|
+
expiresAt: z.ZodNullable<z.ZodString>;
|
|
97
|
+
}, z.core.$strict>;
|
|
98
|
+
/** @beta */
|
|
99
|
+
export declare const inventoryAtpLineResultSchema: z.ZodObject<{
|
|
100
|
+
skuId: z.ZodGUID;
|
|
101
|
+
sku: z.ZodString;
|
|
102
|
+
requestedQuantity: z.ZodNumber;
|
|
103
|
+
availableQuantity: z.ZodNumber;
|
|
104
|
+
missingQuantity: z.ZodNumber;
|
|
105
|
+
status: z.ZodEnum<{
|
|
106
|
+
unsupported: "unsupported";
|
|
107
|
+
fulfillable: "fulfillable";
|
|
108
|
+
insufficient: "insufficient";
|
|
109
|
+
review_required: "review_required";
|
|
110
|
+
}>;
|
|
111
|
+
allocations: z.ZodArray<z.ZodObject<{
|
|
112
|
+
skuId: z.ZodGUID;
|
|
113
|
+
sku: z.ZodString;
|
|
114
|
+
locationId: z.ZodGUID;
|
|
115
|
+
locationCode: z.ZodString;
|
|
116
|
+
lotId: z.ZodNullable<z.ZodGUID>;
|
|
117
|
+
lotCode: z.ZodNullable<z.ZodString>;
|
|
118
|
+
quantity: z.ZodNumber;
|
|
119
|
+
expiresAt: z.ZodNullable<z.ZodString>;
|
|
120
|
+
}, z.core.$strict>>;
|
|
121
|
+
}, z.core.$strict>;
|
|
122
|
+
/** @beta */
|
|
123
|
+
export declare const inventoryAtpResultSchema: z.ZodObject<{
|
|
124
|
+
contractVersion: z.ZodLiteral<"inventory.v0">;
|
|
125
|
+
status: z.ZodEnum<{
|
|
126
|
+
unsupported: "unsupported";
|
|
127
|
+
fulfillable: "fulfillable";
|
|
128
|
+
insufficient: "insufficient";
|
|
129
|
+
review_required: "review_required";
|
|
130
|
+
}>;
|
|
131
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
132
|
+
locationId: z.ZodNullable<z.ZodGUID>;
|
|
133
|
+
locationCode: z.ZodNullable<z.ZodString>;
|
|
134
|
+
lines: z.ZodArray<z.ZodObject<{
|
|
135
|
+
skuId: z.ZodGUID;
|
|
136
|
+
sku: z.ZodString;
|
|
137
|
+
requestedQuantity: z.ZodNumber;
|
|
138
|
+
availableQuantity: z.ZodNumber;
|
|
139
|
+
missingQuantity: z.ZodNumber;
|
|
140
|
+
status: z.ZodEnum<{
|
|
141
|
+
unsupported: "unsupported";
|
|
142
|
+
fulfillable: "fulfillable";
|
|
143
|
+
insufficient: "insufficient";
|
|
144
|
+
review_required: "review_required";
|
|
145
|
+
}>;
|
|
146
|
+
allocations: z.ZodArray<z.ZodObject<{
|
|
147
|
+
skuId: z.ZodGUID;
|
|
148
|
+
sku: z.ZodString;
|
|
149
|
+
locationId: z.ZodGUID;
|
|
150
|
+
locationCode: z.ZodString;
|
|
151
|
+
lotId: z.ZodNullable<z.ZodGUID>;
|
|
152
|
+
lotCode: z.ZodNullable<z.ZodString>;
|
|
153
|
+
quantity: z.ZodNumber;
|
|
154
|
+
expiresAt: z.ZodNullable<z.ZodString>;
|
|
155
|
+
}, z.core.$strict>>;
|
|
156
|
+
}, z.core.$strict>>;
|
|
157
|
+
}, z.core.$strict>;
|
|
158
|
+
/** @beta */
|
|
159
|
+
export type InventoryAtpRequest = z.input<typeof inventoryAtpRequestSchema>;
|
|
160
|
+
/** @beta */
|
|
161
|
+
export type InventoryAtpResult = z.infer<typeof inventoryAtpResultSchema>;
|
|
162
|
+
/** @beta */
|
|
163
|
+
export type InventoryStockLine = z.infer<typeof inventoryStockLineSchema>;
|
|
164
|
+
/** @beta */
|
|
165
|
+
export interface CalculateInventoryAtpInput {
|
|
166
|
+
request: InventoryAtpRequest;
|
|
167
|
+
stock: InventoryStockLine[];
|
|
168
|
+
}
|
|
169
|
+
/** @beta */
|
|
170
|
+
export declare function availableNow(line: Pick<InventoryStockLine, "onHand" | "reserved" | "unavailable" | "safetyStock">): number;
|
|
171
|
+
/** @beta */
|
|
172
|
+
export declare function calculateInventoryAtp(input: CalculateInventoryAtpInput): InventoryAtpResult;
|