@openlup/platform 0.20.0 → 0.22.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 +272 -278
- package/README.md +195 -6
- package/dist/composition/composeBundle.d.ts +13 -0
- package/dist/composition/composeBundle.js +4 -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 +2 -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 +93 -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 +105 -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 +52 -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 +38 -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 +86 -0
- package/dist/fulfillment/contracts/store.js +1 -0
- package/dist/fulfillment/contracts/sync.d.ts +170 -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 +7 -0
- package/dist/fulfillment/dispatchProjection.js +18 -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 +37 -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 +27 -0
- package/dist/fulfillment/migrations.js +8 -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 +11 -0
- package/dist/fulfillment/observationProjection.js +37 -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 +37 -0
- package/dist/fulfillment/omnipack/client.js +162 -0
- package/dist/fulfillment/omnipack/index.d.ts +52 -0
- package/dist/fulfillment/omnipack/index.js +349 -0
- package/dist/fulfillment/omnipack/mappers.d.ts +73 -0
- package/dist/fulfillment/omnipack/mappers.js +177 -0
- package/dist/fulfillment/omnipack/observation.d.ts +21 -0
- package/dist/fulfillment/omnipack/observation.js +57 -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/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 +63 -0
- package/dist/fulfillment/postgresShipmentSpine.js +232 -0
- package/dist/fulfillment/postgresStockSyncPort.d.ts +8 -0
- package/dist/fulfillment/postgresStockSyncPort.js +63 -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 +26 -0
- package/dist/fulfillment/providerDispatchAcceptance.js +23 -0
- package/dist/fulfillment/providerDispatchError.d.ts +6 -0
- package/dist/fulfillment/providerDispatchError.js +5 -0
- package/dist/fulfillment/providerDispatchWorker.d.ts +31 -0
- package/dist/fulfillment/providerDispatchWorker.js +172 -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 +82 -0
- package/dist/fulfillment/providerReconciliationWorker.js +151 -0
- package/dist/fulfillment/providerStockAvailability.d.ts +13 -0
- package/dist/fulfillment/providerStockAvailability.js +51 -0
- package/dist/fulfillment/providerStockSyncWorker.d.ts +37 -0
- package/dist/fulfillment/providerStockSyncWorker.js +186 -0
- package/dist/fulfillment/providerTrackingReferences.d.ts +15 -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 +52 -0
- package/dist/fulfillment/verifiedIngress.js +66 -0
- package/dist/payments/adapterConformance.d.ts +8 -0
- package/dist/payments/adapterConformance.js +109 -0
- package/dist/payments/bindings.d.ts +3 -0
- package/dist/payments/bindings.js +17 -0
- package/dist/payments/checkoutActivePaymentActionResolver.d.ts +7 -0
- package/dist/payments/checkoutActivePaymentActionResolver.js +77 -0
- package/dist/payments/checkoutRecoveryPaymentResolver.d.ts +8 -0
- package/dist/payments/checkoutRecoveryPaymentResolver.js +87 -0
- package/dist/payments/contracts/adapter.d.ts +113 -0
- package/dist/payments/contracts/adapter.js +1 -0
- package/dist/payments/contracts/conformance.d.ts +155 -0
- package/dist/payments/contracts/conformance.js +1 -0
- package/dist/payments/contracts/evidence.d.ts +157 -0
- package/dist/payments/contracts/evidence.js +1 -0
- package/dist/payments/contracts/index.d.ts +10 -0
- package/dist/payments/contracts/index.js +1 -0
- package/dist/payments/contracts/preparedAttempt.d.ts +69 -0
- package/dist/payments/contracts/preparedAttempt.js +1 -0
- package/dist/payments/contracts/reconciliation.d.ts +303 -0
- package/dist/payments/contracts/reconciliation.js +1 -0
- package/dist/payments/contracts/repair.d.ts +92 -0
- package/dist/payments/contracts/repair.js +1 -0
- package/dist/payments/contracts/truth.d.ts +92 -0
- package/dist/payments/contracts/truth.js +1 -0
- package/dist/payments/eventConformanceRunner.d.ts +12 -0
- package/dist/payments/eventConformanceRunner.js +125 -0
- package/dist/payments/index.d.ts +16 -0
- package/dist/payments/index.js +16 -0
- package/dist/payments/paymentAttemptSilenceWindow.d.ts +10 -0
- package/dist/payments/paymentAttemptSilenceWindow.js +19 -0
- package/dist/payments/paymentMethodLifecycle.d.ts +11 -0
- package/dist/payments/paymentMethodLifecycle.js +37 -0
- package/dist/payments/paymentProviderReconciliationAbsenceReopen.d.ts +7 -0
- package/dist/payments/paymentProviderReconciliationAbsenceReopen.js +100 -0
- package/dist/payments/paymentProviderReconciliationEvidence.d.ts +36 -0
- package/dist/payments/paymentProviderReconciliationEvidence.js +91 -0
- package/dist/payments/paymentProviderReconciliationWorker.d.ts +4 -0
- package/dist/payments/paymentProviderReconciliationWorker.js +245 -0
- package/dist/payments/paymentTruth.d.ts +8 -0
- package/dist/payments/paymentTruth.js +125 -0
- package/dist/payments/paymentVerifyNowHandler.d.ts +27 -0
- package/dist/payments/paymentVerifyNowHandler.js +70 -0
- package/dist/payments/paymentVerifyNowService.d.ts +23 -0
- package/dist/payments/paymentVerifyNowService.js +93 -0
- package/dist/payments/paymentWebhookPostProcessing.d.ts +22 -0
- package/dist/payments/paymentWebhookPostProcessing.js +15 -0
- package/dist/payments/postgresPaymentTruth.d.ts +10 -0
- package/dist/payments/postgresPaymentTruth.js +126 -0
- package/dist/payments/preparedAttempt.d.ts +6 -0
- package/dist/payments/preparedAttempt.js +70 -0
- package/dist/payments/preparedAttemptFinalization.d.ts +5 -0
- package/dist/payments/preparedAttemptFinalization.js +12 -0
- package/dist/payments/schemaCapabilities.d.ts +10 -0
- package/dist/payments/schemaCapabilities.js +10 -0
- package/dist/payments/sql.d.ts +20 -0
- package/dist/payments/sql.js +6 -0
- package/dist/payments/stripe/classifyDecline.d.ts +46 -0
- package/dist/payments/stripe/classifyDecline.js +58 -0
- package/dist/payments/stripe/declineFailureHints.d.ts +46 -0
- package/dist/payments/stripe/declineFailureHints.js +87 -0
- package/dist/payments/stripe/donorContracts.d.ts +120 -0
- package/dist/payments/stripe/donorContracts.js +1 -0
- package/dist/payments/stripe/executionDecline.d.ts +53 -0
- package/dist/payments/stripe/executionDecline.js +107 -0
- package/dist/payments/stripe/index.d.ts +41 -0
- package/dist/payments/stripe/index.js +264 -0
- package/dist/payments/stripe/intentMapper.d.ts +26 -0
- package/dist/payments/stripe/intentMapper.js +51 -0
- package/dist/payments/stripe/methodFacts.d.ts +10 -0
- package/dist/payments/stripe/methodFacts.js +12 -0
- package/dist/payments/stripe/paymentFailureEvidence.d.ts +26 -0
- package/dist/payments/stripe/paymentFailureEvidence.js +33 -0
- package/dist/payments/stripe/stripeFailureEvidence.d.ts +41 -0
- package/dist/payments/stripe/stripeFailureEvidence.js +82 -0
- package/dist/payments/stripe/stripeIntentConfirmationEvidence.d.ts +28 -0
- package/dist/payments/stripe/stripeIntentConfirmationEvidence.js +32 -0
- package/dist/payments/stripe/stripePaymentReconciliationProvider.d.ts +35 -0
- package/dist/payments/stripe/stripePaymentReconciliationProvider.js +222 -0
- package/dist/payments/stripe/stripeRefundEvidence.d.ts +8 -0
- package/dist/payments/stripe/stripeRefundEvidence.js +17 -0
- package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.d.ts +45 -0
- package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.js +220 -0
- package/dist/payments/stripe/stripeWebhookNormalizer.d.ts +14 -0
- package/dist/payments/stripe/stripeWebhookNormalizer.js +243 -0
- package/dist/payments/stripe/webhookSignature.d.ts +14 -0
- package/dist/payments/stripe/webhookSignature.js +43 -0
- package/dist/payments/tpay/blikRecurringMapper.d.ts +37 -0
- package/dist/payments/tpay/blikRecurringMapper.js +24 -0
- package/dist/payments/tpay/capabilities.d.ts +3 -0
- package/dist/payments/tpay/capabilities.js +18 -0
- package/dist/payments/tpay/classifyDecline.d.ts +46 -0
- package/dist/payments/tpay/classifyDecline.js +58 -0
- package/dist/payments/tpay/declineFailureHints.d.ts +72 -0
- package/dist/payments/tpay/declineFailureHints.js +37 -0
- package/dist/payments/tpay/donorContracts.d.ts +140 -0
- package/dist/payments/tpay/donorContracts.js +1 -0
- package/dist/payments/tpay/index.d.ts +73 -0
- package/dist/payments/tpay/index.js +294 -0
- package/dist/payments/tpay/paymentFailureEvidence.d.ts +26 -0
- package/dist/payments/tpay/paymentFailureEvidence.js +33 -0
- package/dist/payments/tpay/preparedProviderAttempt.d.ts +22 -0
- package/dist/payments/tpay/preparedProviderAttempt.js +24 -0
- package/dist/payments/tpay/providerAttemptFailureDiagnostic.d.ts +3 -0
- package/dist/payments/tpay/providerAttemptFailureDiagnostic.js +95 -0
- package/dist/payments/tpay/tpayCreateTransactionInput.d.ts +25 -0
- package/dist/payments/tpay/tpayCreateTransactionInput.js +128 -0
- package/dist/payments/tpay/tpayDeclineEvidence.d.ts +126 -0
- package/dist/payments/tpay/tpayDeclineEvidence.js +177 -0
- package/dist/payments/tpay/tpayDispatchFailure.d.ts +60 -0
- package/dist/payments/tpay/tpayDispatchFailure.js +121 -0
- package/dist/payments/tpay/tpayFailureEvidence.d.ts +20 -0
- package/dist/payments/tpay/tpayFailureEvidence.js +61 -0
- package/dist/payments/tpay/tpayHttpClient.d.ts +52 -0
- package/dist/payments/tpay/tpayHttpClient.js +185 -0
- package/dist/payments/tpay/tpayHttpFailure.d.ts +57 -0
- package/dist/payments/tpay/tpayHttpFailure.js +143 -0
- package/dist/payments/tpay/tpayPaymentExecutionAdapter.d.ts +20 -0
- package/dist/payments/tpay/tpayPaymentExecutionAdapter.js +249 -0
- package/dist/payments/tpay/tpayPaymentReconciliationProvider.d.ts +5 -0
- package/dist/payments/tpay/tpayPaymentReconciliationProvider.js +244 -0
- package/dist/payments/tpay/tpayTransactionPayload.d.ts +120 -0
- package/dist/payments/tpay/tpayTransactionPayload.js +96 -0
- package/dist/payments/tpay/wallClockTime.d.ts +31 -0
- package/dist/payments/tpay/wallClockTime.js +57 -0
- package/dist/payments/tpay/webhookJwsVerifier.d.ts +17 -0
- package/dist/payments/tpay/webhookJwsVerifier.js +51 -0
- package/dist/payments/tpay/webhookNormalizer.d.ts +54 -0
- package/dist/payments/tpay/webhookNormalizer.js +200 -0
- package/dist/payments/verifiedEvidenceProjection.d.ts +12 -0
- package/dist/payments/verifiedEvidenceProjection.js +166 -0
- package/dist/payments/verifiedIngress.d.ts +119 -0
- package/dist/payments/verifiedIngress.js +220 -0
- package/package.json +37 -2
- package/release-gates.json +105 -3
- package/sql/20260808210000_fulfillment_shipment_rail.sql +810 -0
- package/sql/20260810180000_payment_settlement_rail.sql +574 -0
- package/sql/20260815150000_payment_truth_reconciliation.sql +479 -0
- package/sql/20260819150000_fulfillment_shipment_sequence.sql +98 -0
- package/sql/20260826100000_late_payment_shifts_the_subscription_cycle.sql +215 -0
- package/sql/20261022080000_fulfillment_command_ledger.sql +786 -0
- package/sql/20261022090000_fulfillment_delivery_alignment.sql +1106 -0
- package/src/composition/composeBundle.ts +13 -0
- package/src/composition/fulfillmentContribution.ts +28 -0
- package/src/composition/index.ts +2 -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 +61 -0
- package/src/fulfillment/contracts/customerAlignment.ts +4 -0
- package/src/fulfillment/contracts/dispatch.ts +58 -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 +18 -0
- package/src/fulfillment/contracts/operator.ts +31 -0
- package/src/fulfillment/contracts/read.ts +42 -0
- package/src/fulfillment/contracts/readback.ts +11 -0
- package/src/fulfillment/contracts/replacement.ts +15 -0
- package/src/fulfillment/contracts/store.ts +26 -0
- package/src/fulfillment/contracts/sync.ts +61 -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 +20 -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 +38 -0
- package/src/fulfillment/labelLifecycle.ts +11 -0
- package/src/fulfillment/lowStockEvidenceHandler.ts +60 -0
- package/src/fulfillment/migrations.ts +8 -0
- package/src/fulfillment/nativeUnsupported.ts +23 -0
- package/src/fulfillment/normalizedFulfillmentFacts.ts +2 -0
- package/src/fulfillment/observationProjection.ts +41 -0
- package/src/fulfillment/omnipack/capabilities.ts +18 -0
- package/src/fulfillment/omnipack/client.ts +258 -0
- package/src/fulfillment/omnipack/index.ts +312 -0
- package/src/fulfillment/omnipack/mappers.ts +261 -0
- package/src/fulfillment/omnipack/observation.ts +61 -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/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 +321 -0
- package/src/fulfillment/postgresStockSyncPort.ts +65 -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 +31 -0
- package/src/fulfillment/providerDispatchError.ts +7 -0
- package/src/fulfillment/providerDispatchWorker.ts +131 -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 +150 -0
- package/src/fulfillment/providerStockAvailability.ts +60 -0
- package/src/fulfillment/providerStockSyncWorker.ts +222 -0
- package/src/fulfillment/providerTrackingReferences.ts +31 -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 +80 -0
- package/src/payments/adapterConformance.ts +118 -0
- package/src/payments/bindings.ts +17 -0
- package/src/payments/checkoutActivePaymentActionResolver.ts +97 -0
- package/src/payments/checkoutRecoveryPaymentResolver.ts +108 -0
- package/src/payments/contracts/adapter.ts +96 -0
- package/src/payments/contracts/conformance.ts +171 -0
- package/src/payments/contracts/evidence.ts +142 -0
- package/src/payments/contracts/index.ts +54 -0
- package/src/payments/contracts/preparedAttempt.ts +86 -0
- package/src/payments/contracts/reconciliation.ts +296 -0
- package/src/payments/contracts/repair.ts +95 -0
- package/src/payments/contracts/truth.ts +108 -0
- package/src/payments/eventConformanceRunner.ts +149 -0
- package/src/payments/index.ts +21 -0
- package/src/payments/paymentAttemptSilenceWindow.ts +26 -0
- package/src/payments/paymentMethodLifecycle.ts +50 -0
- package/src/payments/paymentProviderReconciliationAbsenceReopen.ts +130 -0
- package/src/payments/paymentProviderReconciliationEvidence.ts +117 -0
- package/src/payments/paymentProviderReconciliationWorker.ts +291 -0
- package/src/payments/paymentTruth.ts +131 -0
- package/src/payments/paymentVerifyNowHandler.ts +94 -0
- package/src/payments/paymentVerifyNowService.ts +125 -0
- package/src/payments/paymentWebhookPostProcessing.ts +36 -0
- package/src/payments/postgresPaymentTruth.ts +149 -0
- package/src/payments/preparedAttempt.ts +90 -0
- package/src/payments/preparedAttemptFinalization.ts +13 -0
- package/src/payments/schemaCapabilities.ts +10 -0
- package/src/payments/sql.ts +6 -0
- package/src/payments/stripe/classifyDecline.ts +62 -0
- package/src/payments/stripe/declineFailureHints.ts +89 -0
- package/src/payments/stripe/donorContracts.ts +87 -0
- package/src/payments/stripe/executionDecline.ts +126 -0
- package/src/payments/stripe/index.ts +265 -0
- package/src/payments/stripe/intentMapper.ts +58 -0
- package/src/payments/stripe/methodFacts.ts +21 -0
- package/src/payments/stripe/paymentFailureEvidence.ts +58 -0
- package/src/payments/stripe/stripeFailureEvidence.ts +115 -0
- package/src/payments/stripe/stripeIntentConfirmationEvidence.ts +34 -0
- package/src/payments/stripe/stripePaymentReconciliationProvider.ts +263 -0
- package/src/payments/stripe/stripeRefundEvidence.ts +19 -0
- package/src/payments/stripe/stripeSandboxPaymentExecutionAdapter.ts +283 -0
- package/src/payments/stripe/stripeWebhookNormalizer.ts +277 -0
- package/src/payments/stripe/webhookSignature.ts +55 -0
- package/src/payments/tpay/blikRecurringMapper.ts +42 -0
- package/src/payments/tpay/capabilities.ts +21 -0
- package/src/payments/tpay/classifyDecline.ts +62 -0
- package/src/payments/tpay/declineFailureHints.ts +90 -0
- package/src/payments/tpay/donorContracts.ts +104 -0
- package/src/payments/tpay/index.ts +293 -0
- package/src/payments/tpay/paymentFailureEvidence.ts +58 -0
- package/src/payments/tpay/preparedProviderAttempt.ts +38 -0
- package/src/payments/tpay/providerAttemptFailureDiagnostic.ts +99 -0
- package/src/payments/tpay/tpayCreateTransactionInput.ts +158 -0
- package/src/payments/tpay/tpayDeclineEvidence.ts +233 -0
- package/src/payments/tpay/tpayDispatchFailure.ts +186 -0
- package/src/payments/tpay/tpayFailureEvidence.ts +75 -0
- package/src/payments/tpay/tpayHttpClient.ts +290 -0
- package/src/payments/tpay/tpayHttpFailure.ts +186 -0
- package/src/payments/tpay/tpayPaymentExecutionAdapter.ts +297 -0
- package/src/payments/tpay/tpayPaymentReconciliationProvider.ts +285 -0
- package/src/payments/tpay/tpayTransactionPayload.ts +207 -0
- package/src/payments/tpay/wallClockTime.ts +57 -0
- package/src/payments/tpay/webhookJwsVerifier.ts +82 -0
- package/src/payments/tpay/webhookNormalizer.ts +279 -0
- package/src/payments/verifiedEvidenceProjection.ts +179 -0
- package/src/payments/verifiedIngress.ts +317 -0
package/README.md
CHANGED
|
@@ -4,18 +4,18 @@ Development preview. This package is part of the publishable set with core and o
|
|
|
4
4
|
|
|
5
5
|
It holds the runnable server substrate an OpenLup application composes: the Node HTTP runtime, BFF dispatch, scheduling and job leases, blob storage, data gateways, the node-postgres migration runner and bundle composition. Ports and their contracts stay in `@openlup/core/platform-runtime`.
|
|
6
6
|
|
|
7
|
-
For the prepared `0.
|
|
8
|
-
limitations](CHANGELOG.md#
|
|
9
|
-
M06 composition bindings. M06 remains incomplete: inventory and shipping entries
|
|
7
|
+
For the prepared `0.22.0` set, read the [adjacent-release migration and known
|
|
8
|
+
limitations](CHANGELOG.md#0220) before selecting the opt-in M05 parity seams or
|
|
9
|
+
M06 composition bindings, the M07 payment selectors, or M08 fulfillment services. Native delivery alignment stays off or shadow until a host composes its automatic-renewal runner with the G23 admission decorator. M07 payment truth is complete for this wave; its selectors are default-off. M06 remains incomplete: inventory and shipping entries
|
|
10
10
|
are development-preview surfaces, with managed-host proof classes pending in the
|
|
11
11
|
[M06 parity ledger](../../docs/platform/plans/oss-m06-stock-reservations-delivery.md#parity-pending-rows).
|
|
12
|
-
The size gate compares the prepared `0.
|
|
12
|
+
The size gate compares the prepared `0.22.0` set with the published `0.21.0` tarball;
|
|
13
13
|
preparation, offline adapter evidence and a passing size cap grant no publication
|
|
14
14
|
or provider-account certification.
|
|
15
15
|
|
|
16
16
|
## Package surface
|
|
17
17
|
|
|
18
|
-
`release-gates.json` declares kind `rail`: this foundation provides runtime infrastructure over core ports, matching the architecture's rail responsibility. Its
|
|
18
|
+
`release-gates.json` declares kind `rail`: this foundation provides runtime infrastructure over core ports, matching the architecture's rail responsibility. Its experimental entries each bind a declaration snapshot and package-owned smoke tests. The API checker reads this same inventory.
|
|
19
19
|
|
|
20
20
|
| Export | Contents |
|
|
21
21
|
| --- | --- |
|
|
@@ -32,6 +32,11 @@ or provider-account certification.
|
|
|
32
32
|
| `./identity/contracts` | Experimental capability: browser-safe customer auth port, value types and provider parser |
|
|
33
33
|
| `./communications` | Experimental existing public consent/status, newsletter sync, Auth email hook, transport contracts/capture, observation/polling orchestration, standard webhook verification and delivery receipts; host-selected rendering, egress, protocol mapping and control policy |
|
|
34
34
|
| `./communications/contracts` | Pure renderer, block builders, copy-injected commerce/subscription factories and email/newsletter/Auth/intake/receipt types; no Zod, Node or provider runtime imports |
|
|
35
|
+
| `./payments/stripe` | Opt-in Node payment example with an injected structural client, verified observations and purpose-sensitive readback; no SDK dependency or default activation. |
|
|
36
|
+
| `./payments/tpay` | Opt-in Node payment example with injected HTTP/certificate I/O, Model O aliases and verified observations; no default activation. |
|
|
37
|
+
| `./fulfillment/omnipack` | Opt-in Node fulfillment example with injected I/O; default-off. |
|
|
38
|
+
| `./payments` | Experimental prepared-attempt ordering with injected persistence, execution and failure policy; contracts at `./payments/contracts`. |
|
|
39
|
+
| `./fulfillment`, `./fulfillment/contracts` | [Experimental fulfillment services, contracts and status canon](../../docs/platform/ARCHITECTURE_AND_EXTENSIONS.md#fulfillment-contracts). |
|
|
35
40
|
| `./inventory` | Experimental schemas/ports, handlers, native stores, projection, injected forecast and SQL descriptor; `./inventory/contracts` DTOs. |
|
|
36
41
|
| `./catalog` | Experimental neutral catalog schemas, drafts, reads, media source packs and publication ports |
|
|
37
42
|
| `./catalog/contracts` | Dependency-free catalog DTOs, media resolver, authority and canonicalization helpers |
|
|
@@ -771,7 +776,7 @@ for both-bundle commands, the committed managed grants and installation limits.
|
|
|
771
776
|
|
|
772
777
|
## Communications import
|
|
773
778
|
|
|
774
|
-
For adoption from `0.16.0`, follow the [0.17.0 Migration guidance](CHANGELOG.md#0170)
|
|
779
|
+
For adoption from `0.16.0`, follow the [0.17.0 Migration guidance](CHANGELOG-HISTORY.md#0170) (source repository)
|
|
775
780
|
for the complete package set, canonical SQL installation and host-owned bindings.
|
|
776
781
|
|
|
777
782
|
M04 P1 relocates the existing public implementation without changing its reference
|
|
@@ -1052,3 +1057,187 @@ opt-in browser profile adds no npm entry or UI package export.
|
|
|
1052
1057
|
Source contributors record notable changes in package changelog fragments;
|
|
1053
1058
|
release preparation assembles the shipped CHANGELOG. Follow the
|
|
1054
1059
|
[public contribution guide](https://github.com/openlup/openlup/blob/main/CONTRIBUTING.md#development-preview-checks).
|
|
1060
|
+
|
|
1061
|
+
## Payments prepared attempts
|
|
1062
|
+
|
|
1063
|
+
`@openlup/platform/payments` exports `executePreparedAttemptMechanism`: validate before durable preparation, refuse prepared replay, dispatch once unless the host proves no charge, and retry one immutable local finalization. The host owns keys, fingerprints, nominal error trust and persistence. An unknown execution never authorizes redispatch. The reference facade retains its existing binding.
|
|
1064
|
+
|
|
1065
|
+
`./payments/contracts` is type-only: execution/dispatch, transient method handoff, capability overlay, verified money/method evidence, bounded readback/absence, neutral refusals, refund observations and multi-subject conformance fixtures. Verification uses receipt time for freshness; ingress starts processing at receipt time and reuses durable first receipt on redelivery. Event identity includes outcome class. Transient authorization and opaque handoffs never enter persisted diagnostics. Capability support is explicit; timeout/truncation is unknown, never exact absence. Refunds require explicit evidence by default; `legacy-assume-full` is an explicit host opt-in. See the [M07 ledger](../../docs/platform/plans/oss-m07-payments-money-truth.md).
|
|
1066
|
+
|
|
1067
|
+
The payments entry also exports verified ingress, canonical truth fingerprints, method lifecycle/refusal handoff, reconciliation/verify-now and three runner-independent conformance functions. A new internal boundary projects verified envelopes into the contributed processing carrier: original bytes reach verification unchanged, receipt time starts processing, and durable first receipt overrides redelivery time. Full-reversal chargebacks use the existing dispute path. Snapshot and delta observations preserve original payment money; missing observation, required money or identity refuses before terminal application. Missing money, amounts that are not safe positive integers, or blank currencies on succeeded, refunded or disputed adapter evidence use the existing amount-mismatch refusal before ingestion or terminal application; ingress returns the existing processing-failure response. Failed and pending evidence may still omit money. Hosts own durable stores and response transport. Conformance requires safe positive integer amounts and nonblank currencies on settling money and checks exact identity/money, capability/port agreement and transient-secret exclusion; its evidence contains no handoff. Real-store durability still requires a selected-store proof.
|
|
1068
|
+
|
|
1069
|
+
`createPostgresPaymentTruthPort` accepts the platform data executor structural
|
|
1070
|
+
shape and `PostgresPaymentTruthOptions` from the contracts entry. It preserves
|
|
1071
|
+
the native truth ingest, reconciliation, sweep and readback SQL. `PAYMENT_MIGRATIONS`
|
|
1072
|
+
projects canonical units 10/35/56 byte for byte, with path, position, SHA-256 and
|
|
1073
|
+
asset URL; install the complete catalogue, never those three files alone.
|
|
1074
|
+
`paymentSchemaDescriptor` admits direct truth, settlement and later-only cycle
|
|
1075
|
+
alignment, and explicitly refuses prepared durable storage, method setup,
|
|
1076
|
+
absence reopen and cumulative refund. Refunds are purely financial. Seven real
|
|
1077
|
+
PostgreSQL cases cover fresh/prefix installs, replay/conflict, unknown readback,
|
|
1078
|
+
lost acknowledgement, rollback, denied access, dunning/recovery, concurrency and
|
|
1079
|
+
later-only dates; this does not claim managed-store parity or host activation.
|
|
1080
|
+
|
|
1081
|
+
Attempt input adds optional method selection with opaque, persistable channelRef
|
|
1082
|
+
and transient payer email/name. Payer contact is dispatch-only: never persist,
|
|
1083
|
+
audit, log or diagnose it. Missing adapter-required input must refuse before
|
|
1084
|
+
dispatch with exact `notDispatched` evidence. Conformance scans the execution
|
|
1085
|
+
and complete projected stored payload for transient values. Nested normalized
|
|
1086
|
+
evidence copies only declared typed fields, including nested facts; unknown
|
|
1087
|
+
extra properties are excluded. Incomplete chargeback evidence remains stored as
|
|
1088
|
+
a refund and unapplied, a declared operator recovery limit: reclassification
|
|
1089
|
+
would apply the donor dispute path without checking completeness.
|
|
1090
|
+
|
|
1091
|
+
`createCheckoutActivePaymentActionResolver` authorizes the exact owner, order,
|
|
1092
|
+
intent, attempt and execution rail, reads the provider once, then rechecks the
|
|
1093
|
+
complete canonical attempt. Hosts inject synchronous `railHandoffKinds` from
|
|
1094
|
+
adapter capture capabilities; an unknown rail supplies an empty list. Actions
|
|
1095
|
+
carry the provider address and the existing transient `PaymentMethodHandoff`.
|
|
1096
|
+
Pending evidence alone may expose a handoff; terminal, mismatched, unknown or
|
|
1097
|
+
raced evidence refuses it. `createCheckoutRecoveryPaymentResolver` preserves
|
|
1098
|
+
paid/retry/resume/wait/manual-review ordering without creating a new obligation.
|
|
1099
|
+
`createCommercePaymentVerifyHandler` validates the existing identity triple,
|
|
1100
|
+
checks stored ownership before readback, and uses the shared HTTP/responder
|
|
1101
|
+
contracts. A host may inject its responder to preserve request references.
|
|
1102
|
+
|
|
1103
|
+
`PaymentRepairMethodPort` collects a method only; it grants no token redemption,
|
|
1104
|
+
subscription change or payment success. Unsupported setup is explicit. Method
|
|
1105
|
+
evidence may carry neutral registration refs/kind/status. Ingress projects them
|
|
1106
|
+
only with `resolveMethodOwner` supplied by the host; adapter-supplied client or
|
|
1107
|
+
subscription fields never confer ownership. Without host ownership resolution,
|
|
1108
|
+
registration is not projected. Lifecycle carriers copy typed facts only.
|
|
1109
|
+
The existing refusal handoff accepts a failure-reason string, not a typed
|
|
1110
|
+
adapter refusal envelope; envelope forwarding remains unsupported.
|
|
1111
|
+
|
|
1112
|
+
### Payment service composition
|
|
1113
|
+
|
|
1114
|
+
`createPaymentServices` accepts `PaymentServicesOptions` and returns
|
|
1115
|
+
`PaymentServices` or null when truth schema/capability is unavailable. The host
|
|
1116
|
+
supplies `openTruth`, which opens one port and its asynchronous close operation.
|
|
1117
|
+
`run` closes that scope after success or failure. Construction performs no I/O.
|
|
1118
|
+
Use the existing `createBundleComposer` payments binder for named registration;
|
|
1119
|
+
omitting it leaves the host cold. Schema declarations do not prove installation:
|
|
1120
|
+
retain the existing readiness/schema checks before admitting work. Existing jobs
|
|
1121
|
+
and routes keep their owners; this binding adds no schedule or event handler.
|
|
1122
|
+
|
|
1123
|
+
The reference direct truth binding injects its existing executor, templates and
|
|
1124
|
+
recovery path. The separate verified-ingress selector is explicit and default-off;
|
|
1125
|
+
unknown selections refuse. Its projected carrier includes the adapter fingerprint,
|
|
1126
|
+
so the reference store records stable identity on the first receipt independently
|
|
1127
|
+
of audit payload or clock. On replay the store reads the first stored instant;
|
|
1128
|
+
application and follow-up work reuse it. Missing durable clock evidence refuses
|
|
1129
|
+
rather than advancing a cycle. This D11 behaviour change does not mount a provider
|
|
1130
|
+
route or enable an adapter; hosts must explicitly wire the selected ingress.
|
|
1131
|
+
|
|
1132
|
+
### Opt-in payment example
|
|
1133
|
+
|
|
1134
|
+
Import `createStripePaymentAdapter`, `type StripePaymentAdapterConfig` and
|
|
1135
|
+
`stripePaymentCapabilities` from `@openlup/platform/payments/stripe`. Supply a
|
|
1136
|
+
structural client, `webhookSigningSecrets`, explicit `mode`, required
|
|
1137
|
+
`supportedCurrencies` (a non-empty list of upper-case three-letter codes) and optionally
|
|
1138
|
+
`toleranceSeconds` (default 300) and `clock` (UTC seconds). The host constructs
|
|
1139
|
+
and configures the SDK, associates saved methods with customers and confirms
|
|
1140
|
+
off-session requests. Interactive methods follow the declared card/wallet offer.
|
|
1141
|
+
Importing creates no I/O or default binding. The adapter advertises the host
|
|
1142
|
+
currency list as a frozen copy; the frozen exported descriptor keeps an empty list.
|
|
1143
|
+
Account-specific readiness remains pending TEST verification. Exact absence,
|
|
1144
|
+
refund creation and split authorization/capture are unsupported. Cancellation
|
|
1145
|
+
is enabled only when the injected client supplies it. See the
|
|
1146
|
+
[A2 ledger](../../docs/platform/plans/oss-a2-payment-adapters.md) for the current
|
|
1147
|
+
preview boundary and the host's responsibilities.
|
|
1148
|
+
|
|
1149
|
+
Verified dispute events retain their distinct money outcome and original payment
|
|
1150
|
+
identity. Audit payloads carry only donor-filtered facts: the source event type,
|
|
1151
|
+
refund charge snapshot and failure-evidence provenance when present. They exclude
|
|
1152
|
+
raw bodies, client handoffs and customer authorization and do not affect event
|
|
1153
|
+
IDs or fingerprints. The offline adapter conformance witness uses two distinct
|
|
1154
|
+
intents and attempts with separate money and provider references.
|
|
1155
|
+
|
|
1156
|
+
Method evidence carries the donor lifecycle and replacement facts through M07
|
|
1157
|
+
projection when provider time is present. Without provider time it retains the
|
|
1158
|
+
method outcome and omits the lifecycle instead of inventing a transition time.
|
|
1159
|
+
|
|
1160
|
+
### Opt-in bank and code payment example
|
|
1161
|
+
|
|
1162
|
+
Import `createTpayPaymentAdapter`, `type TpayPaymentAdapterConfig` and
|
|
1163
|
+
`tpayPaymentCapabilities` from `@openlup/platform/payments/tpay`. Supply `http`
|
|
1164
|
+
with explicit `baseUrl`, `clientId`, `clientSecret`, `fetchImpl` and optional
|
|
1165
|
+
`timeoutMs`; `urls` with `notificationUrl`, `successUrl`, `errorUrl`;
|
|
1166
|
+
`modelOActivationEnabled`, `mode`, and optional UTC-seconds `clock`.
|
|
1167
|
+
`notification` requires `rootCertificatePem`, `allowedCertificateUrlPrefix`
|
|
1168
|
+
(HTTPS directory ending in `/`) and `resolveCertificate`. The resolver owns
|
|
1169
|
+
redirect/egress policy. Both certificates must be valid at verification time.
|
|
1170
|
+
|
|
1171
|
+
Verify certificate validity and notification freshness using original raw bytes.
|
|
1172
|
+
A signed protected-header `iat` supplies UTC-seconds provider time; absent time
|
|
1173
|
+
stays null. `maxNotificationAgeSeconds` defaults to seven days, a generous
|
|
1174
|
+
assumption pending confirmation of the provider retry schedule. Signed issue time
|
|
1175
|
+
more than `maxClockSkewSeconds` (default 300) after receipt refuses as stale;
|
|
1176
|
+
both freshness limits must be finite and nonnegative. Host dedupe
|
|
1177
|
+
uses exact event identity plus stable fingerprint when time is absent. The age
|
|
1178
|
+
and future-skew limits apply only when the signed header carries a provider issue time. Receipt
|
|
1179
|
+
time is used only for freshness. Transaction identity includes outcome class;
|
|
1180
|
+
method identity includes event/alias plus outcome.
|
|
1181
|
+
|
|
1182
|
+
Bank redirects, one-time codes and unattended Model O alias charges are supported
|
|
1183
|
+
when the host supplies the M07 P3 contract payer and method selection inputs.
|
|
1184
|
+
Supply transient `payer: { email, name? }` on every
|
|
1185
|
+
attempt, including renewals. An omitted or blank name uses the donor
|
|
1186
|
+
neutral display label `Customer`. Select `methodSelection: { family: "bank-redirect",
|
|
1187
|
+
channelRef }` for the bank channel, or `{ family: "one-time-code" }` with
|
|
1188
|
+
`customerAuthorization` as a `TransientSecret`. A `renewal-unattended` attempt
|
|
1189
|
+
uses `storedMethod.reference` and its Model O mandate. Missing payer/channel/code
|
|
1190
|
+
and Model M or unknown mandates refuse before any dispatch with neutral
|
|
1191
|
+
data-correction evidence. Payer and authorization never cross the return boundary
|
|
1192
|
+
in diagnostics or request/response payloads. Readback uses the returned API
|
|
1193
|
+
transaction ID; notification titles remain distinct. The donor's typed no-charge
|
|
1194
|
+
errors return an exact `notDispatched` envelope: proven request-document refusals
|
|
1195
|
+
require customer data correction; OAuth, encoding and exhausted pre-send deadlines
|
|
1196
|
+
remain transient retryable refusals. Host mode/purpose/currency mismatches throw before I/O.
|
|
1197
|
+
A renewal the provider refuses for data validation is classified as needing customer data correction: the host should prompt the customer to update payment details, and the bundled retry schedule keeps its normal retries unless the host treats that class as terminating. The subscription stays active; nothing is paused or suspended by this classification. The contributed adopter applies this classification only when reopening a checkout; the example applies it to every purpose.
|
|
1198
|
+
Every other execution error propagates unchanged as ambiguous.
|
|
1199
|
+
|
|
1200
|
+
The exported capability descriptor and its nested records and arrays are frozen.
|
|
1201
|
+
Currencyless money notifications preserve the paid amount and bind to the adapter's single declared currency; multiple declared currencies refuse as malformed.
|
|
1202
|
+
Alias method evidence preserves the donor lifecycle through M07 projection with
|
|
1203
|
+
signed provider time; absent provider time leaves the lifecycle field omitted.
|
|
1204
|
+
The adapter offers its declared market currency; alias registration and recurring
|
|
1205
|
+
cards are not offered by this factory. Account offers
|
|
1206
|
+
remain `verification_pending` until owner TEST readback. Chargeback observations carry full reversal,
|
|
1207
|
+
original money and stable operation identity; refunded/chargeback readback stays
|
|
1208
|
+
unknown for manual review, including historical attempt/error refusals in either
|
|
1209
|
+
readback purpose. Pending and refused readback report validated configured money,
|
|
1210
|
+
including when the paid amount is zero or absent; a refused result with mismatched
|
|
1211
|
+
nonzero paid money stays unknown for manual review. Succeeded readback requires the donor's
|
|
1212
|
+
observed paid amount to equal expected money; underpayment and overpayment remain
|
|
1213
|
+
unknown for manual review. There is no close or exact-absence port. Refund
|
|
1214
|
+
creation and authorization/capture separation are unsupported. See the
|
|
1215
|
+
[A2 ledger](../../docs/platform/plans/oss-a2-payment-adapters.md#o-p2-boundary-and-named-deltas).
|
|
1216
|
+
|
|
1217
|
+
Verified audit payloads retain only donor-filtered transaction and alias facts,
|
|
1218
|
+
including `eventKind`, alias `event` and `aliasType` when supplied. Missing failure
|
|
1219
|
+
provenance and failure prose stay absent. Payer, authorization, certificates and
|
|
1220
|
+
raw bodies never enter the evidence; audit facts do not affect event identity or
|
|
1221
|
+
fingerprints. The offline M07 P2 conformance witness uses two distinct intents,
|
|
1222
|
+
attempts, amounts and provider references, with replay and refusal boundaries.
|
|
1223
|
+
|
|
1224
|
+
### Reference-support surface
|
|
1225
|
+
|
|
1226
|
+
The vendor subpaths additionally expose these experimental helpers for the
|
|
1227
|
+
reference application's existing host bindings. Each declaration names its exact
|
|
1228
|
+
reference consumer and carries `@experimental`: Reference-application support
|
|
1229
|
+
surface; may change.
|
|
1230
|
+
|
|
1231
|
+
| Entry | Additional exports |
|
|
1232
|
+
| --- | --- |
|
|
1233
|
+
| `@openlup/platform/payments/stripe` | `createStripeSandboxPaymentExecutionAdapter`, `createStripePaymentReconciliationProvider`, `mapStripeIntent`, `mapSetupIntent`, `normalizeStripeFailureEvidence` |
|
|
1234
|
+
| `@openlup/platform/payments/tpay` | `createTpayPaymentExecutionAdapter`, `createTpayPaymentReconciliationProvider`, `createTpayHttpClient`, `normalizeTpayFailureEvidence`, `aliasValue`, `ProviderAttemptPreDispatchError` |
|
|
1235
|
+
|
|
1236
|
+
Types are inferred from those functions; no extra type export or neutral payment
|
|
1237
|
+
entry is added. These are the existing contributed implementations, including
|
|
1238
|
+
rich execution, recovery and filtered audit carriers. The host still owns SDK
|
|
1239
|
+
construction, environment/feature flags, callback/return presentation, account
|
|
1240
|
+
readiness and simulator composition. Its prepared-attempt facade re-exports the
|
|
1241
|
+
same error class so `instanceof` preserves the no-charge trust boundary.
|
|
1242
|
+
The M07 factories' neutral envelopes and capability limits remain unchanged;
|
|
1243
|
+
reference-support availability establishes no provider activation or stable API.
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { AnalyticsPort, BlobStoragePort, DataGatewayPort, HttpRuntimePort, MigrationRunnerPort, SchedulerPort, TransactionalRuntimePort } from "@openlup/core/platform-runtime";
|
|
2
2
|
import { type PlatformBundleDescriptor, type PlatformBundleId } from "../kernel/index.js";
|
|
3
|
+
interface PaymentServices {
|
|
4
|
+
run<T>(work: (port: unknown) => Promise<T>): Promise<T>;
|
|
5
|
+
}
|
|
3
6
|
type Env = Record<string, string | undefined>;
|
|
4
7
|
export interface PlatformBundlePorts {
|
|
8
|
+
fulfillment?: {
|
|
9
|
+
runDispatch(): Promise<unknown>;
|
|
10
|
+
} | null;
|
|
11
|
+
payments?: PaymentServices | null;
|
|
5
12
|
http: HttpRuntimePort | null;
|
|
6
13
|
scheduler: SchedulerPort | null;
|
|
7
14
|
blob: BlobStoragePort | null;
|
|
@@ -21,6 +28,12 @@ export interface PlatformBundle {
|
|
|
21
28
|
* transactional binder also receives the bundle's data capability.
|
|
22
29
|
*/
|
|
23
30
|
export interface PlatformBundleBinders {
|
|
31
|
+
/** Explicit host selection only; no default binder. */
|
|
32
|
+
fulfillment?: (env: Env) => {
|
|
33
|
+
runDispatch(): Promise<unknown>;
|
|
34
|
+
} | null;
|
|
35
|
+
/** Explicit host selection only; no default payment executor. */
|
|
36
|
+
payments?: (env: Env) => PaymentServices | null;
|
|
24
37
|
/**
|
|
25
38
|
* The supabase-js `createClient` used by the default Supabase Storage blob binder. Declared
|
|
26
39
|
* structurally here so the composition entry's declarations reference no data-module type.
|
|
@@ -39,6 +39,10 @@ export function createBundleComposer(binders = {}) {
|
|
|
39
39
|
analytics: binders.analytics?.(capabilities.analytics, env) ?? null,
|
|
40
40
|
transactional: binders.transactional?.(capabilities.transactional, capabilities.data, env) ?? null,
|
|
41
41
|
};
|
|
42
|
+
if (binders.fulfillment)
|
|
43
|
+
ports.fulfillment = binders.fulfillment(env);
|
|
44
|
+
if (binders.payments)
|
|
45
|
+
ports.payments = binders.payments(env);
|
|
42
46
|
ports.scheduler = binders.scheduler?.(capabilities.scheduler, env) ?? null;
|
|
43
47
|
return { id, descriptor, ports };
|
|
44
48
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Contribution, RequiredSchema } from '@openlup/core/readiness';
|
|
2
|
+
/** Structural callables keep composition independent of fulfillment implementation types. */
|
|
3
|
+
export declare function createFulfillmentContribution(input: {
|
|
4
|
+
version: string;
|
|
5
|
+
requiredSchema: RequiredSchema;
|
|
6
|
+
fulfillment: Readonly<Record<string, ((...args: never[]) => unknown) | null>>;
|
|
7
|
+
handlers: Contribution['handlers'];
|
|
8
|
+
schedules: Contribution['schedules'];
|
|
9
|
+
routes: Contribution['routes'];
|
|
10
|
+
}): {
|
|
11
|
+
contributions: Contribution[];
|
|
12
|
+
ports: {
|
|
13
|
+
[k: string]: ((...args: never[]) => unknown) | null;
|
|
14
|
+
};
|
|
15
|
+
routes: {
|
|
16
|
+
[k: string]: (...args: never[]) => unknown;
|
|
17
|
+
};
|
|
18
|
+
triggers: {
|
|
19
|
+
[k: string]: (...args: never[]) => unknown;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Structural callables keep composition independent of fulfillment implementation types. */
|
|
2
|
+
export function createFulfillmentContribution(input) {
|
|
3
|
+
const contribution = {
|
|
4
|
+
manifest: {
|
|
5
|
+
name: '@openlup/platform', version: input.version, kind: 'rail',
|
|
6
|
+
module: { id: 'fulfillment', kind: 'capability', provides: ['./fulfillment'], dependsOn: [] },
|
|
7
|
+
emits: [], handles: input.handlers.map(handler => handler.eventType),
|
|
8
|
+
schedules: input.schedules.map(({ id, cadenceSeconds }) => ({ id, cadenceSeconds })), env: [],
|
|
9
|
+
requiredPorts: Object.keys(input.fulfillment), routes: input.routes.map(route => route.id), requiredSchema: input.requiredSchema,
|
|
10
|
+
},
|
|
11
|
+
handlers: input.handlers, schedules: input.schedules, routes: input.routes,
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
contributions: [contribution],
|
|
15
|
+
ports: Object.fromEntries(Object.entries(input.fulfillment).filter(([, fn]) => typeof fn === 'function').map(([name, fn]) => [`@openlup/platform/fulfillment:${name}`, fn])),
|
|
16
|
+
routes: Object.fromEntries(input.routes.map(route => [`@openlup/platform/fulfillment:${route.id}`, route.handle])),
|
|
17
|
+
triggers: Object.fromEntries(input.schedules.map(schedule => [`@openlup/platform/fulfillment:${schedule.id}`, schedule.run])),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -4,3 +4,4 @@ export { createNodeHost, type NodeHost, type NodeHostAdmission, type NodeHostDep
|
|
|
4
4
|
export { createCatalogContribution, type CatalogContributionInput } from "./catalogContribution.js";
|
|
5
5
|
export { createInventoryContribution } from "./inventoryContribution.js";
|
|
6
6
|
export { createShippingContribution } from "./shippingContribution.js";
|
|
7
|
+
export { createFulfillmentContribution } from "./fulfillmentContribution.js";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
// The existing composer accepts one explicit payments binder; default hosts stay cold.
|
|
1
2
|
export { createBundleComposer, } from "./composeBundle.js";
|
|
2
3
|
export { bindNodeHttpPort, createScheduledEntrypointResolver } from "./httpBinding.js";
|
|
3
4
|
export { createNodeHost, } from "./nodeHost.js";
|
|
4
5
|
export { createCatalogContribution } from "./catalogContribution.js";
|
|
5
6
|
export { createInventoryContribution } from "./inventoryContribution.js";
|
|
6
7
|
export { createShippingContribution } from "./shippingContribution.js";
|
|
8
|
+
export { createFulfillmentContribution } from "./fulfillmentContribution.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FulfillmentAdapterConformanceEvidence, FulfillmentAdapterConformanceSubject } from "./contracts/index.js";
|
|
2
|
+
/** Runner-independent offline proof using only adapter-owned fixtures. Throws on a violated contract. */
|
|
3
|
+
export declare function runFulfillmentAdapterConformance(subjects: readonly FulfillmentAdapterConformanceSubject[]): Promise<FulfillmentAdapterConformanceEvidence>;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { dispatchEvidence, dispatchResult } from "./dispatchProjection.js";
|
|
2
|
+
import { projectObservation } from "./observationProjection.js";
|
|
3
|
+
import { readFulfillmentCapabilities } from "./providerCapabilities.js";
|
|
4
|
+
import { runFulfillmentDispatchWorker } from "./providerDispatchWorker.js";
|
|
5
|
+
/** Runner-independent offline proof using only adapter-owned fixtures. Throws on a violated contract. */
|
|
6
|
+
export async function runFulfillmentAdapterConformance(subjects) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
const cases = [];
|
|
9
|
+
for (const subject of subjects) {
|
|
10
|
+
const { adapter, fixtures } = subject;
|
|
11
|
+
const caps = adapter.capabilities;
|
|
12
|
+
const passed = (operation) => { cases.push({ subjectId: subject.subjectId, operation, passed: true }); };
|
|
13
|
+
requireFact(caps.defaultOff === true, "default-off");
|
|
14
|
+
requireFact(readFulfillmentCapabilities([caps], null).kind === "refused", "selection_required");
|
|
15
|
+
const first = fixtures.dispatch[0];
|
|
16
|
+
requireFact(Boolean(first), "dispatch fixture required");
|
|
17
|
+
const disabled = readFulfillmentCapabilities([{ ...caps, selected: false }], first.input.selection);
|
|
18
|
+
requireFact(disabled.kind === "refused" && disabled.refusal.reason === "disabled", "disabled selection");
|
|
19
|
+
const missing = await adapter.dispatch.prepare({ ...first.input, selection: { ...first.input.selection, providerRef: null } });
|
|
20
|
+
requireFact(missing.kind === "refused" && missing.refusal.reason === "selection_required", "adapter selection_required");
|
|
21
|
+
passed("default-off and selection_required");
|
|
22
|
+
for (const fixture of fixtures.dispatch) {
|
|
23
|
+
const before = subject.readIoCount();
|
|
24
|
+
const prepared = await adapter.dispatch.prepare(fixture.input);
|
|
25
|
+
requireSame(prepared, fixture.expectedPrepare, "prepare fixture");
|
|
26
|
+
requireFact(subject.readIoCount() === before, "prepare before I/O");
|
|
27
|
+
assertDurable(prepared, subject);
|
|
28
|
+
if (prepared.kind === "refused") {
|
|
29
|
+
passed("dispatch refused before I/O");
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
requireFact(caps.operations.dispatch.supported, "unsupported dispatch refuses");
|
|
33
|
+
requireSame(prepared.evidence, dispatchEvidence(prepared.evidence), "prepared evidence allowlist");
|
|
34
|
+
requireFact(prepared.evidence.providerRef === fixture.input.selection.providerRef, "prepared provider identity");
|
|
35
|
+
requireSame(prepared.evidence.contactRevision, fixture.input.contactRevision, "prepared contact identity");
|
|
36
|
+
const mismatch = await adapter.dispatch.dispatch(fixture.input, { ...prepared.evidence, commandFingerprint: `${prepared.evidence.commandFingerprint}:mismatch` });
|
|
37
|
+
requireSame(mismatch, { kind: "not_dispatched", effectMayHaveSucceeded: false,
|
|
38
|
+
refusal: { reason: "not_dispatchable", phase: "dispatch", retryable: false } }, "mismatched preparation refusal");
|
|
39
|
+
requireFact(subject.readIoCount() === before, "mismatch before I/O");
|
|
40
|
+
requireFact(fixture.expectedDispatch !== undefined, "dispatch expected result");
|
|
41
|
+
const result = await adapter.dispatch.dispatch(fixture.input, prepared.evidence);
|
|
42
|
+
requireSame(result, fixture.expectedDispatch, "dispatch fixture");
|
|
43
|
+
requireSame(result, dispatchResult(result), "dispatch allowlist");
|
|
44
|
+
assertDurable(result, subject);
|
|
45
|
+
if (result.kind === "accepted") {
|
|
46
|
+
requireSame(result.evidence, prepared.evidence, "dispatch fingerprint identity");
|
|
47
|
+
requireFact(result.externalRef.length > 0 && result.providerAttemptKey.length > 0 && result.labelKey.length > 0, "accepted proof before local ACK");
|
|
48
|
+
}
|
|
49
|
+
else
|
|
50
|
+
requireFact(result.kind === "unknown" ? result.effectMayHaveSucceeded : !result.effectMayHaveSucceeded, "unknown versus refused");
|
|
51
|
+
if (fixture.replay) {
|
|
52
|
+
const calls = subject.readIoCount();
|
|
53
|
+
requireSame(await adapter.dispatch.dispatch(fixture.input, prepared.evidence), result, "dispatch replay");
|
|
54
|
+
requireFact(subject.readIoCount() === calls, "replay no second I/O");
|
|
55
|
+
}
|
|
56
|
+
passed(`dispatch ${result.kind}, fingerprint, mismatch and replay`);
|
|
57
|
+
}
|
|
58
|
+
for (const operation of ["create", "read", "void"]) {
|
|
59
|
+
const supported = caps.operations[operation === "create" ? "labelCreate" : operation === "read" ? "labelRead" : "labelVoid"].supported;
|
|
60
|
+
const rows = fixtures.label?.[operation] ?? [];
|
|
61
|
+
if (supported)
|
|
62
|
+
requireFact(Boolean(adapter.label) && rows.length > 0, `label ${operation} fixture`);
|
|
63
|
+
if (rows.length)
|
|
64
|
+
requireFact(Boolean(adapter.label), "label port required");
|
|
65
|
+
for (const fixture of rows) {
|
|
66
|
+
const before = subject.readIoCount();
|
|
67
|
+
const result = operation === "create" ? await adapter.label.create(fixture.input[0])
|
|
68
|
+
: operation === "read" ? await adapter.label.read(fixture.input[0])
|
|
69
|
+
: await adapter.label.void(fixture.input[0]);
|
|
70
|
+
requireSame(result, fixture.expected, `label ${operation}`);
|
|
71
|
+
if (!supported && !(operation === "read" && caps.labelReadOwner === "host_storage")) {
|
|
72
|
+
requireFact("refusal" in result && result.refusal?.reason === "unsupported", `unsupported label ${operation}`);
|
|
73
|
+
requireFact(subject.readIoCount() === before, "unsupported label before I/O");
|
|
74
|
+
}
|
|
75
|
+
if (!(operation === "read" && "kind" in result && result.kind === "available"))
|
|
76
|
+
assertDurable(result, subject);
|
|
77
|
+
passed(`label ${operation}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (caps.operations.verifyObservation.supported)
|
|
81
|
+
requireFact(Boolean(adapter.observation) && Boolean(fixtures.observation?.length), "observation fixtures required");
|
|
82
|
+
const events = new Map();
|
|
83
|
+
for (const fixture of fixtures.observation ?? []) {
|
|
84
|
+
requireFact(Boolean(adapter.observation), "observation port required");
|
|
85
|
+
const result = await adapter.observation.verifyAndNormalize(...fixture.input);
|
|
86
|
+
requireSame(result, fixture.expected, "observation fixture");
|
|
87
|
+
assertDurable(result, subject);
|
|
88
|
+
if (!caps.operations.verifyObservation.supported)
|
|
89
|
+
requireFact(result.kind === "refused" && result.refusal.reason === "unsupported", "unsupported observation");
|
|
90
|
+
if (result.kind === "verified") {
|
|
91
|
+
requireSame(result.observation, projectObservation(result.observation), "observation allowlist");
|
|
92
|
+
const observation = result.observation;
|
|
93
|
+
const identity = stable({ provider: observation.fact.providerKind, reference: observation.fact.fulfillmentReference,
|
|
94
|
+
kind: observation.eventKind, outcome: observation.fact.canonicalStatus, occurredAt: observation.fact.occurredAt });
|
|
95
|
+
const previous = events.get(observation.eventKey);
|
|
96
|
+
if (previous)
|
|
97
|
+
requireFact(previous.identity === identity, "eventKey includes outcome and occurrence");
|
|
98
|
+
events.set(observation.eventKey, { identity, occurredAt: observation.fact.occurredAt });
|
|
99
|
+
const replay = await adapter.observation.verifyAndNormalize(...fixture.input);
|
|
100
|
+
requireSame(replay, result, "observation exact retry");
|
|
101
|
+
}
|
|
102
|
+
passed("observation identity, replay and redaction");
|
|
103
|
+
}
|
|
104
|
+
if (caps.operations.readback.supported)
|
|
105
|
+
requireFact(Boolean(adapter.readback) && Boolean(fixtures.readback?.length), "readback fixtures required");
|
|
106
|
+
for (const fixture of fixtures.readback ?? []) {
|
|
107
|
+
requireFact(Boolean(adapter.readback), "readback port required");
|
|
108
|
+
const result = await adapter.readback.read(...fixture.input);
|
|
109
|
+
requireSame(result, fixture.expected, "readback fixture");
|
|
110
|
+
assertDurable(result, subject);
|
|
111
|
+
if (!caps.operations.readback.supported)
|
|
112
|
+
requireFact(result.kind === "unsupported", "unsupported readback");
|
|
113
|
+
if (fixture.localMissing)
|
|
114
|
+
requireFact(result.kind === "unknown" && result.refusal.reason === "local_missing", "local missing is never absence");
|
|
115
|
+
if (result.kind === "absent") {
|
|
116
|
+
requireFact(caps.operations.exactAbsence.supported, "unsupported exact absence refuses");
|
|
117
|
+
const { mode: _mode, ...scope } = fixture.input[0];
|
|
118
|
+
requireSame(result.scope, scope, "absence scope");
|
|
119
|
+
requireFact(result.proof.kind === "authoritative_exact_lookup" && /^evidence:.+/.test(result.proof.evidenceRef) && Number.isFinite(Date.parse(result.proof.checkedAt)), "absence proof");
|
|
120
|
+
}
|
|
121
|
+
if (result.kind === "found")
|
|
122
|
+
requireFact(result.externalRef === fixture.input[0].externalRef || fixture.input[0].externalRef === null, "found identity");
|
|
123
|
+
passed(`readback ${result.kind}`);
|
|
124
|
+
}
|
|
125
|
+
for (const name of ["stockSync", "productSync"]) {
|
|
126
|
+
const supported = caps.operations[name === "stockSync" ? "syncStock" : "syncProducts"].supported;
|
|
127
|
+
const port = adapter[name];
|
|
128
|
+
const rows = fixtures[name] ?? [];
|
|
129
|
+
if (supported)
|
|
130
|
+
requireFact(Boolean(port) && rows.length > 0, `${name} fixtures required`);
|
|
131
|
+
for (const fixture of rows) {
|
|
132
|
+
requireFact(Boolean(port), `${name} port required`);
|
|
133
|
+
let returned;
|
|
134
|
+
try {
|
|
135
|
+
// Dispatch a typed fixture tuple to its adapter-owned operation.
|
|
136
|
+
const method = port[fixture.operation];
|
|
137
|
+
returned = await method.apply(port, fixture.input);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const refusal = error;
|
|
141
|
+
requireFact(refusal.reason === "unsupported" && refusal.phase === "sync" && refusal.retryable === false, "typed unsupported sync refusal");
|
|
142
|
+
returned = { kind: "refused", refusal };
|
|
143
|
+
}
|
|
144
|
+
requireSame(returned, fixture.expected, `${name} ${fixture.operation}`);
|
|
145
|
+
assertDurable(returned, subject);
|
|
146
|
+
if (!supported)
|
|
147
|
+
requireFact(typeof returned === "object" && returned !== null && "kind" in returned && returned.kind === "refused", "unsupported sync");
|
|
148
|
+
passed(`${name} ${fixture.operation}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (subject.store) {
|
|
152
|
+
await checkStore(subject);
|
|
153
|
+
passed("store G14 replay, own-key remap and one-remap bound");
|
|
154
|
+
}
|
|
155
|
+
assertDurable({ subjectId: subject.subjectId, providerRef: caps.providerRef }, subject);
|
|
156
|
+
evidence.push({ subjectId: subject.subjectId, providerRef: caps.providerRef });
|
|
157
|
+
}
|
|
158
|
+
return { subjects: evidence, cases };
|
|
159
|
+
}
|
|
160
|
+
async function checkStore(subject) {
|
|
161
|
+
const fixture = subject.store;
|
|
162
|
+
const store = await fixture.create();
|
|
163
|
+
const first = await store.claim(fixture.claim);
|
|
164
|
+
requireFact(first.kind === "claimed", "store initial claim");
|
|
165
|
+
const replay = await store.claim(fixture.claim);
|
|
166
|
+
requireFact(replay.kind === "claimed" && replay.record.replayed, "store same-key claim replay");
|
|
167
|
+
const attempt = () => store.recordProviderAttempt(fixture.mappedAttemptKey, fixture.claim.fulfillmentRef, fixture.claim.evidence, fixture.claim.mode, "recorded");
|
|
168
|
+
requireFact(!(await attempt()).replayed && (await attempt()).replayed, "G14 same key replays");
|
|
169
|
+
requireFact(fixture.mappedAttemptKey !== fixture.remappedAttemptKey, "G14 remap own key");
|
|
170
|
+
requireFact(fixture.claim.requestKey === fixture.remappedClaim.requestKey && fixture.claim.evidence.commandFingerprint !== fixture.remappedClaim.evidence.commandFingerprint, "draft remap fixture");
|
|
171
|
+
const remap = await store.claim(fixture.remappedClaim);
|
|
172
|
+
requireFact(remap.kind === "claimed", "draft re-claim");
|
|
173
|
+
const row = await store.read(fixture.claim.requestKey);
|
|
174
|
+
requireSame(row?.evidence, fixture.remappedClaim.evidence, "atomic remap fingerprint and evidence");
|
|
175
|
+
assertDurable(row, subject);
|
|
176
|
+
requireFact(!(await store.recordProviderAttempt(fixture.remappedAttemptKey, fixture.claim.fulfillmentRef, fixture.remappedClaim.evidence, fixture.claim.mode, "recorded")).replayed, "G14 new mapped attempt");
|
|
177
|
+
const attempts = await store.readMappedAttempts();
|
|
178
|
+
requireFact(attempts.length === 2, "G14 no duplicate durable attempts");
|
|
179
|
+
requireSame(attempts.map(row => row.attemptKey).sort(), [fixture.mappedAttemptKey, fixture.remappedAttemptKey].sort(), "G14 durable remap identity");
|
|
180
|
+
for (const attempt of attempts) {
|
|
181
|
+
requireSame(attempt.preparedEvidence, dispatchEvidence(attempt.preparedEvidence), "G14 sanitized evidence only");
|
|
182
|
+
assertDurable(attempt, subject);
|
|
183
|
+
}
|
|
184
|
+
const stale = await store.begin({ dispatchRef: row.dispatchRef, commandFingerprint: fixture.claim.evidence.commandFingerprint });
|
|
185
|
+
requireFact(stale.kind === "refused", "stale prepared command refuses");
|
|
186
|
+
let claims = 0;
|
|
187
|
+
let calls = 0;
|
|
188
|
+
let reads = 0;
|
|
189
|
+
const bounded = await runFulfillmentDispatchWorker({
|
|
190
|
+
store: { ...await fixture.create(), async read() { return null; }, async readCandidate() { reads += 1; return reads === 1 ? fixture.request : fixture.remappedRequest; },
|
|
191
|
+
async claim() { claims += 1; return { kind: "refused", refusal: { reason: "contact_stale", phase: "claim", retryable: true } }; } },
|
|
192
|
+
provider: { prepare: request => subject.adapter.dispatch.prepare(request), async dispatch() { calls += 1; throw new Error("unexpected dispatch"); } },
|
|
193
|
+
listCandidates: async () => [fixture.request], config: { mode: "stage", batchLimit: 1 },
|
|
194
|
+
keys: { request: () => fixture.claim.requestKey, mappedAttempt: (_key, index) => index === 0 ? fixture.mappedAttemptKey : fixture.remappedAttemptKey,
|
|
195
|
+
acceptance: () => ({ providerAttemptKey: "fixture-attempt", labelKey: "fixture-label" }) },
|
|
196
|
+
});
|
|
197
|
+
requireFact(claims === 2 && calls === 0 && !bounded.ok, "one-remap limit before I/O");
|
|
198
|
+
}
|
|
199
|
+
function assertDurable(value, subject) {
|
|
200
|
+
const text = JSON.stringify(value) ?? "";
|
|
201
|
+
requireFact(!subject.sensitiveValues.filter(Boolean).some(secret => text.includes(secret) || text.includes(JSON.stringify(secret).slice(1, -1))), "durable secret redaction");
|
|
202
|
+
const visit = (node) => {
|
|
203
|
+
if (!node || typeof node !== "object")
|
|
204
|
+
return;
|
|
205
|
+
for (const [key, child] of Object.entries(node)) {
|
|
206
|
+
requireFact(!/^(rawBody|rawPayload|rawCommand|password|secret|credential|authorization|headers|signedUrl)$/i.test(key), "durable field allowlist");
|
|
207
|
+
if (typeof child === "string")
|
|
208
|
+
requireFact(!/[?&](?:signature|sig|token|x-amz-signature|credential)=/i.test(child), "durable signed URL");
|
|
209
|
+
visit(child);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
visit(value);
|
|
213
|
+
}
|
|
214
|
+
function stable(value) {
|
|
215
|
+
if (Array.isArray(value))
|
|
216
|
+
return `[${value.map(stable).join(",")}]`;
|
|
217
|
+
if (value && typeof value === "object")
|
|
218
|
+
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => `${JSON.stringify(key)}:${stable(child)}`).join(",")}}`;
|
|
219
|
+
return JSON.stringify(value);
|
|
220
|
+
}
|
|
221
|
+
function requireSame(actual, expected, label) { requireFact(stable(actual) === stable(expected), label); }
|
|
222
|
+
function requireFact(value, label) { if (!value)
|
|
223
|
+
throw new Error(`fulfillment_adapter_conformance:${label}`); }
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FulfillmentServices, FulfillmentServicesOptions } from './contracts/index.js';
|
|
2
|
+
/** Explicit selection; no environment readers, implicit provider or automatic dispatch. */
|
|
3
|
+
export declare function createFulfillmentServices(options: FulfillmentServicesOptions): FulfillmentServices;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FULFILLMENT_NATIVE_UNSUPPORTED } from './nativeUnsupported.js';
|
|
2
|
+
import { readFulfillmentCapabilities } from './providerCapabilities.js';
|
|
3
|
+
import { runFulfillmentDispatchWorker } from './providerDispatchWorker.js';
|
|
4
|
+
/** Explicit selection; no environment readers, implicit provider or automatic dispatch. */
|
|
5
|
+
export function createFulfillmentServices(options) {
|
|
6
|
+
const { enabled, selection, capability, store, provider, readiness: observe, listCandidates, now } = options;
|
|
7
|
+
const config = { ...options.config }, keys = { ...options.keys };
|
|
8
|
+
const readiness = async () => {
|
|
9
|
+
if (enabled !== true)
|
|
10
|
+
return { ready: false, refusal: { reason: 'disabled', phase: 'selection', retryable: false } };
|
|
11
|
+
const selected = readFulfillmentCapabilities(capability ? [capability] : [], selection);
|
|
12
|
+
if (selected.kind === 'refused')
|
|
13
|
+
return { ready: false, refusal: selected.refusal };
|
|
14
|
+
if (!selected.descriptor.operations.dispatch.supported)
|
|
15
|
+
return { ready: false, refusal: { reason: 'unsupported', phase: 'selection', retryable: false } };
|
|
16
|
+
if (!store || !provider)
|
|
17
|
+
return { ready: false, refusal: FULFILLMENT_NATIVE_UNSUPPORTED };
|
|
18
|
+
return observe();
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
readiness,
|
|
22
|
+
async runDispatch() {
|
|
23
|
+
const state = await readiness();
|
|
24
|
+
if (!state.ready)
|
|
25
|
+
return 'kind' in state.refusal ? state.refusal : { kind: 'refused', refusal: state.refusal };
|
|
26
|
+
return runFulfillmentDispatchWorker({ store: store, provider, listCandidates, keys, config, now });
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|