@openlup/platform 0.21.0 → 0.23.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 +432 -146
- package/README.md +82 -589
- package/dist/composition/composeBundle.d.ts +7 -0
- package/dist/composition/composeBundle.js +2 -0
- package/dist/composition/fulfillmentContribution.d.ts +21 -0
- package/dist/composition/fulfillmentContribution.js +19 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/fulfillment/adapterConformance.d.ts +3 -0
- package/dist/fulfillment/adapterConformance.js +223 -0
- package/dist/fulfillment/bindings.d.ts +3 -0
- package/dist/fulfillment/bindings.js +29 -0
- package/dist/fulfillment/commerceFulfillmentHandlers.d.ts +29 -0
- package/dist/fulfillment/commerceFulfillmentHandlers.js +184 -0
- package/dist/fulfillment/commerceFulfillmentReadModel.d.ts +86 -0
- package/dist/fulfillment/commerceFulfillmentReadModel.js +131 -0
- package/dist/fulfillment/contracts/alignment.d.ts +25 -0
- package/dist/fulfillment/contracts/alignment.js +1 -0
- package/dist/fulfillment/contracts/bindings.d.ts +51 -0
- package/dist/fulfillment/contracts/bindings.js +1 -0
- package/dist/fulfillment/contracts/capabilities.d.ts +29 -0
- package/dist/fulfillment/contracts/capabilities.js +1 -0
- package/dist/fulfillment/contracts/conformance.d.ts +95 -0
- package/dist/fulfillment/contracts/conformance.js +1 -0
- package/dist/fulfillment/contracts/customerAlignment.d.ts +6 -0
- package/dist/fulfillment/contracts/customerAlignment.js +1 -0
- package/dist/fulfillment/contracts/dispatch.d.ts +143 -0
- package/dist/fulfillment/contracts/dispatch.js +1 -0
- package/dist/fulfillment/contracts/index.d.ts +18 -0
- package/dist/fulfillment/contracts/index.js +1 -0
- package/dist/fulfillment/contracts/label.d.ts +35 -0
- package/dist/fulfillment/contracts/label.js +1 -0
- package/dist/fulfillment/contracts/nativeUnsupported.d.ts +6 -0
- package/dist/fulfillment/contracts/nativeUnsupported.js +1 -0
- package/dist/fulfillment/contracts/observation.d.ts +72 -0
- package/dist/fulfillment/contracts/observation.js +1 -0
- package/dist/fulfillment/contracts/operator.d.ts +64 -0
- package/dist/fulfillment/contracts/operator.js +1 -0
- package/dist/fulfillment/contracts/read.d.ts +110 -0
- package/dist/fulfillment/contracts/read.js +1 -0
- package/dist/fulfillment/contracts/readback.d.ts +59 -0
- package/dist/fulfillment/contracts/readback.js +1 -0
- package/dist/fulfillment/contracts/replacement.d.ts +34 -0
- package/dist/fulfillment/contracts/replacement.js +1 -0
- package/dist/fulfillment/contracts/store.d.ts +89 -0
- package/dist/fulfillment/contracts/store.js +1 -0
- package/dist/fulfillment/contracts/sync.d.ts +226 -0
- package/dist/fulfillment/contracts/sync.js +1 -0
- package/dist/fulfillment/currentFulfillmentParcel.d.ts +2 -0
- package/dist/fulfillment/currentFulfillmentParcel.js +1 -0
- package/dist/fulfillment/delayReadModel.d.ts +11 -0
- package/dist/fulfillment/delayReadModel.js +45 -0
- package/dist/fulfillment/deliveryAlignmentReadModel.d.ts +4 -0
- package/dist/fulfillment/deliveryAlignmentReadModel.js +12 -0
- package/dist/fulfillment/dispatchProjection.d.ts +26 -0
- package/dist/fulfillment/dispatchProjection.js +107 -0
- package/dist/fulfillment/fulfillmentObservationEffects.d.ts +66 -0
- package/dist/fulfillment/fulfillmentObservationEffects.js +36 -0
- package/dist/fulfillment/handlerSchemas.d.ts +647 -0
- package/dist/fulfillment/handlerSchemas.js +213 -0
- package/dist/fulfillment/handlerSupport.d.ts +33 -0
- package/dist/fulfillment/handlerSupport.js +25 -0
- package/dist/fulfillment/index.d.ts +38 -0
- package/dist/fulfillment/index.js +37 -0
- package/dist/fulfillment/labelLifecycle.d.ts +24 -0
- package/dist/fulfillment/labelLifecycle.js +9 -0
- package/dist/fulfillment/lowStockEvidenceHandler.d.ts +36 -0
- package/dist/fulfillment/lowStockEvidenceHandler.js +46 -0
- package/dist/fulfillment/migrations.d.ts +32 -0
- package/dist/fulfillment/migrations.js +9 -0
- package/dist/fulfillment/nativeUnsupported.d.ts +10 -0
- package/dist/fulfillment/nativeUnsupported.js +18 -0
- package/dist/fulfillment/normalizedFulfillmentFacts.d.ts +2 -0
- package/dist/fulfillment/normalizedFulfillmentFacts.js +1 -0
- package/dist/fulfillment/observationProjection.d.ts +32 -0
- package/dist/fulfillment/observationProjection.js +90 -0
- package/dist/fulfillment/omnipack/capabilities.d.ts +3 -0
- package/dist/fulfillment/omnipack/capabilities.js +16 -0
- package/dist/fulfillment/omnipack/client.d.ts +39 -0
- package/dist/fulfillment/omnipack/client.js +172 -0
- package/dist/fulfillment/omnipack/index.d.ts +55 -0
- package/dist/fulfillment/omnipack/index.js +408 -0
- package/dist/fulfillment/omnipack/mappers.d.ts +79 -0
- package/dist/fulfillment/omnipack/mappers.js +199 -0
- package/dist/fulfillment/omnipack/observation.d.ts +28 -0
- package/dist/fulfillment/omnipack/observation.js +72 -0
- package/dist/fulfillment/omnipack/omnipackDispatchPayload.d.ts +36 -0
- package/dist/fulfillment/omnipack/omnipackDispatchPayload.js +63 -0
- package/dist/fulfillment/omnipack/omnipackOutboundOrderPayload.d.ts +83 -0
- package/dist/fulfillment/omnipack/omnipackOutboundOrderPayload.js +113 -0
- package/dist/fulfillment/omnipack/omnipackProductPackPayload.d.ts +11 -0
- package/dist/fulfillment/omnipack/omnipackProductPackPayload.js +38 -0
- package/dist/fulfillment/omnipack/omnipackProductPayload.d.ts +18 -0
- package/dist/fulfillment/omnipack/omnipackProductPayload.js +51 -0
- package/dist/fulfillment/omnipack/omnipackReconciliationPayload.d.ts +1 -0
- package/dist/fulfillment/omnipack/omnipackReconciliationPayload.js +3 -0
- package/dist/fulfillment/omnipack/omnipackStatusVocabulary.d.ts +4 -0
- package/dist/fulfillment/omnipack/omnipackStatusVocabulary.js +87 -0
- package/dist/fulfillment/omnipack/omnipackWebhookPayload.d.ts +9 -0
- package/dist/fulfillment/omnipack/omnipackWebhookPayload.js +76 -0
- package/dist/fulfillment/omnipack/productSyncMappers.d.ts +18 -0
- package/dist/fulfillment/omnipack/productSyncMappers.js +43 -0
- package/dist/fulfillment/omnipack/providerError.d.ts +25 -0
- package/dist/fulfillment/omnipack/providerError.js +47 -0
- package/dist/fulfillment/omnipack/reconciliation.d.ts +15 -0
- package/dist/fulfillment/omnipack/reconciliation.js +55 -0
- package/dist/fulfillment/omnipack/trackingReferences.d.ts +16 -0
- package/dist/fulfillment/omnipack/trackingReferences.js +52 -0
- package/dist/fulfillment/paidFulfillmentRecovery.d.ts +67 -0
- package/dist/fulfillment/paidFulfillmentRecovery.js +154 -0
- package/dist/fulfillment/paidHandoff.d.ts +27 -0
- package/dist/fulfillment/paidHandoff.js +86 -0
- package/dist/fulfillment/paidHandoffPorts.d.ts +24 -0
- package/dist/fulfillment/paidHandoffPorts.js +3 -0
- package/dist/fulfillment/postgresDeliveryAlignment.d.ts +4 -0
- package/dist/fulfillment/postgresDeliveryAlignment.js +12 -0
- package/dist/fulfillment/postgresDispatchStore.d.ts +16 -0
- package/dist/fulfillment/postgresDispatchStore.js +44 -0
- package/dist/fulfillment/postgresEvidenceReadPort.d.ts +25 -0
- package/dist/fulfillment/postgresEvidenceReadPort.js +45 -0
- package/dist/fulfillment/postgresPaidFulfillmentRecovery.d.ts +25 -0
- package/dist/fulfillment/postgresPaidFulfillmentRecovery.js +26 -0
- package/dist/fulfillment/postgresPaidHandoffStore.d.ts +4 -0
- package/dist/fulfillment/postgresPaidHandoffStore.js +10 -0
- package/dist/fulfillment/postgresShipmentSpine.d.ts +70 -0
- package/dist/fulfillment/postgresShipmentSpine.js +246 -0
- package/dist/fulfillment/postgresStockSyncPort.d.ts +8 -0
- package/dist/fulfillment/postgresStockSyncPort.js +66 -0
- package/dist/fulfillment/productReconciliationEvidenceHandler.d.ts +36 -0
- package/dist/fulfillment/productReconciliationEvidenceHandler.js +50 -0
- package/dist/fulfillment/providerCapabilities.d.ts +9 -0
- package/dist/fulfillment/providerCapabilities.js +17 -0
- package/dist/fulfillment/providerCommandLedger.d.ts +143 -0
- package/dist/fulfillment/providerCommandLedger.js +14 -0
- package/dist/fulfillment/providerDispatchAcceptance.d.ts +27 -0
- package/dist/fulfillment/providerDispatchAcceptance.js +29 -0
- package/dist/fulfillment/providerDispatchError.d.ts +6 -0
- package/dist/fulfillment/providerDispatchError.js +5 -0
- package/dist/fulfillment/providerDispatchWorker.d.ts +44 -0
- package/dist/fulfillment/providerDispatchWorker.js +199 -0
- package/dist/fulfillment/providerProductSyncWorker.d.ts +28 -0
- package/dist/fulfillment/providerProductSyncWorker.js +122 -0
- package/dist/fulfillment/providerReconciliationError.d.ts +9 -0
- package/dist/fulfillment/providerReconciliationError.js +17 -0
- package/dist/fulfillment/providerReconciliationTerminalSkip.d.ts +2 -0
- package/dist/fulfillment/providerReconciliationTerminalSkip.js +6 -0
- package/dist/fulfillment/providerReconciliationTimestamp.d.ts +1 -0
- package/dist/fulfillment/providerReconciliationTimestamp.js +27 -0
- package/dist/fulfillment/providerReconciliationWorker.d.ts +90 -0
- package/dist/fulfillment/providerReconciliationWorker.js +207 -0
- package/dist/fulfillment/providerStockAvailability.d.ts +13 -0
- package/dist/fulfillment/providerStockAvailability.js +51 -0
- package/dist/fulfillment/providerStockSyncWorker.d.ts +41 -0
- package/dist/fulfillment/providerStockSyncWorker.js +295 -0
- package/dist/fulfillment/providerTrackingReferences.d.ts +16 -0
- package/dist/fulfillment/providerTrackingReferences.js +23 -0
- package/dist/fulfillment/replacementRefusals.d.ts +5 -0
- package/dist/fulfillment/replacementRefusals.js +38 -0
- package/dist/fulfillment/replacementShipmentHandler.d.ts +5 -0
- package/dist/fulfillment/replacementShipmentHandler.js +46 -0
- package/dist/fulfillment/schemaCapabilities.d.ts +13 -0
- package/dist/fulfillment/schemaCapabilities.js +7 -0
- package/dist/fulfillment/shipmentsOverviewHandler.d.ts +16 -0
- package/dist/fulfillment/shipmentsOverviewHandler.js +54 -0
- package/dist/fulfillment/statusMap.d.ts +2 -0
- package/dist/fulfillment/statusMap.js +1 -0
- package/dist/fulfillment/trackingProgress.d.ts +1 -0
- package/dist/fulfillment/trackingProgress.js +1 -0
- package/dist/fulfillment/verifiedIngress.d.ts +79 -0
- package/dist/fulfillment/verifiedIngress.js +94 -0
- package/dist/invoicing/capabilities.d.ts +11 -0
- package/dist/invoicing/capabilities.js +10 -0
- package/dist/invoicing/contracts/capabilities.d.ts +40 -0
- package/dist/invoicing/contracts/capabilities.js +1 -0
- package/dist/invoicing/contracts/companyLookup.d.ts +49 -0
- package/dist/invoicing/contracts/companyLookup.js +1 -0
- package/dist/invoicing/contracts/delivery.d.ts +74 -0
- package/dist/invoicing/contracts/delivery.js +1 -0
- package/dist/invoicing/contracts/facts.d.ts +100 -0
- package/dist/invoicing/contracts/facts.js +1 -0
- package/dist/invoicing/contracts/host.d.ts +246 -0
- package/dist/invoicing/contracts/host.js +1 -0
- package/dist/invoicing/contracts/index.d.ts +16 -0
- package/dist/invoicing/contracts/index.js +1 -0
- package/dist/invoicing/contracts/provider.d.ts +126 -0
- package/dist/invoicing/contracts/provider.js +1 -0
- package/dist/invoicing/contracts/settlement.d.ts +66 -0
- package/dist/invoicing/contracts/settlement.js +1 -0
- package/dist/invoicing/contracts/store.d.ts +267 -0
- package/dist/invoicing/contracts/store.js +1 -0
- package/dist/invoicing/index.d.ts +1 -0
- package/dist/invoicing/index.js +1 -0
- package/dist/payments/stripe/index.d.ts +25 -0
- package/dist/payments/stripe/index.js +25 -0
- package/dist/payments/tpay/index.d.ts +30 -0
- package/dist/payments/tpay/index.js +30 -0
- package/dist/subscriptions/automaticRenewal.d.ts +44 -0
- package/dist/subscriptions/automaticRenewal.js +81 -0
- package/dist/subscriptions/automaticRenewalExecution.d.ts +21 -0
- package/dist/subscriptions/automaticRenewalExecution.js +44 -0
- package/dist/subscriptions/contracts/commands.d.ts +115 -0
- package/dist/subscriptions/contracts/commands.js +1 -0
- package/dist/subscriptions/contracts/dunning.d.ts +133 -0
- package/dist/subscriptions/contracts/dunning.js +1 -0
- package/dist/subscriptions/contracts/index.d.ts +6 -0
- package/dist/subscriptions/contracts/index.js +1 -0
- package/dist/subscriptions/contracts/messages.d.ts +43 -0
- package/dist/subscriptions/contracts/messages.js +1 -0
- package/dist/subscriptions/contracts/renewal.d.ts +533 -0
- package/dist/subscriptions/contracts/renewal.js +1 -0
- package/dist/subscriptions/contracts/stockWait.d.ts +73 -0
- package/dist/subscriptions/contracts/stockWait.js +1 -0
- package/dist/subscriptions/index.d.ts +39 -0
- package/dist/subscriptions/index.js +17 -0
- package/dist/subscriptions/paymentOutcomes.d.ts +8 -0
- package/dist/subscriptions/paymentOutcomes.js +27 -0
- package/dist/subscriptions/postgresRenewalStore.d.ts +20 -0
- package/dist/subscriptions/postgresRenewalStore.js +61 -0
- package/dist/subscriptions/postgresRetryStore.d.ts +78 -0
- package/dist/subscriptions/postgresRetryStore.js +142 -0
- package/dist/subscriptions/renewalCycleFailureMechanism.d.ts +34 -0
- package/dist/subscriptions/renewalCycleFailureMechanism.js +79 -0
- package/dist/subscriptions/renewalCycleMechanism.d.ts +46 -0
- package/dist/subscriptions/renewalCycleMechanism.js +157 -0
- package/dist/subscriptions/renewalCycleRefusalMechanism.d.ts +41 -0
- package/dist/subscriptions/renewalCycleRefusalMechanism.js +160 -0
- package/dist/subscriptions/renewalIdentity.d.ts +3 -0
- package/dist/subscriptions/renewalIdentity.js +4 -0
- package/package.json +37 -2
- package/release-gates.json +97 -3
- package/sql/20260808210000_fulfillment_shipment_rail.sql +810 -0
- package/sql/20260819150000_fulfillment_shipment_sequence.sql +98 -0
- package/sql/20261022080000_fulfillment_command_ledger.sql +786 -0
- package/sql/20261022090000_fulfillment_delivery_alignment.sql +1106 -0
- package/sql/20261022160000_subscription_retry_lifecycle.sql +328 -0
- package/sql/20261022165000_fulfillment_stock_evidence_replay_reopen.sql +30 -0
- package/src/composition/composeBundle.ts +4 -0
- package/src/composition/fulfillmentContribution.ts +28 -0
- package/src/composition/index.ts +1 -0
- package/src/fulfillment/adapterConformance.ts +195 -0
- package/src/fulfillment/bindings.ts +26 -0
- package/src/fulfillment/commerceFulfillmentHandlers.ts +263 -0
- package/src/fulfillment/commerceFulfillmentReadModel.ts +257 -0
- package/src/fulfillment/contracts/alignment.ts +13 -0
- package/src/fulfillment/contracts/bindings.ts +30 -0
- package/src/fulfillment/contracts/capabilities.ts +15 -0
- package/src/fulfillment/contracts/conformance.ts +63 -0
- package/src/fulfillment/contracts/customerAlignment.ts +4 -0
- package/src/fulfillment/contracts/dispatch.ts +73 -0
- package/src/fulfillment/contracts/index.ts +18 -0
- package/src/fulfillment/contracts/label.ts +9 -0
- package/src/fulfillment/contracts/nativeUnsupported.ts +6 -0
- package/src/fulfillment/contracts/observation.ts +26 -0
- package/src/fulfillment/contracts/operator.ts +31 -0
- package/src/fulfillment/contracts/read.ts +42 -0
- package/src/fulfillment/contracts/readback.ts +24 -0
- package/src/fulfillment/contracts/replacement.ts +15 -0
- package/src/fulfillment/contracts/store.ts +27 -0
- package/src/fulfillment/contracts/sync.ts +72 -0
- package/src/fulfillment/currentFulfillmentParcel.ts +2 -0
- package/src/fulfillment/delayReadModel.ts +60 -0
- package/src/fulfillment/deliveryAlignmentReadModel.ts +15 -0
- package/src/fulfillment/dispatchProjection.ts +105 -0
- package/src/fulfillment/fulfillmentObservationEffects.ts +86 -0
- package/src/fulfillment/handlerSchemas.ts +255 -0
- package/src/fulfillment/handlerSupport.ts +25 -0
- package/src/fulfillment/index.ts +39 -0
- package/src/fulfillment/labelLifecycle.ts +11 -0
- package/src/fulfillment/lowStockEvidenceHandler.ts +60 -0
- package/src/fulfillment/migrations.ts +9 -0
- package/src/fulfillment/nativeUnsupported.ts +23 -0
- package/src/fulfillment/normalizedFulfillmentFacts.ts +2 -0
- package/src/fulfillment/observationProjection.ts +92 -0
- package/src/fulfillment/omnipack/capabilities.ts +18 -0
- package/src/fulfillment/omnipack/client.ts +270 -0
- package/src/fulfillment/omnipack/index.ts +369 -0
- package/src/fulfillment/omnipack/mappers.ts +282 -0
- package/src/fulfillment/omnipack/observation.ts +79 -0
- package/src/fulfillment/omnipack/omnipackDispatchPayload.ts +93 -0
- package/src/fulfillment/omnipack/omnipackOutboundOrderPayload.ts +194 -0
- package/src/fulfillment/omnipack/omnipackProductPackPayload.ts +47 -0
- package/src/fulfillment/omnipack/omnipackProductPayload.ts +67 -0
- package/src/fulfillment/omnipack/omnipackReconciliationPayload.ts +7 -0
- package/src/fulfillment/omnipack/omnipackStatusVocabulary.ts +103 -0
- package/src/fulfillment/omnipack/omnipackWebhookPayload.ts +73 -0
- package/src/fulfillment/omnipack/productSyncMappers.ts +74 -0
- package/src/fulfillment/omnipack/providerError.ts +64 -0
- package/src/fulfillment/omnipack/reconciliation.ts +56 -0
- package/src/fulfillment/omnipack/trackingReferences.ts +77 -0
- package/src/fulfillment/paidFulfillmentRecovery.ts +252 -0
- package/src/fulfillment/paidHandoff.ts +122 -0
- package/src/fulfillment/paidHandoffPorts.ts +21 -0
- package/src/fulfillment/postgresDeliveryAlignment.ts +14 -0
- package/src/fulfillment/postgresDispatchStore.ts +55 -0
- package/src/fulfillment/postgresEvidenceReadPort.ts +59 -0
- package/src/fulfillment/postgresPaidFulfillmentRecovery.ts +29 -0
- package/src/fulfillment/postgresPaidHandoffStore.ts +11 -0
- package/src/fulfillment/postgresShipmentSpine.ts +338 -0
- package/src/fulfillment/postgresStockSyncPort.ts +68 -0
- package/src/fulfillment/productReconciliationEvidenceHandler.ts +64 -0
- package/src/fulfillment/providerCapabilities.ts +18 -0
- package/src/fulfillment/providerCommandLedger.ts +53 -0
- package/src/fulfillment/providerDispatchAcceptance.ts +37 -0
- package/src/fulfillment/providerDispatchError.ts +7 -0
- package/src/fulfillment/providerDispatchWorker.ts +159 -0
- package/src/fulfillment/providerProductSyncWorker.ts +154 -0
- package/src/fulfillment/providerReconciliationError.ts +20 -0
- package/src/fulfillment/providerReconciliationTerminalSkip.ts +8 -0
- package/src/fulfillment/providerReconciliationTimestamp.ts +34 -0
- package/src/fulfillment/providerReconciliationWorker.ts +206 -0
- package/src/fulfillment/providerStockAvailability.ts +60 -0
- package/src/fulfillment/providerStockSyncWorker.ts +318 -0
- package/src/fulfillment/providerTrackingReferences.ts +32 -0
- package/src/fulfillment/replacementRefusals.ts +43 -0
- package/src/fulfillment/replacementShipmentHandler.ts +47 -0
- package/src/fulfillment/schemaCapabilities.ts +7 -0
- package/src/fulfillment/shipmentsOverviewHandler.ts +73 -0
- package/src/fulfillment/statusMap.ts +2 -0
- package/src/fulfillment/trackingProgress.ts +1 -0
- package/src/fulfillment/verifiedIngress.ts +113 -0
- package/src/invoicing/capabilities.ts +9 -0
- package/src/invoicing/contracts/capabilities.ts +15 -0
- package/src/invoicing/contracts/companyLookup.ts +13 -0
- package/src/invoicing/contracts/delivery.ts +24 -0
- package/src/invoicing/contracts/facts.ts +60 -0
- package/src/invoicing/contracts/host.ts +85 -0
- package/src/invoicing/contracts/index.ts +16 -0
- package/src/invoicing/contracts/provider.ts +78 -0
- package/src/invoicing/contracts/settlement.ts +39 -0
- package/src/invoicing/contracts/store.ts +98 -0
- package/src/invoicing/index.ts +1 -0
- package/src/payments/stripe/index.ts +30 -0
- package/src/payments/tpay/index.ts +36 -0
- package/src/subscriptions/automaticRenewal.ts +87 -0
- package/src/subscriptions/automaticRenewalExecution.ts +39 -0
- package/src/subscriptions/contracts/commands.ts +46 -0
- package/src/subscriptions/contracts/dunning.ts +48 -0
- package/src/subscriptions/contracts/index.ts +6 -0
- package/src/subscriptions/contracts/messages.ts +18 -0
- package/src/subscriptions/contracts/renewal.ts +325 -0
- package/src/subscriptions/contracts/stockWait.ts +34 -0
- package/src/subscriptions/index.ts +24 -0
- package/src/subscriptions/paymentOutcomes.ts +23 -0
- package/src/subscriptions/postgresRenewalStore.ts +76 -0
- package/src/subscriptions/postgresRetryStore.ts +174 -0
- package/src/subscriptions/renewalCycleFailureMechanism.ts +119 -0
- package/src/subscriptions/renewalCycleMechanism.ts +225 -0
- package/src/subscriptions/renewalCycleRefusalMechanism.ts +215 -0
- package/src/subscriptions/renewalIdentity.ts +5 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FulfillmentServices, FulfillmentServicesOptions } from './contracts/index.js';
|
|
2
|
+
import { FULFILLMENT_NATIVE_UNSUPPORTED } from './nativeUnsupported.js';
|
|
3
|
+
import { readFulfillmentCapabilities } from './providerCapabilities.js';
|
|
4
|
+
import { runFulfillmentDispatchWorker } from './providerDispatchWorker.js';
|
|
5
|
+
|
|
6
|
+
/** Explicit selection; no environment readers, implicit provider or automatic dispatch. */
|
|
7
|
+
export function createFulfillmentServices(options: FulfillmentServicesOptions): FulfillmentServices {
|
|
8
|
+
const { enabled, selection, capability, store, provider, readiness: observe, listCandidates, now } = options;
|
|
9
|
+
const config = { ...options.config }, keys = { ...options.keys };
|
|
10
|
+
const readiness: FulfillmentServices['readiness'] = async () => {
|
|
11
|
+
if (enabled !== true) return { ready: false, refusal: { reason: 'disabled', phase: 'selection', retryable: false } };
|
|
12
|
+
const selected = readFulfillmentCapabilities(capability ? [capability] : [], selection);
|
|
13
|
+
if (selected.kind === 'refused') return { ready: false, refusal: selected.refusal };
|
|
14
|
+
if (!selected.descriptor.operations.dispatch.supported) return { ready: false, refusal: { reason: 'unsupported', phase: 'selection', retryable: false } };
|
|
15
|
+
if (!store || (config.mode !== "shadow" && !provider)) return { ready: false, refusal: FULFILLMENT_NATIVE_UNSUPPORTED };
|
|
16
|
+
return observe();
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
readiness,
|
|
20
|
+
async runDispatch() {
|
|
21
|
+
const state = await readiness();
|
|
22
|
+
if (!state.ready) return 'kind' in state.refusal ? state.refusal : { kind: 'refused', refusal: state.refusal };
|
|
23
|
+
return runFulfillmentDispatchWorker({ store: store!, provider, listCandidates, keys, config, now });
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import {
|
|
2
|
+
adminCommerceFulfillmentCancelRequestSchema,
|
|
3
|
+
adminCommerceFulfillmentCreateRequestSchema,
|
|
4
|
+
adminCommerceFulfillmentHandOffRequestSchema,
|
|
5
|
+
adminCommerceFulfillmentMutationResponseSchema,
|
|
6
|
+
adminCommerceFulfillmentOrderDetailRequestSchema,
|
|
7
|
+
adminCommerceFulfillmentOrderDetailResponseSchema,
|
|
8
|
+
adminCommerceFulfillmentOrdersListRequestSchema,
|
|
9
|
+
adminCommerceFulfillmentOrdersListResponseSchema,
|
|
10
|
+
adminCommerceFulfillmentRecordLabelRequestSchema,
|
|
11
|
+
adminCommerceFulfillmentRecordProviderAttemptRequestSchema,
|
|
12
|
+
adminCommerceFulfillmentTrackingEventRequestSchema,
|
|
13
|
+
} from "./handlerSchemas.js";
|
|
14
|
+
import type { FulfillmentOperatorMutationPort as CommerceFulfillmentMutationPort } from './contracts/operator.js';
|
|
15
|
+
import type { FulfillmentCommerceReadPort as CommerceFulfillmentReadPort } from './contracts/read.js';
|
|
16
|
+
import { CommerceFulfillmentConflictError, authorize, isNativeUnsupported, type BaseDeps, type FulfillmentHandlerRequest } from './handlerSupport.js';
|
|
17
|
+
type PortRef<T> = T | (() => T);
|
|
18
|
+
|
|
19
|
+
export function createAdminCommerceFulfillmentOrdersListHandler<Response>({
|
|
20
|
+
authorizeAdmin,
|
|
21
|
+
respond,
|
|
22
|
+
fulfillmentPort,
|
|
23
|
+
}: BaseDeps<Response> & {
|
|
24
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentReadPort, "listCommerceFulfillmentOrders">>;
|
|
25
|
+
}) {
|
|
26
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = respond;
|
|
27
|
+
return async function handler(req: FulfillmentHandlerRequest, res: Response): Promise<void> {
|
|
28
|
+
if (req.method !== "GET") return sendMethodNotAllowed(res, ["GET"]);
|
|
29
|
+
if (!(await authorize(req, res, authorizeAdmin, respond))) return;
|
|
30
|
+
|
|
31
|
+
const request = adminCommerceFulfillmentOrdersListRequestSchema.safeParse(req.query);
|
|
32
|
+
if (!request.success) {
|
|
33
|
+
sendBffError(res, "BAD_REQUEST", "Invalid admin commerce fulfillment orders request", {
|
|
34
|
+
details: request.error.flatten(),
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const port = resolvePort(fulfillmentPort);
|
|
41
|
+
const result = await port.listCommerceFulfillmentOrders(request.data);
|
|
42
|
+
if (isNativeUnsupported(result)) {
|
|
43
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Native fulfillment operation unsupported", { details: result });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const response = adminCommerceFulfillmentOrdersListResponseSchema.safeParse(result);
|
|
47
|
+
if (!response.success) {
|
|
48
|
+
sendBffError(res, "INVALID_RESPONSE", "Admin commerce fulfillment orders response invalid");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
sendBffSuccess(res, response.data);
|
|
52
|
+
} catch {
|
|
53
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin commerce fulfillment orders read failed");
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createAdminCommerceFulfillmentOrderDetailHandler<Response>({
|
|
59
|
+
authorizeAdmin,
|
|
60
|
+
respond,
|
|
61
|
+
fulfillmentPort,
|
|
62
|
+
}: BaseDeps<Response> & {
|
|
63
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentReadPort, "getCommerceFulfillmentOrderDetail">>;
|
|
64
|
+
}) {
|
|
65
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = respond;
|
|
66
|
+
return async function handler(req: FulfillmentHandlerRequest, res: Response): Promise<void> {
|
|
67
|
+
if (req.method !== "GET") return sendMethodNotAllowed(res, ["GET"]);
|
|
68
|
+
if (!(await authorize(req, res, authorizeAdmin, respond))) return;
|
|
69
|
+
|
|
70
|
+
const request = adminCommerceFulfillmentOrderDetailRequestSchema.safeParse(req.query);
|
|
71
|
+
if (!request.success) {
|
|
72
|
+
sendBffError(res, "BAD_REQUEST", "Invalid admin commerce fulfillment detail request", {
|
|
73
|
+
details: request.error.flatten(),
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const port = resolvePort(fulfillmentPort);
|
|
80
|
+
const result = await port.getCommerceFulfillmentOrderDetail(request.data);
|
|
81
|
+
if (isNativeUnsupported(result)) {
|
|
82
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Native fulfillment operation unsupported", { details: result });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!result) {
|
|
86
|
+
sendBffError(res, "NOT_FOUND", "Commerce fulfillment order not found");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const response = adminCommerceFulfillmentOrderDetailResponseSchema.safeParse(result);
|
|
90
|
+
if (!response.success) {
|
|
91
|
+
sendBffError(res, "INVALID_RESPONSE", "Admin commerce fulfillment detail response invalid");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
sendBffSuccess(res, response.data);
|
|
95
|
+
} catch {
|
|
96
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin commerce fulfillment detail read failed");
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function createAdminCommerceFulfillmentCreateOrderHandler<Response>({
|
|
102
|
+
authorizeAdmin,
|
|
103
|
+
respond,
|
|
104
|
+
fulfillmentPort,
|
|
105
|
+
}: BaseDeps<Response> & {
|
|
106
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentMutationPort, "createCommerceFulfillmentOrder">>;
|
|
107
|
+
}) {
|
|
108
|
+
return createMutationHandler({
|
|
109
|
+
authorizeAdmin,
|
|
110
|
+
respond,
|
|
111
|
+
requestSchema: adminCommerceFulfillmentCreateRequestSchema,
|
|
112
|
+
invalidMessage: "Invalid admin commerce fulfillment create request",
|
|
113
|
+
execute: (request, actorUserId) => {
|
|
114
|
+
const port = resolvePort(fulfillmentPort);
|
|
115
|
+
return port.createCommerceFulfillmentOrder({ ...request, actorUserId });
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function createAdminCommerceFulfillmentRecordProviderAttemptHandler<Response>({
|
|
121
|
+
authorizeAdmin,
|
|
122
|
+
respond,
|
|
123
|
+
fulfillmentPort,
|
|
124
|
+
}: BaseDeps<Response> & {
|
|
125
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentMutationPort, "recordCommerceFulfillmentProviderAttempt">>;
|
|
126
|
+
}) {
|
|
127
|
+
return createMutationHandler({
|
|
128
|
+
authorizeAdmin,
|
|
129
|
+
respond,
|
|
130
|
+
requestSchema: adminCommerceFulfillmentRecordProviderAttemptRequestSchema,
|
|
131
|
+
invalidMessage: "Invalid admin commerce fulfillment provider attempt request",
|
|
132
|
+
execute: (request, actorUserId) => {
|
|
133
|
+
const port = resolvePort(fulfillmentPort);
|
|
134
|
+
return port.recordCommerceFulfillmentProviderAttempt({ ...request, actorUserId });
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function createAdminCommerceFulfillmentRecordLabelHandler<Response>({
|
|
140
|
+
authorizeAdmin,
|
|
141
|
+
respond,
|
|
142
|
+
fulfillmentPort,
|
|
143
|
+
}: BaseDeps<Response> & {
|
|
144
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentMutationPort, "recordCommerceFulfillmentLabel">>;
|
|
145
|
+
}) {
|
|
146
|
+
return createMutationHandler({
|
|
147
|
+
authorizeAdmin,
|
|
148
|
+
respond,
|
|
149
|
+
requestSchema: adminCommerceFulfillmentRecordLabelRequestSchema,
|
|
150
|
+
invalidMessage: "Invalid admin commerce fulfillment label request",
|
|
151
|
+
execute: (request, actorUserId) => {
|
|
152
|
+
const port = resolvePort(fulfillmentPort);
|
|
153
|
+
return port.recordCommerceFulfillmentLabel({ ...request, actorUserId });
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function createAdminCommerceFulfillmentHandOffHandler<Response>({
|
|
159
|
+
authorizeAdmin,
|
|
160
|
+
respond,
|
|
161
|
+
fulfillmentPort,
|
|
162
|
+
}: BaseDeps<Response> & {
|
|
163
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentMutationPort, "handOffCommerceFulfillmentOrder">>;
|
|
164
|
+
}) {
|
|
165
|
+
return createMutationHandler({
|
|
166
|
+
authorizeAdmin,
|
|
167
|
+
respond,
|
|
168
|
+
requestSchema: adminCommerceFulfillmentHandOffRequestSchema,
|
|
169
|
+
invalidMessage: "Invalid admin commerce fulfillment handoff request",
|
|
170
|
+
execute: (request, actorUserId) => {
|
|
171
|
+
const port = resolvePort(fulfillmentPort);
|
|
172
|
+
return port.handOffCommerceFulfillmentOrder({ ...request, actorUserId });
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function createAdminCommerceFulfillmentTrackingEventHandler<Response>({
|
|
178
|
+
authorizeAdmin,
|
|
179
|
+
respond,
|
|
180
|
+
fulfillmentPort,
|
|
181
|
+
}: BaseDeps<Response> & {
|
|
182
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentMutationPort, "recordCommerceFulfillmentTrackingEvent">>;
|
|
183
|
+
}) {
|
|
184
|
+
return createMutationHandler({
|
|
185
|
+
authorizeAdmin,
|
|
186
|
+
respond,
|
|
187
|
+
requestSchema: adminCommerceFulfillmentTrackingEventRequestSchema,
|
|
188
|
+
invalidMessage: "Invalid admin commerce fulfillment tracking event request",
|
|
189
|
+
execute: (request, actorUserId) => {
|
|
190
|
+
const port = resolvePort(fulfillmentPort);
|
|
191
|
+
return port.recordCommerceFulfillmentTrackingEvent({ ...request, actorUserId });
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function createAdminCommerceFulfillmentCancelOrderHandler<Response>({
|
|
197
|
+
authorizeAdmin,
|
|
198
|
+
respond,
|
|
199
|
+
fulfillmentPort,
|
|
200
|
+
}: BaseDeps<Response> & {
|
|
201
|
+
fulfillmentPort: PortRef<Pick<CommerceFulfillmentMutationPort, "cancelCommerceFulfillmentOrder">>;
|
|
202
|
+
}) {
|
|
203
|
+
return createMutationHandler({
|
|
204
|
+
authorizeAdmin,
|
|
205
|
+
respond,
|
|
206
|
+
requestSchema: adminCommerceFulfillmentCancelRequestSchema,
|
|
207
|
+
invalidMessage: "Invalid admin commerce fulfillment cancel request",
|
|
208
|
+
execute: (request, actorUserId) => {
|
|
209
|
+
const port = resolvePort(fulfillmentPort);
|
|
210
|
+
return port.cancelCommerceFulfillmentOrder({ ...request, actorUserId });
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function resolvePort<T>(port: PortRef<T>): T {
|
|
216
|
+
return typeof port === "function" ? (port as () => T)() : port;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function createMutationHandler<T, Response>({
|
|
220
|
+
authorizeAdmin,
|
|
221
|
+
respond,
|
|
222
|
+
requestSchema,
|
|
223
|
+
invalidMessage,
|
|
224
|
+
execute,
|
|
225
|
+
}: BaseDeps<Response> & {
|
|
226
|
+
requestSchema: { safeParse: (input: unknown) => { success: true; data: T } | { success: false; error: { flatten: () => unknown } } };
|
|
227
|
+
invalidMessage: string;
|
|
228
|
+
execute: (request: T, actorUserId: string) => Promise<unknown>;
|
|
229
|
+
}) {
|
|
230
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = respond;
|
|
231
|
+
return async function handler(req: FulfillmentHandlerRequest, res: Response): Promise<void> {
|
|
232
|
+
if (req.method !== "POST") return sendMethodNotAllowed(res, ["POST"]);
|
|
233
|
+
const auth = await authorize(req, res, authorizeAdmin, respond);
|
|
234
|
+
if (!auth) return;
|
|
235
|
+
const request = requestSchema.safeParse(req.body ?? {});
|
|
236
|
+
if (request.success === false) {
|
|
237
|
+
sendBffError(res, "BAD_REQUEST", invalidMessage, { details: request.error.flatten() });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const result = await execute(request.data, auth.userId);
|
|
243
|
+
if (isNativeUnsupported(result)) {
|
|
244
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Native fulfillment operation unsupported", { details: result });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const response = adminCommerceFulfillmentMutationResponseSchema.safeParse(result);
|
|
248
|
+
if (!response.success) {
|
|
249
|
+
sendBffError(res, "INVALID_RESPONSE", "Admin commerce fulfillment mutation response invalid");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
sendBffSuccess(res, response.data);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (error instanceof CommerceFulfillmentConflictError) {
|
|
255
|
+
sendBffError(res, "CONFLICT", error.message, { details: error.details });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin commerce fulfillment mutation failed", {
|
|
259
|
+
details: { reason: "fulfillment_mutation_failed" },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import type { FulfillmentCommerceReadPolicy, FulfillmentPaymentStatus, FulfillmentOrderStatus as OrderStatus } from './contracts/read.js';
|
|
2
|
+
import type { SubscriptionCycleStatus } from '@openlup/core/subscription';
|
|
3
|
+
import {
|
|
4
|
+
COMMERCE_FULFILLMENT_CONTRACT_VERSION,
|
|
5
|
+
adminCommerceFulfillmentOrderDetailResponseSchema,
|
|
6
|
+
adminCommerceFulfillmentOrdersListResponseSchema,
|
|
7
|
+
type AdminCommerceFulfillmentOrderDetailResponse,
|
|
8
|
+
type AdminCommerceFulfillmentOrdersListResponse,
|
|
9
|
+
type CommerceFulfillmentOrder,
|
|
10
|
+
type CommerceFulfillmentOperationType,
|
|
11
|
+
type CommerceFulfillmentStatus,
|
|
12
|
+
} from "./handlerSchemas.js";
|
|
13
|
+
|
|
14
|
+
export interface CommerceFulfillmentOrderRow {
|
|
15
|
+
id: string;
|
|
16
|
+
order_id: string;
|
|
17
|
+
client_id: string | null;
|
|
18
|
+
status: CommerceFulfillmentStatus;
|
|
19
|
+
provider_kind: string | null;
|
|
20
|
+
shipping_address_snapshot: CommerceFulfillmentOrder["shippingAddress"];
|
|
21
|
+
created_at: string;
|
|
22
|
+
updated_at: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CommerceFulfillmentLineRow {
|
|
26
|
+
id: string;
|
|
27
|
+
fulfillment_order_id: string;
|
|
28
|
+
order_item_id: string;
|
|
29
|
+
sku_id: string;
|
|
30
|
+
sku: string;
|
|
31
|
+
title: string | null;
|
|
32
|
+
quantity: number;
|
|
33
|
+
inventory_reservation_ids: string[];
|
|
34
|
+
product_snapshot: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CommerceFulfillmentOperationRow {
|
|
38
|
+
id: string;
|
|
39
|
+
fulfillment_order_id: string;
|
|
40
|
+
operation_type: CommerceFulfillmentOperationType;
|
|
41
|
+
actor_user_id: string | null;
|
|
42
|
+
occurred_at: string;
|
|
43
|
+
payload: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CommerceFulfillmentOrderContextRow {
|
|
47
|
+
id: string;
|
|
48
|
+
status: OrderStatus;
|
|
49
|
+
mode: "one_time" | "subscription_cycle";
|
|
50
|
+
shipping_address_id: string | null;
|
|
51
|
+
subscription_cycle_id: string | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CommerceFulfillmentPaymentIntentRow {
|
|
55
|
+
id: string;
|
|
56
|
+
order_id: string;
|
|
57
|
+
status: FulfillmentPaymentStatus;
|
|
58
|
+
provider_payment_id: string | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface CommerceFulfillmentInventoryReservationRow {
|
|
62
|
+
id: string;
|
|
63
|
+
order_id: string;
|
|
64
|
+
status: "reserved" | "released" | "consumed" | "expired";
|
|
65
|
+
expires_at: string | null;
|
|
66
|
+
location_id: string | null;
|
|
67
|
+
inventory_locations?: { code?: string | null } | null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface CommerceFulfillmentShipmentRefRow {
|
|
71
|
+
order_id: string;
|
|
72
|
+
provider_tracking_id: string;
|
|
73
|
+
active: boolean;
|
|
74
|
+
created_at?: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function buildCommerceFulfillmentOrdersListResponse(input: {
|
|
78
|
+
fulfillmentOrders: CommerceFulfillmentOrderRow[];
|
|
79
|
+
lines: CommerceFulfillmentLineRow[];
|
|
80
|
+
latestOperations: CommerceFulfillmentOperationRow[];
|
|
81
|
+
orderContexts: CommerceFulfillmentOrderContextRow[];
|
|
82
|
+
paymentIntents: CommerceFulfillmentPaymentIntentRow[];
|
|
83
|
+
inventoryReservations: CommerceFulfillmentInventoryReservationRow[];
|
|
84
|
+
shipmentRefs: CommerceFulfillmentShipmentRefRow[];
|
|
85
|
+
activeHoldCounts: Record<string, number>;
|
|
86
|
+
subscriptionCycleStatuses: Record<string, SubscriptionCycleStatus | null>;
|
|
87
|
+
totalCount: number;
|
|
88
|
+
page: number;
|
|
89
|
+
pageSize: number;
|
|
90
|
+
}, policy: FulfillmentCommerceReadPolicy): AdminCommerceFulfillmentOrdersListResponse {
|
|
91
|
+
return adminCommerceFulfillmentOrdersListResponseSchema.parse({
|
|
92
|
+
contractVersion: COMMERCE_FULFILLMENT_CONTRACT_VERSION,
|
|
93
|
+
orders: input.fulfillmentOrders.map((fulfillmentOrder) =>
|
|
94
|
+
buildCommerceFulfillmentOrder({
|
|
95
|
+
fulfillmentOrder,
|
|
96
|
+
lines: input.lines.filter((line) => line.fulfillment_order_id === fulfillmentOrder.id),
|
|
97
|
+
latestOperation:
|
|
98
|
+
input.latestOperations.find((operation) => operation.fulfillment_order_id === fulfillmentOrder.id) ??
|
|
99
|
+
null,
|
|
100
|
+
orderContext: findRequired(input.orderContexts, fulfillmentOrder.order_id, "order context"),
|
|
101
|
+
paymentIntent: findRequired(input.paymentIntents, fulfillmentOrder.order_id, "payment intent"),
|
|
102
|
+
inventoryReservations: input.inventoryReservations.filter(
|
|
103
|
+
(reservation) => reservation.order_id === fulfillmentOrder.order_id,
|
|
104
|
+
),
|
|
105
|
+
shipmentRef: input.shipmentRefs.find((ref) => ref.order_id === fulfillmentOrder.order_id && ref.active) ?? null,
|
|
106
|
+
activeHoldCount: input.activeHoldCounts[fulfillmentOrder.order_id] ?? 0,
|
|
107
|
+
subscriptionCycleStatuses: input.subscriptionCycleStatuses,
|
|
108
|
+
policy,
|
|
109
|
+
}),
|
|
110
|
+
),
|
|
111
|
+
totalCount: input.totalCount,
|
|
112
|
+
page: input.page,
|
|
113
|
+
pageSize: input.pageSize,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function buildCommerceFulfillmentOrderDetailResponse(input: {
|
|
118
|
+
fulfillmentOrder: CommerceFulfillmentOrderRow;
|
|
119
|
+
lines: CommerceFulfillmentLineRow[];
|
|
120
|
+
latestOperation: CommerceFulfillmentOperationRow | null;
|
|
121
|
+
orderContext: CommerceFulfillmentOrderContextRow;
|
|
122
|
+
paymentIntent: CommerceFulfillmentPaymentIntentRow;
|
|
123
|
+
inventoryReservations: CommerceFulfillmentInventoryReservationRow[];
|
|
124
|
+
shipmentRef: CommerceFulfillmentShipmentRefRow | null;
|
|
125
|
+
activeHoldCount: number;
|
|
126
|
+
subscriptionCycleStatus: SubscriptionCycleStatus | null;
|
|
127
|
+
}, policy: FulfillmentCommerceReadPolicy): AdminCommerceFulfillmentOrderDetailResponse {
|
|
128
|
+
return adminCommerceFulfillmentOrderDetailResponseSchema.parse({
|
|
129
|
+
contractVersion: COMMERCE_FULFILLMENT_CONTRACT_VERSION,
|
|
130
|
+
order: buildCommerceFulfillmentOrder({
|
|
131
|
+
fulfillmentOrder: input.fulfillmentOrder,
|
|
132
|
+
lines: input.lines,
|
|
133
|
+
latestOperation: input.latestOperation,
|
|
134
|
+
orderContext: input.orderContext,
|
|
135
|
+
paymentIntent: input.paymentIntent,
|
|
136
|
+
inventoryReservations: input.inventoryReservations,
|
|
137
|
+
shipmentRef: input.shipmentRef,
|
|
138
|
+
activeHoldCount: input.activeHoldCount,
|
|
139
|
+
policy,
|
|
140
|
+
subscriptionCycleStatuses: {
|
|
141
|
+
[input.orderContext.subscription_cycle_id ?? ""]: input.subscriptionCycleStatus,
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildCommerceFulfillmentOrder(input: {
|
|
148
|
+
fulfillmentOrder: CommerceFulfillmentOrderRow;
|
|
149
|
+
lines: CommerceFulfillmentLineRow[];
|
|
150
|
+
latestOperation: CommerceFulfillmentOperationRow | null;
|
|
151
|
+
orderContext: CommerceFulfillmentOrderContextRow;
|
|
152
|
+
paymentIntent: CommerceFulfillmentPaymentIntentRow;
|
|
153
|
+
inventoryReservations: CommerceFulfillmentInventoryReservationRow[];
|
|
154
|
+
shipmentRef: CommerceFulfillmentShipmentRefRow | null;
|
|
155
|
+
activeHoldCount: number;
|
|
156
|
+
subscriptionCycleStatuses: Record<string, SubscriptionCycleStatus | null>;
|
|
157
|
+
policy: FulfillmentCommerceReadPolicy;
|
|
158
|
+
}): CommerceFulfillmentOrder {
|
|
159
|
+
const paymentStatus = input.paymentIntent.status;
|
|
160
|
+
const inventory = inventorySummary(input.inventoryReservations);
|
|
161
|
+
return {
|
|
162
|
+
id: input.fulfillmentOrder.id,
|
|
163
|
+
orderId: input.fulfillmentOrder.order_id,
|
|
164
|
+
clientId: input.fulfillmentOrder.client_id,
|
|
165
|
+
status: input.fulfillmentOrder.status,
|
|
166
|
+
providerKind: input.fulfillmentOrder.provider_kind,
|
|
167
|
+
providerTrackingId: input.shipmentRef?.provider_tracking_id ?? null,
|
|
168
|
+
shippingAddress: normalizeShippingAddressSnapshot(input.fulfillmentOrder.shipping_address_snapshot),
|
|
169
|
+
lines: input.lines.map((line) => ({
|
|
170
|
+
id: line.id,
|
|
171
|
+
orderItemId: line.order_item_id,
|
|
172
|
+
skuId: line.sku_id,
|
|
173
|
+
sku: line.sku,
|
|
174
|
+
title: line.title,
|
|
175
|
+
quantity: line.quantity,
|
|
176
|
+
inventoryReservationIds: line.inventory_reservation_ids,
|
|
177
|
+
productSnapshot: line.product_snapshot,
|
|
178
|
+
})),
|
|
179
|
+
payment: {
|
|
180
|
+
paymentIntentId: input.paymentIntent.id,
|
|
181
|
+
paymentStatus,
|
|
182
|
+
providerPaymentId: input.paymentIntent.provider_payment_id,
|
|
183
|
+
},
|
|
184
|
+
inventory,
|
|
185
|
+
omsEligibility: input.policy.evaluateCommerceFulfillmentEligibility({
|
|
186
|
+
orderMode: input.orderContext.mode,
|
|
187
|
+
orderStatus: input.orderContext.status,
|
|
188
|
+
paymentStatus,
|
|
189
|
+
subscriptionCycleStatus: input.orderContext.subscription_cycle_id
|
|
190
|
+
? input.subscriptionCycleStatuses[input.orderContext.subscription_cycle_id] ?? null
|
|
191
|
+
: null,
|
|
192
|
+
activeHoldCount: input.activeHoldCount,
|
|
193
|
+
hasShippingAddress: Boolean(input.orderContext.shipping_address_id),
|
|
194
|
+
inventoryStatus: inventory.status,
|
|
195
|
+
}),
|
|
196
|
+
latestOperation: input.latestOperation
|
|
197
|
+
? {
|
|
198
|
+
id: input.latestOperation.id,
|
|
199
|
+
type: input.latestOperation.operation_type,
|
|
200
|
+
occurredAt: input.latestOperation.occurred_at,
|
|
201
|
+
actorUserId: input.latestOperation.actor_user_id,
|
|
202
|
+
payload: input.latestOperation.payload,
|
|
203
|
+
}
|
|
204
|
+
: null,
|
|
205
|
+
createdAt: input.fulfillmentOrder.created_at,
|
|
206
|
+
updatedAt: input.fulfillmentOrder.updated_at,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function normalizeShippingAddressSnapshot(
|
|
211
|
+
snapshot: CommerceFulfillmentOrderRow["shipping_address_snapshot"],
|
|
212
|
+
): CommerceFulfillmentOrder["shippingAddress"] {
|
|
213
|
+
return {
|
|
214
|
+
addressId: snapshot.addressId,
|
|
215
|
+
clientId: snapshot.clientId,
|
|
216
|
+
label: snapshot.label,
|
|
217
|
+
line1: snapshot.line1,
|
|
218
|
+
line2: snapshot.line2,
|
|
219
|
+
city: snapshot.city,
|
|
220
|
+
postalCode: snapshot.postalCode,
|
|
221
|
+
country: snapshot.country,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function inventorySummary(
|
|
226
|
+
reservations: CommerceFulfillmentInventoryReservationRow[],
|
|
227
|
+
): CommerceFulfillmentOrder["inventory"] {
|
|
228
|
+
const reservation = reservations[0] ?? null;
|
|
229
|
+
if (!reservation) {
|
|
230
|
+
return {
|
|
231
|
+
status: "missing",
|
|
232
|
+
reservationId: null,
|
|
233
|
+
reservationStatus: null,
|
|
234
|
+
expiresAt: null,
|
|
235
|
+
locationId: null,
|
|
236
|
+
locationCode: null,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
status: reservation.status === "reserved" ? "reserved" : reservation.status,
|
|
241
|
+
reservationId: reservation.id,
|
|
242
|
+
reservationStatus: reservation.status,
|
|
243
|
+
expiresAt: reservation.expires_at,
|
|
244
|
+
locationId: reservation.location_id,
|
|
245
|
+
locationCode: reservation.inventory_locations?.code ?? null,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function findRequired<T extends { order_id?: string; id?: string }>(
|
|
250
|
+
rows: T[],
|
|
251
|
+
orderId: string,
|
|
252
|
+
label: string,
|
|
253
|
+
): T {
|
|
254
|
+
const row = rows.find((candidate) => candidate.order_id === orderId || candidate.id === orderId);
|
|
255
|
+
if (!row) throw new Error(`Missing commerce fulfillment ${label} for order ${orderId}`);
|
|
256
|
+
return row;
|
|
257
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Late delivery extends the next cycle, never stacks deliveries or moves it earlier.
|
|
3
|
+
* A replacement may settle alignment as aligned only after reaching the customer.
|
|
4
|
+
* Its superseded predecessor is no longer outstanding; an in-flight or undelivered
|
|
5
|
+
* replacement still is. Never call subscription_delivery_alignment_confirm_replacement
|
|
6
|
+
* for a replacement that did not arrive: it records "Parcel delivered" and shifts
|
|
7
|
+
* the cycle later. That note-only operator attestation remains outside this port;
|
|
8
|
+
* no automatic caller or new guard is introduced.
|
|
9
|
+
*/
|
|
10
|
+
export interface DeliveryAlignmentPort {
|
|
11
|
+
recordArrival(input: { requestKey: string; parcelRef: string; deliveredAt: string; evidenceRef: `evidence:${string}` }): Promise<{ disposition: "aligned" | "outstanding" | "manual_review"; nextCycleAt: string | null; replayed: boolean }>;
|
|
12
|
+
read(fulfillmentRef: string): Promise<{ disposition: "aligned" | "outstanding" | "manual_review"; nextCycleAt: string | null }>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FulfillmentCapabilityDescriptor } from './capabilities.js';
|
|
2
|
+
import type { FulfillmentDispatchPort, FulfillmentDispatchRequest, FulfillmentRefusal, FulfillmentSelection } from './dispatch.js';
|
|
3
|
+
import type { FulfillmentNativeUnsupported } from './nativeUnsupported.js';
|
|
4
|
+
import type { DispatchRecord, FulfillmentStorePort } from './store.js';
|
|
5
|
+
|
|
6
|
+
/** In-memory ports and host closures only; never serialize options or recipient inputs. */
|
|
7
|
+
export interface FulfillmentServicesOptions {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
selection: FulfillmentSelection | null;
|
|
10
|
+
capability: FulfillmentCapabilityDescriptor | null;
|
|
11
|
+
store: FulfillmentStorePort | null;
|
|
12
|
+
provider: FulfillmentDispatchPort | null;
|
|
13
|
+
readiness(): Promise<{ ready: true } | { ready: false; refusal: FulfillmentRefusal | FulfillmentNativeUnsupported }>;
|
|
14
|
+
listCandidates(limit: number): Promise<readonly FulfillmentDispatchRequest[]>;
|
|
15
|
+
keys: {
|
|
16
|
+
request(candidate: FulfillmentDispatchRequest): string;
|
|
17
|
+
mappedAttempt(requestKey: string, mappedAttemptIndex: number): string;
|
|
18
|
+
acceptance(fulfillmentRef: string): { providerAttemptKey: string; labelKey: string };
|
|
19
|
+
};
|
|
20
|
+
config: { mode: DispatchRecord['mode']; batchLimit: number };
|
|
21
|
+
now?: () => Date;
|
|
22
|
+
}
|
|
23
|
+
/** Import and construction are inert. Every invocation checks activation before any worker. */
|
|
24
|
+
export interface FulfillmentServices {
|
|
25
|
+
readiness(): ReturnType<FulfillmentServicesOptions['readiness']>;
|
|
26
|
+
runDispatch(): Promise<FulfillmentNativeUnsupported | { kind: 'refused'; refusal: FulfillmentRefusal } | {
|
|
27
|
+
ok: boolean; skipped: boolean; checked: number; updated: number; failures: number;
|
|
28
|
+
mode: DispatchRecord['mode']; replayed: number; providerCalls: number; readBacks: number; reason: string | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FulfillmentRefusal } from "./dispatch.js";
|
|
2
|
+
export interface FulfillmentCapabilityDescriptor {
|
|
3
|
+
providerRef: string;
|
|
4
|
+
role: "warehouse" | "direct_carrier" | "manual" | "simulator";
|
|
5
|
+
operations: Readonly<Record<"dispatch" | "readback" | "exactAbsence" | "verifyObservation" | "syncStock" | "syncProducts" | "labelCreate" | "labelRead" | "labelVoid" | "cancel" | "pickupPoint" | "courierPickup", { supported: true } | { supported: false; reason: "not_exposed" | "host_only" | "not_implemented" }>>;
|
|
6
|
+
authentication: "token" | "authenticated_timestamp" | "none";
|
|
7
|
+
freshness: "authenticated_timestamp" | "not_available";
|
|
8
|
+
defaultOff: true;
|
|
9
|
+
selected: boolean;
|
|
10
|
+
readiness: { ready: true } | { ready: false; refusal: FulfillmentRefusal };
|
|
11
|
+
ownsDeliveredNotification: boolean;
|
|
12
|
+
stockAuthority: "local_atp" | "external_stock_master_with_local_reservations";
|
|
13
|
+
labelReadOwner: "host_storage" | "adapter" | "none";
|
|
14
|
+
actionPolicy: { providerSpecificAdminActions: boolean; genericAdminActions: boolean; mutationSurface: "none" | "local_only" | "provider_adapter" };
|
|
15
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { FulfillmentCapabilityDescriptor } from "./capabilities.js";
|
|
2
|
+
import type { FulfillmentDispatchPort, FulfillmentDispatchRequest } from "./dispatch.js";
|
|
3
|
+
import type { FulfillmentLabelPort } from "./label.js";
|
|
4
|
+
import type { FulfillmentObservationPort, FulfillmentObservationCodecs } from "./observation.js";
|
|
5
|
+
import type { FulfillmentReadbackPort } from "./readback.js";
|
|
6
|
+
import type { FulfillmentStockSyncProvider, FulfillmentProductSyncProvider } from "./sync.js";
|
|
7
|
+
import type { FulfillmentStorePort } from "./store.js";
|
|
8
|
+
|
|
9
|
+
type Fixture<Method extends (...args: never[]) => unknown> = {
|
|
10
|
+
input: Parameters<Method>; expected: Awaited<ReturnType<Method>>;
|
|
11
|
+
};
|
|
12
|
+
type SyncFixture<Port> = { [K in keyof Port]: Port[K] extends (...args: never[]) => unknown
|
|
13
|
+
? { operation: K; input: Parameters<Port[K]>; expected: Awaited<ReturnType<Port[K]>> | { kind: "refused"; refusal: { reason: "unsupported"; phase: "sync"; retryable: false } } } : never }[keyof Port];
|
|
14
|
+
|
|
15
|
+
/** Offline adapter-owned inputs and exact expected outputs; never real customer data. */
|
|
16
|
+
export interface FulfillmentAdapterConformanceSubject {
|
|
17
|
+
approveTrackingUrl?(url: string): boolean;
|
|
18
|
+
observationCodecs?: FulfillmentObservationCodecs;
|
|
19
|
+
subjectId: string;
|
|
20
|
+
adapter: {
|
|
21
|
+
capabilities: FulfillmentCapabilityDescriptor; dispatch: FulfillmentDispatchPort;
|
|
22
|
+
label?: FulfillmentLabelPort; observation?: FulfillmentObservationPort; readback?: FulfillmentReadbackPort;
|
|
23
|
+
stockSync?: FulfillmentStockSyncProvider; productSync?: FulfillmentProductSyncProvider;
|
|
24
|
+
};
|
|
25
|
+
/** Count external submission I/O, including attempted calls. Preparation/refusals must not increase it. */
|
|
26
|
+
readIoCount(): number;
|
|
27
|
+
sensitiveValues: readonly string[];
|
|
28
|
+
fixtures: {
|
|
29
|
+
dispatch: readonly {
|
|
30
|
+
input: FulfillmentDispatchRequest;
|
|
31
|
+
expectedPrepare: Awaited<ReturnType<FulfillmentDispatchPort["prepare"]>>;
|
|
32
|
+
expectedDispatch?: Awaited<ReturnType<FulfillmentDispatchPort["dispatch"]>>;
|
|
33
|
+
/** Repeating this operation must not increase external submission I/O. */
|
|
34
|
+
replay?: boolean;
|
|
35
|
+
}[];
|
|
36
|
+
label?: { create?: readonly Fixture<FulfillmentLabelPort["create"]>[]; read?: readonly Fixture<FulfillmentLabelPort["read"]>[]; void?: readonly Fixture<FulfillmentLabelPort["void"]>[] };
|
|
37
|
+
observation?: readonly Fixture<FulfillmentObservationPort["verifyAndNormalize"]>[];
|
|
38
|
+
readback?: readonly (Fixture<FulfillmentReadbackPort["read"]> & { localMissing?: boolean })[];
|
|
39
|
+
stockSync?: readonly SyncFixture<FulfillmentStockSyncProvider>[];
|
|
40
|
+
productSync?: readonly SyncFixture<FulfillmentProductSyncProvider>[];
|
|
41
|
+
};
|
|
42
|
+
/** A fresh isolated host store per call; no database implementation is supplied by the runner. */
|
|
43
|
+
store?: {
|
|
44
|
+
create(): Promise<FulfillmentStorePort & {
|
|
45
|
+
/** Offline fixture inspection, not a new production store operation. */
|
|
46
|
+
readMappedAttempts(): Promise<readonly {
|
|
47
|
+
attemptKey: string; fulfillmentRef: string;
|
|
48
|
+
preparedEvidence: Parameters<FulfillmentStorePort["recordProviderAttempt"]>[2];
|
|
49
|
+
mode: Parameters<FulfillmentStorePort["recordProviderAttempt"]>[3]; status: "recorded";
|
|
50
|
+
}[]>;
|
|
51
|
+
}>;
|
|
52
|
+
claim: Parameters<FulfillmentStorePort["claim"]>[0];
|
|
53
|
+
remappedClaim: Parameters<FulfillmentStorePort["claim"]>[0];
|
|
54
|
+
mappedAttemptKey: string; remappedAttemptKey: string;
|
|
55
|
+
request: FulfillmentDispatchRequest; remappedRequest: FulfillmentDispatchRequest;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Sanitized proof only: no fixture inputs, raw responses, transient URLs or credentials. */
|
|
60
|
+
export interface FulfillmentAdapterConformanceEvidence {
|
|
61
|
+
subjects: readonly { subjectId: string; providerRef: string }[];
|
|
62
|
+
cases: readonly { subjectId: string; operation: string; passed: true }[];
|
|
63
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FulfillmentNativeUnsupported } from './nativeUnsupported.js';
|
|
2
|
+
export interface CustomerDeliveryAlignment { state: 'protected' | 'aligned' }
|
|
3
|
+
/** Absence from the map means no current customer case, distinct from protected. */
|
|
4
|
+
export type FulfillmentCustomerAlignmentRowsReader = (subscriptionIds: string[]) => Promise<Record<string, unknown>[] | FulfillmentNativeUnsupported>;
|