@devlab.group/agent-commerce 2.4.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +115 -15
  2. package/dist/ap2.d.ts +24 -12
  3. package/dist/ap2.js +31 -13
  4. package/dist/ap2.js.map +1 -1
  5. package/dist/{chunk-DTKU3Z2M.js → chunk-3OTCFFFT.js} +37 -4
  6. package/dist/chunk-3OTCFFFT.js.map +1 -0
  7. package/dist/{chunk-QPV3EGJO.js → chunk-4K7FIBJM.js} +25 -4
  8. package/dist/chunk-4K7FIBJM.js.map +1 -0
  9. package/dist/{chunk-CXWC5LWM.js → chunk-FKQK5XZA.js} +652 -68
  10. package/dist/chunk-FKQK5XZA.js.map +1 -0
  11. package/dist/chunk-GA3O2A7R.js +92 -0
  12. package/dist/chunk-GA3O2A7R.js.map +1 -0
  13. package/dist/{chunk-WZW5ORZH.js → chunk-VUCMBGUR.js} +3 -3
  14. package/dist/{chunk-WZW5ORZH.js.map → chunk-VUCMBGUR.js.map} +1 -1
  15. package/dist/cli/{ap2-GWSP4LYY.js → ap2-YAPLERSC.js} +34 -15
  16. package/dist/cli/ap2-YAPLERSC.js.map +1 -0
  17. package/dist/cli/{chunk-G2JEY7MB.js → chunk-2J2IQQ4X.js} +8 -7
  18. package/dist/cli/chunk-2J2IQQ4X.js.map +1 -0
  19. package/dist/cli/chunk-2UPPX3FJ.js +311 -0
  20. package/dist/cli/chunk-2UPPX3FJ.js.map +1 -0
  21. package/dist/cli/{chunk-WYOGIANL.js → chunk-4H2QLHQI.js} +4 -4
  22. package/dist/cli/{chunk-WYOGIANL.js.map → chunk-4H2QLHQI.js.map} +1 -1
  23. package/dist/cli/{chunk-OZ4LAWBK.js → chunk-5R7DM5LM.js} +25 -4
  24. package/dist/cli/chunk-5R7DM5LM.js.map +1 -0
  25. package/dist/cli/chunk-663ALDZA.js +92 -0
  26. package/dist/cli/chunk-663ALDZA.js.map +1 -0
  27. package/dist/cli/{chunk-QQ3WY5L3.js → chunk-IDY2735M.js} +36 -104
  28. package/dist/cli/chunk-IDY2735M.js.map +1 -0
  29. package/dist/cli/chunk-JFVRG2HH.js +107 -0
  30. package/dist/cli/chunk-JFVRG2HH.js.map +1 -0
  31. package/dist/cli/chunk-M7NXVRU5.js +8 -0
  32. package/dist/cli/{chunk-ITWNSS2B.js.map → chunk-M7NXVRU5.js.map} +1 -1
  33. package/dist/cli/chunk-NIU5EMSB.js +119 -0
  34. package/dist/cli/chunk-NIU5EMSB.js.map +1 -0
  35. package/dist/cli/{chunk-TNMLDPUO.js → chunk-QA2OPHBF.js} +655 -70
  36. package/dist/cli/chunk-QA2OPHBF.js.map +1 -0
  37. package/dist/cli/{chunk-GJIMDMYT.js → chunk-QLUCNPRT.js} +404 -161
  38. package/dist/cli/chunk-QLUCNPRT.js.map +1 -0
  39. package/dist/cli/chunk-SYQLJXIU.js +105 -0
  40. package/dist/cli/chunk-SYQLJXIU.js.map +1 -0
  41. package/dist/cli/{chunk-3MGAJ5FX.js → chunk-VJ4EO4LE.js} +3 -3
  42. package/dist/cli/{chunk-3MGAJ5FX.js.map → chunk-VJ4EO4LE.js.map} +1 -1
  43. package/dist/cli/{chunk-PIMFPCNZ.js → chunk-Z34UMZBB.js} +234 -85
  44. package/dist/cli/chunk-Z34UMZBB.js.map +1 -0
  45. package/dist/cli/config-BWFMN2ZB.js +12 -0
  46. package/dist/cli/{config-UI4EDG4L.js.map → config-BWFMN2ZB.js.map} +1 -1
  47. package/dist/cli/index.js +258 -11
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/cli/{mcp-PGBRWZVA.js → mcp-KZEMXLHR.js} +5 -4
  50. package/dist/cli/mcp-KZEMXLHR.js.map +1 -0
  51. package/dist/cli/{near-5AY3HWYF.js → near-U6365VFX.js} +15 -4
  52. package/dist/cli/near-U6365VFX.js.map +1 -0
  53. package/dist/cli/payment-providers-SZL36H3Y.js +6 -0
  54. package/dist/cli/payment-providers-SZL36H3Y.js.map +1 -0
  55. package/dist/cli/{provider-I2ESL6RS.js → provider-6TKB5ADJ.js} +26 -15
  56. package/dist/cli/provider-6TKB5ADJ.js.map +1 -0
  57. package/dist/cli/receipts-ZGLJWMQG.js +8 -0
  58. package/dist/cli/receipts-ZGLJWMQG.js.map +1 -0
  59. package/dist/cli/reconcile-IC22MJD5.js +4 -0
  60. package/dist/cli/reconcile-IC22MJD5.js.map +1 -0
  61. package/dist/cli/{run-JK4ERPJV.js → run-ZTYPQI4D.js} +151 -115
  62. package/dist/cli/run-ZTYPQI4D.js.map +1 -0
  63. package/dist/cli/svm-KDSPDFJG.js +48 -0
  64. package/dist/cli/svm-KDSPDFJG.js.map +1 -0
  65. package/dist/cli/{x402-7WBFZQXV.js → x402-WLYBLODI.js} +10 -9
  66. package/dist/cli/x402-WLYBLODI.js.map +1 -0
  67. package/dist/index.d.ts +112 -12
  68. package/dist/index.js +948 -157
  69. package/dist/index.js.map +1 -1
  70. package/dist/mcp.d.ts +1 -2
  71. package/dist/mcp.js +1 -1
  72. package/dist/mpp.js +20 -10
  73. package/dist/mpp.js.map +1 -1
  74. package/dist/{near-D6C42VPE.js → near-FH7HPZV3.js} +15 -4
  75. package/dist/near-FH7HPZV3.js.map +1 -0
  76. package/dist/{protocol-adapter-C9odYl6l.d.ts → protocol-adapter-DEXpdp8m.d.ts} +102 -48
  77. package/dist/{runtime-Cl_BLVZV.d.ts → runtime-Dz2fSO-P.d.ts} +220 -2
  78. package/dist/svm-KNC3PHMS.js +48 -0
  79. package/dist/svm-KNC3PHMS.js.map +1 -0
  80. package/dist/x402.js +5 -5
  81. package/package.json +1 -1
  82. package/dist/authorization-DjAecwuW.d.ts +0 -115
  83. package/dist/chunk-CXWC5LWM.js.map +0 -1
  84. package/dist/chunk-DTKU3Z2M.js.map +0 -1
  85. package/dist/chunk-KA2AX5FH.js +0 -44
  86. package/dist/chunk-KA2AX5FH.js.map +0 -1
  87. package/dist/chunk-QPV3EGJO.js.map +0 -1
  88. package/dist/cli/ap2-GWSP4LYY.js.map +0 -1
  89. package/dist/cli/chunk-G2JEY7MB.js.map +0 -1
  90. package/dist/cli/chunk-GJIMDMYT.js.map +0 -1
  91. package/dist/cli/chunk-ITWNSS2B.js +0 -8
  92. package/dist/cli/chunk-OSTCU6ZK.js +0 -44
  93. package/dist/cli/chunk-OSTCU6ZK.js.map +0 -1
  94. package/dist/cli/chunk-OZ4LAWBK.js.map +0 -1
  95. package/dist/cli/chunk-PIMFPCNZ.js.map +0 -1
  96. package/dist/cli/chunk-QQ3WY5L3.js.map +0 -1
  97. package/dist/cli/chunk-TNMLDPUO.js.map +0 -1
  98. package/dist/cli/config-UI4EDG4L.js +0 -11
  99. package/dist/cli/mcp-PGBRWZVA.js.map +0 -1
  100. package/dist/cli/near-5AY3HWYF.js.map +0 -1
  101. package/dist/cli/provider-I2ESL6RS.js.map +0 -1
  102. package/dist/cli/run-JK4ERPJV.js.map +0 -1
  103. package/dist/cli/svm-3MMYFPC7.js +0 -33
  104. package/dist/cli/svm-3MMYFPC7.js.map +0 -1
  105. package/dist/cli/x402-7WBFZQXV.js.map +0 -1
  106. package/dist/near-D6C42VPE.js.map +0 -1
  107. package/dist/svm-QEUAUNPQ.js +0 -33
  108. package/dist/svm-QEUAUNPQ.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/storage/receipts/redact.ts","../../src/storage/receipts/rows.ts","../../src/storage/receipts/schema.ts","../../src/storage/receipts/store.ts"],"names":["fsConstants"],"mappings":"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","file":"chunk-QLUCNPRT.js","sourcesContent":["/**\n * Defense in depth against persisting secrets. Receipt, event and payment\n * metadata must not carry secrets (docs/contracts.md, \"Store no secrets\"); at\n * the persistence boundary this module replaces secret-shaped values rather\n * than rejecting the write, because event persistence must not break a\n * commerce flow.\n */\n\n// Matches anywhere in the key (only `auth` and `proof` must be the whole key),\n// so an audit field whose name contains \"token\", \"session\", \"jwt\" or similar\n// persists as [REDACTED]\nconst SECRET_KEY_PATTERN =\n /private[-_ ]?key|secret|password|mnemonic|seed[-_ ]?phrase|api[-_ ]?key|authoriz(e|ation)|^auth$|auth[-_]?header|payment[-_ ]?proof|^proof$|x-payment|signature|signed[-_ ]?message|token|bearer|credential|cookie|session|jwt/i;\n\nconst REDACTED = '[REDACTED]';\n\n/** True if `key` looks like it names a secret value */\nexport function isSecretKey(key: string): boolean {\n return SECRET_KEY_PATTERN.test(key);\n}\n\n/**\n * Returns a copy with every value under a secret-shaped key replaced, at any\n * depth. Clean input comes back equal to itself.\n */\nexport function redact<T>(value: T): T {\n return redactValue(value) as T;\n}\n\nfunction redactValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(redactValue);\n }\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value as Record<string, unknown>)) {\n out[key] = isSecretKey(key) ? REDACTED : redactValue(inner);\n }\n return out;\n }\n return value;\n}\n","/**\n * SQLite row shapes and their mapping to and from the canonical model. Under\n * `exactOptionalPropertyTypes`, an optional field is set only when present.\n */\nimport {\n type AuthorizationRecord,\n type CommerceEvent,\n type CommerceEventType,\n type CommerceReceipt,\n type IsoTimestamp,\n type PaymentAttempt,\n type PaymentAttemptResolution,\n type PaymentResult,\n type Transaction,\n transactionState,\n} from '../../core';\nimport { redact } from './redact';\n\nexport interface ReceiptRow {\n readonly id: string;\n readonly request_id: string;\n readonly resource_id: string;\n readonly payment_json: string | null;\n readonly delivered_at: string;\n readonly backend_status: number;\n readonly duration_ms: number | null;\n readonly protocol: string | null;\n readonly metadata_json: string | null;\n readonly authorization_json: string | null;\n readonly transaction_id: string | null;\n}\n\nexport interface EventRow {\n readonly id: string;\n readonly type: string;\n readonly request_id: string;\n readonly resource_id: string | null;\n readonly at: string;\n readonly adapter: string | null;\n readonly payment_provider: string | null;\n readonly duration_ms: number | null;\n readonly status: string | null;\n readonly data_json: string | null;\n readonly transaction_id: string | null;\n}\n\nexport interface PaymentAttemptRow {\n readonly id: string;\n readonly request_id: string;\n readonly resource_id: string;\n readonly provider: string;\n readonly replay_key: string;\n readonly status: string;\n readonly amount: string;\n readonly currency: string;\n readonly payer: string | null;\n readonly payee: string | null;\n readonly external_reference: string | null;\n readonly rejection_reason: string | null;\n readonly created_at: string;\n readonly updated_at: string;\n readonly transaction_id: string | null;\n readonly resolution_json: string | null;\n}\n\n/** A `transactions` row joined with the columns of its payment attempt */\nexport interface TransactionRow {\n readonly seq: number;\n readonly id: string;\n readonly request_id: string;\n readonly resource_id: string;\n readonly protocol: string;\n readonly provider: string;\n readonly attempt_id: string;\n readonly replay_key: string;\n readonly network: string | null;\n readonly asset: string | null;\n readonly authorization_json: string | null;\n readonly reservation_id: string | null;\n readonly locator_json: string | null;\n readonly expires_at: string | null;\n readonly input_digest: string | null;\n readonly config_digest: string;\n readonly settling_at: string | null;\n readonly backend_dispatched_at: string | null;\n readonly backend_deadline_at: string | null;\n readonly backend_status: number | null;\n readonly backend_error_code: string | null;\n readonly receipt_id: string | null;\n readonly persistence_failures_json: string | null;\n readonly completed_at: string | null;\n readonly created_at: string;\n readonly updated_at: string;\n readonly payment_status: string;\n readonly payment_amount: string;\n readonly payment_currency: string;\n readonly payment_payee: string | null;\n readonly payment_external_reference: string | null;\n}\n\nexport function receiptToRow(receipt: CommerceReceipt): {\n id: string;\n request_id: string;\n resource_id: string;\n payment_json: string | null;\n delivered_at: string;\n backend_status: number;\n duration_ms: number | null;\n protocol: string | null;\n metadata_json: string | null;\n authorization_json: string | null;\n transaction_id: string | null;\n} {\n return {\n id: receipt.id,\n request_id: receipt.requestId,\n resource_id: receipt.resourceId,\n payment_json: receipt.payment !== undefined ? JSON.stringify(redact(receipt.payment)) : null,\n delivered_at: receipt.deliveredAt,\n backend_status: receipt.backendStatus,\n duration_ms: receipt.durationMs ?? null,\n protocol: receipt.protocol ?? null,\n metadata_json: receipt.metadata !== undefined ? JSON.stringify(redact(receipt.metadata)) : null,\n authorization_json:\n receipt.authorization !== undefined ? JSON.stringify(redact(receipt.authorization)) : null,\n transaction_id: receipt.transactionId ?? null,\n };\n}\n\nexport function rowToReceipt(row: ReceiptRow): CommerceReceipt {\n const payment =\n row.payment_json !== null ? (JSON.parse(row.payment_json) as PaymentResult) : undefined;\n const metadata =\n row.metadata_json !== null\n ? (JSON.parse(row.metadata_json) as Record<string, unknown>)\n : undefined;\n // Null for a receipt written before this column existed and for a resource\n // that requires no authorization\n const authorization =\n row.authorization_json !== null\n ? (JSON.parse(row.authorization_json) as AuthorizationRecord)\n : undefined;\n return {\n id: row.id,\n requestId: row.request_id,\n resourceId: row.resource_id,\n deliveredAt: row.delivered_at,\n backendStatus: row.backend_status,\n ...(payment !== undefined ? { payment } : {}),\n ...(row.duration_ms !== null ? { durationMs: row.duration_ms } : {}),\n ...(row.protocol !== null ? { protocol: row.protocol } : {}),\n ...(metadata !== undefined ? { metadata } : {}),\n ...(authorization !== undefined ? { authorization } : {}),\n ...(row.transaction_id !== null ? { transactionId: row.transaction_id } : {}),\n };\n}\n\nexport function eventToRow(event: CommerceEvent): {\n id: string;\n type: string;\n request_id: string;\n resource_id: string | null;\n at: string;\n adapter: string | null;\n payment_provider: string | null;\n duration_ms: number | null;\n status: string | null;\n data_json: string | null;\n transaction_id: string | null;\n} {\n return {\n id: event.id,\n type: event.type,\n request_id: event.requestId,\n resource_id: event.resourceId ?? null,\n at: event.at,\n adapter: event.adapter ?? null,\n payment_provider: event.paymentProvider ?? null,\n duration_ms: event.durationMs ?? null,\n status: event.status ?? null,\n data_json: event.data !== undefined ? JSON.stringify(redact(event.data)) : null,\n transaction_id: event.transactionId ?? null,\n };\n}\n\nexport function rowToEvent(row: EventRow): CommerceEvent {\n const data =\n row.data_json !== null ? (JSON.parse(row.data_json) as Record<string, unknown>) : undefined;\n return {\n id: row.id,\n type: row.type as CommerceEventType,\n requestId: row.request_id,\n at: row.at,\n ...(row.resource_id !== null ? { resourceId: row.resource_id } : {}),\n ...(row.adapter !== null ? { adapter: row.adapter } : {}),\n ...(row.payment_provider !== null ? { paymentProvider: row.payment_provider } : {}),\n ...(row.duration_ms !== null ? { durationMs: row.duration_ms } : {}),\n ...(row.status !== null ? { status: row.status as 'ok' | 'error' } : {}),\n ...(data !== undefined ? { data } : {}),\n ...(row.transaction_id !== null ? { transactionId: row.transaction_id } : {}),\n };\n}\n\nexport function rowToPaymentAttempt(row: PaymentAttemptRow): PaymentAttempt {\n return {\n id: row.id,\n requestId: row.request_id,\n resourceId: row.resource_id,\n provider: row.provider,\n replayKey: row.replay_key,\n status: row.status as PaymentAttempt['status'],\n amount: row.amount,\n currency: row.currency,\n ...(row.payer !== null ? { payer: row.payer } : {}),\n ...(row.payee !== null ? { payee: row.payee } : {}),\n ...(row.external_reference !== null ? { externalReference: row.external_reference } : {}),\n ...(row.rejection_reason !== null ? { rejectionReason: row.rejection_reason } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n ...(row.transaction_id !== null ? { transactionId: row.transaction_id } : {}),\n ...(row.resolution_json !== null\n ? { resolution: JSON.parse(row.resolution_json) as PaymentAttemptResolution }\n : {}),\n };\n}\n\n/**\n * Recompute state using `now` so reads reflect backend deadlines that have\n * passed since the last write\n */\nexport function rowToTransaction(row: TransactionRow, now: IsoTimestamp): Transaction {\n const status = row.payment_status as PaymentAttempt['status'];\n const backend =\n row.backend_dispatched_at !== null && row.backend_deadline_at !== null\n ? {\n dispatchedAt: row.backend_dispatched_at,\n deadlineAt: row.backend_deadline_at,\n ...(row.backend_status !== null ? { status: row.backend_status } : {}),\n ...(row.backend_error_code !== null ? { errorCode: row.backend_error_code } : {}),\n }\n : undefined;\n const completedAt = row.completed_at ?? undefined;\n const authorization =\n row.authorization_json !== null && row.reservation_id !== null\n ? {\n ...(JSON.parse(row.authorization_json) as NonNullable<Transaction['authorization']>),\n reservationId: row.reservation_id,\n }\n : undefined;\n return {\n id: row.id,\n requestId: row.request_id,\n resourceId: row.resource_id,\n protocol: row.protocol as Transaction['protocol'],\n state: transactionState(\n {\n payment: { status },\n ...(backend !== undefined ? { backend } : {}),\n ...(completedAt !== undefined ? { completedAt } : {}),\n },\n now,\n ),\n payment: {\n provider: row.provider as Transaction['payment']['provider'],\n attemptId: row.attempt_id,\n replayKey: row.replay_key,\n status,\n amount: row.payment_amount,\n currency: row.payment_currency,\n ...(row.network !== null ? { network: row.network } : {}),\n ...(row.asset !== null ? { asset: row.asset } : {}),\n ...(row.payment_payee !== null ? { payee: row.payment_payee } : {}),\n ...(row.payment_external_reference !== null\n ? { externalReference: row.payment_external_reference }\n : {}),\n ...(row.locator_json !== null\n ? { locator: JSON.parse(row.locator_json) as Record<string, unknown> }\n : {}),\n ...(row.settling_at !== null ? { settlingAt: row.settling_at } : {}),\n ...(row.expires_at !== null ? { expiresAt: row.expires_at } : {}),\n },\n ...(authorization !== undefined ? { authorization } : {}),\n ...(backend !== undefined ? { backend } : {}),\n ...(row.receipt_id !== null ? { receiptId: row.receipt_id } : {}),\n ...(row.input_digest !== null ? { inputDigest: row.input_digest } : {}),\n configDigest: row.config_digest,\n ...(row.persistence_failures_json !== null\n ? { persistenceFailures: JSON.parse(row.persistence_failures_json) as string[] }\n : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n ...(completedAt !== undefined ? { completedAt } : {}),\n };\n}\n","/**\n * Receipt-store schema migrations. `PRAGMA user_version` records the applied\n * version, so a restart against an existing file keeps its data.\n */\nimport type Database from 'better-sqlite3';\n\ninterface Migration {\n readonly version: number;\n up(db: Database.Database): void;\n}\n\nconst MIGRATIONS: readonly Migration[] = [\n {\n version: 1,\n up(db) {\n db.exec(`\n CREATE TABLE IF NOT EXISTS receipts (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n request_id TEXT NOT NULL,\n resource_id TEXT NOT NULL,\n payment_json TEXT,\n delivered_at TEXT NOT NULL,\n backend_status INTEGER NOT NULL,\n duration_ms INTEGER,\n protocol TEXT,\n metadata_json TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_receipts_request_id ON receipts(request_id);\n CREATE INDEX IF NOT EXISTS idx_receipts_delivered_at ON receipts(delivered_at);\n\n CREATE TABLE IF NOT EXISTS events (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n type TEXT NOT NULL,\n request_id TEXT NOT NULL,\n resource_id TEXT,\n at TEXT NOT NULL,\n adapter TEXT,\n payment_provider TEXT,\n duration_ms INTEGER,\n status TEXT,\n data_json TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_events_request_id ON events(request_id);\n CREATE INDEX IF NOT EXISTS idx_events_at ON events(at);\n\n CREATE TABLE IF NOT EXISTS payment_attempts (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n request_id TEXT NOT NULL,\n resource_id TEXT NOT NULL,\n provider TEXT NOT NULL,\n replay_key TEXT NOT NULL UNIQUE,\n status TEXT NOT NULL,\n amount TEXT NOT NULL,\n currency TEXT NOT NULL,\n payer TEXT,\n payee TEXT,\n external_reference TEXT,\n rejection_reason TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_payment_attempts_request_id ON payment_attempts(request_id);\n CREATE INDEX IF NOT EXISTS idx_payment_attempts_created_at ON payment_attempts(created_at);\n `);\n },\n },\n {\n version: 2,\n up(db) {\n // Not folded into v1: an existing database keeps its rows, and a receipt\n // written before this column has no authorization to record\n db.exec(`ALTER TABLE receipts ADD COLUMN authorization_json TEXT;`);\n },\n },\n {\n version: 3,\n up(db) {\n // Archive rejected attempts before reclaim overwrites them. replay_key\n // is not unique here: one authorization can be rejected and reclaimed\n // repeatedly.\n db.exec(`\n CREATE TABLE IF NOT EXISTS payment_attempt_history (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL,\n request_id TEXT NOT NULL,\n resource_id TEXT NOT NULL,\n provider TEXT NOT NULL,\n replay_key TEXT NOT NULL,\n status TEXT NOT NULL,\n amount TEXT NOT NULL,\n currency TEXT NOT NULL,\n payer TEXT,\n payee TEXT,\n external_reference TEXT,\n rejection_reason TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n superseded_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_payment_attempt_history_replay_key ON payment_attempt_history(replay_key);\n CREATE INDEX IF NOT EXISTS idx_payment_attempt_history_request_id ON payment_attempt_history(request_id);\n `);\n },\n },\n {\n version: 4,\n up(db) {\n // Join payment status from the attempt through attempt_id; cache only\n // the derived transaction state for filtering. New columns on existing\n // rows remain NULL, and no journal entries are backfilled.\n db.exec(`\n CREATE TABLE IF NOT EXISTS transactions (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n request_id TEXT NOT NULL,\n resource_id TEXT NOT NULL,\n protocol TEXT NOT NULL,\n state TEXT NOT NULL,\n provider TEXT NOT NULL,\n attempt_id TEXT NOT NULL,\n replay_key TEXT NOT NULL,\n network TEXT,\n asset TEXT,\n authorization_json TEXT,\n reservation_id TEXT,\n locator_json TEXT,\n expires_at TEXT,\n input_digest TEXT,\n config_digest TEXT NOT NULL,\n settling_at TEXT,\n backend_dispatched_at TEXT,\n backend_deadline_at TEXT,\n backend_status INTEGER,\n backend_error_code TEXT,\n receipt_id TEXT,\n persistence_failures_json TEXT,\n completed_at TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_transactions_state_seq ON transactions(state, seq);\n CREATE INDEX IF NOT EXISTS idx_transactions_request_id ON transactions(request_id);\n CREATE INDEX IF NOT EXISTS idx_transactions_replay_key ON transactions(replay_key);\n\n ALTER TABLE receipts ADD COLUMN transaction_id TEXT;\n ALTER TABLE events ADD COLUMN transaction_id TEXT;\n ALTER TABLE payment_attempts ADD COLUMN transaction_id TEXT;\n ALTER TABLE payment_attempts ADD COLUMN resolution_json TEXT;\n ALTER TABLE payment_attempt_history ADD COLUMN transaction_id TEXT;\n ALTER TABLE payment_attempt_history ADD COLUMN resolution_json TEXT;\n CREATE INDEX IF NOT EXISTS idx_payment_attempt_history_id ON payment_attempt_history(id);\n `);\n },\n },\n];\n\n/** Target schema version: the version of the last migration */\nexport const SCHEMA_VERSION = MIGRATIONS.at(-1)?.version ?? 0;\n\n/**\n * Apply pending migrations through `target`, defaulting to {@link SCHEMA_VERSION},\n * with one transaction per migration. At or above the target version, read\n * `PRAGMA user_version` without changing the database.\n */\nexport function migrate(db: Database.Database, target = SCHEMA_VERSION): void {\n const current = db.pragma('user_version', { simple: true }) as number;\n for (const migration of MIGRATIONS) {\n if (migration.version <= current || migration.version > target) continue;\n const apply = db.transaction(() => {\n migration.up(db);\n db.pragma(`user_version = ${migration.version}`);\n });\n apply();\n }\n}\n","/**\n * SQLite implementation of the frozen ReceiptStore interface: a thin layer\n * over statements prepared once at open, with no ORM\n */\nimport { randomUUID } from 'node:crypto';\nimport { accessSync, constants as fsConstants } from 'node:fs';\nimport {\n type AdapterDescriptor,\n type AdapterHealth,\n type Clock,\n CommerceError,\n type CommerceEvent,\n type CommerceReceipt,\n type IdGenerator,\n type IsoTimestamp,\n type ListOptions,\n type Logger,\n NOOP_LOGGER,\n type PaymentAttempt,\n type PaymentAttemptListOptions,\n type PaymentAttemptReservation,\n type PaymentAttemptResolveRequest,\n type PaymentAttemptUpdate,\n type ReceiptListOptions,\n type ReceiptStore,\n systemClock,\n type Transaction,\n type TransactionListOptions,\n type TransactionPage,\n type TransactionStep,\n toCommerceError,\n transactionState,\n} from '../../core';\nimport { PACKAGE_VERSION } from '../../version';\nimport { openSqliteDatabase } from '../sqlite';\nimport { redact } from './redact';\nimport {\n type EventRow,\n eventToRow,\n type PaymentAttemptRow,\n type ReceiptRow,\n receiptToRow,\n rowToEvent,\n rowToPaymentAttempt,\n rowToReceipt,\n rowToTransaction,\n type TransactionRow,\n} from './rows';\nimport { migrate, SCHEMA_VERSION } from './schema';\n\nconst DEFAULT_LIST_LIMIT = 50;\n/**\n * Ceiling on every list query, enforced in the store so every caller is\n * covered, not only the HTTP route. SQLite reads a negative `LIMIT` as no\n * limit, so `{ limit: -1 }` would otherwise return the whole table.\n */\nconst MAX_LIST_LIMIT = 500;\n\n// Clamps to a whole number in [1, MAX_LIST_LIMIT]. An absent or non-finite\n// limit (NaN, Infinity) takes the default, whether or not the caller filtered\n// it first.\nfunction clampListLimit(limit: number | undefined): number {\n const base = limit !== undefined && Number.isFinite(limit) ? limit : DEFAULT_LIST_LIMIT;\n return Math.min(Math.max(Math.trunc(base), 1), MAX_LIST_LIMIT);\n}\n\n// Non-2xx statuses, including 0 (unknown), are undelivered, matching\n// deliveryResult() in ReceiptList.tsx. NOT NULL prevents NULL comparisons from\n// hiding rows.\nconst UNDELIVERED = 'backend_status NOT BETWEEN 200 AND 299';\n\n// Statuses needing reconciliation; see ReceiptStore.countUncertainPaymentAttempts\nconst UNCERTAIN = \"status IN ('settlement-uncertain', 'failed')\";\n\n// Join each transaction with its payment attempt. Reclaim replaces the live\n// attempt's ID and archives the old row, so earlier transactions read their\n// attempt from payment_attempt_history.\nconst TRANSACTION_SELECT = `\n SELECT t.*,\n COALESCE(a.status, h.status) AS payment_status,\n COALESCE(a.amount, h.amount) AS payment_amount,\n COALESCE(a.currency, h.currency) AS payment_currency,\n COALESCE(a.payee, h.payee) AS payment_payee,\n COALESCE(a.external_reference, h.external_reference) AS payment_external_reference\n FROM transactions t\n LEFT JOIN payment_attempts a ON a.id = t.attempt_id\n LEFT JOIN payment_attempt_history h ON a.id IS NULL AND h.seq = (\n SELECT MAX(seq) FROM payment_attempt_history\n WHERE replay_key = t.replay_key AND id = t.attempt_id\n )`;\n\n// Rows without completedAt can change state when backend.deadlineAt passes.\n// Include them as filter candidates; transactionState recomputes their state\n// on read without a database write.\nconst TIME_DEPENDENT = 't.backend_deadline_at IS NOT NULL AND t.completed_at IS NULL';\nconst RESOLVED_STATES = ['delivered', 'payment-failed'] as const;\n\nconst CURSOR_PATTERN = /^[1-9][0-9]{0,15}$/;\n\nexport interface SqliteReceiptStoreOptions {\n /** File path, or ':memory:' for tests. Parent directory is created if missing */\n readonly path: string;\n readonly logger?: Logger;\n readonly clock?: Clock;\n readonly ids?: IdGenerator;\n}\n\nconst defaultIds: IdGenerator = {\n next: (prefix) => (prefix !== undefined ? `${prefix}_${randomUUID()}` : randomUUID()),\n};\n\nfunction isUniqueConstraintOn(err: unknown, column: string): boolean {\n return (\n err instanceof Error &&\n 'code' in err &&\n (err as { code?: unknown }).code === 'SQLITE_CONSTRAINT_UNIQUE' &&\n err.message.includes(column)\n );\n}\n\nexport function createSqliteReceiptStore(options: SqliteReceiptStoreOptions): ReceiptStore {\n const logger = options.logger ?? NOOP_LOGGER;\n const clock = options.clock ?? systemClock;\n const ids = options.ids ?? defaultIds;\n const path = options.path;\n const isFileBacked = path !== ':memory:';\n\n // Shared with the ACP idempotency and AP2 replay stores, so the permission\n // hardening has one definition\n const db = openSqliteDatabase({ path, label: 'Receipt database', logger });\n migrate(db);\n\n const insertReceiptStmt = db.prepare(\n `INSERT INTO receipts (id, request_id, resource_id, payment_json, delivered_at, backend_status, duration_ms, protocol, metadata_json, authorization_json, transaction_id)\n VALUES (@id, @request_id, @resource_id, @payment_json, @delivered_at, @backend_status, @duration_ms, @protocol, @metadata_json, @authorization_json, @transaction_id)`,\n );\n const getReceiptStmt = db.prepare<[string], ReceiptRow>('SELECT * FROM receipts WHERE id = ?');\n const listReceiptsStmt = db.prepare<[number], ReceiptRow>(\n 'SELECT * FROM receipts ORDER BY delivered_at DESC, seq DESC LIMIT ?',\n );\n const listReceiptsByRequestStmt = db.prepare<[string, number], ReceiptRow>(\n 'SELECT * FROM receipts WHERE request_id = ? ORDER BY delivered_at DESC, seq DESC LIMIT ?',\n );\n const listUndeliveredReceiptsStmt = db.prepare<[number], ReceiptRow>(\n `SELECT * FROM receipts WHERE ${UNDELIVERED} ORDER BY delivered_at DESC, seq DESC LIMIT ?`,\n );\n const listUndeliveredReceiptsByRequestStmt = db.prepare<[string, number], ReceiptRow>(\n `SELECT * FROM receipts WHERE request_id = ? AND ${UNDELIVERED} ORDER BY delivered_at DESC, seq DESC LIMIT ?`,\n );\n const countReceiptsStmt = db.prepare<[], { count: number }>(\n 'SELECT COUNT(*) AS count FROM receipts',\n );\n const countUndeliveredReceiptsStmt = db.prepare<[], { count: number }>(\n `SELECT COUNT(*) AS count FROM receipts WHERE ${UNDELIVERED}`,\n );\n\n const insertEventStmt = db.prepare(\n `INSERT INTO events (id, type, request_id, resource_id, at, adapter, payment_provider, duration_ms, status, data_json, transaction_id)\n VALUES (@id, @type, @request_id, @resource_id, @at, @adapter, @payment_provider, @duration_ms, @status, @data_json, @transaction_id)`,\n );\n const listEventsStmt = db.prepare<[number], EventRow>(\n 'SELECT * FROM events ORDER BY at DESC, seq DESC LIMIT ?',\n );\n const listEventsByRequestStmt = db.prepare<[string, number], EventRow>(\n 'SELECT * FROM events WHERE request_id = ? ORDER BY at DESC, seq DESC LIMIT ?',\n );\n\n const insertPaymentAttemptStmt = db.prepare<Record<string, unknown>, PaymentAttemptRow>(\n `INSERT INTO payment_attempts (id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, transaction_id)\n VALUES (@id, @request_id, @resource_id, @provider, @replay_key, @status, @amount, @currency, @payer, @payee, @external_reference, @rejection_reason, @created_at, @updated_at, @transaction_id)\n RETURNING *`,\n );\n // Keeps the rejected row a reclaim is about to overwrite\n const archiveRejectedAttemptStmt = db.prepare(\n `INSERT INTO payment_attempt_history (id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, superseded_at, transaction_id, resolution_json)\n SELECT id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, @created_at, transaction_id, resolution_json\n FROM payment_attempts\n WHERE replay_key = @replay_key AND provider = @provider AND status = 'rejected'`,\n );\n const reclaimPaymentAttemptStmt = db.prepare<Record<string, unknown>, PaymentAttemptRow>(\n `UPDATE payment_attempts SET id = @id, request_id = @request_id, resource_id = @resource_id,\n status = 'reserved', amount = @amount, currency = @currency,\n payer = @payer, payee = @payee, external_reference = NULL, rejection_reason = NULL,\n created_at = @created_at, updated_at = @updated_at,\n transaction_id = @transaction_id, resolution_json = NULL\n WHERE replay_key = @replay_key AND provider = @provider AND status = 'rejected'\n RETURNING *`,\n );\n // Omitted fields bind as NULL, so COALESCE preserves stored values. A\n // status-only update must retain external_reference: it may identify funds\n // that already moved. Clearing a field would need an explicit sentinel\n // rather than undefined. Rows with recorded resolutions reject later\n // pipeline updates.\n const updatePaymentAttemptStmt = db.prepare(\n `UPDATE payment_attempts SET status = @status,\n external_reference = COALESCE(@external_reference, external_reference),\n rejection_reason = COALESCE(@rejection_reason, rejection_reason),\n updated_at = @updated_at\n WHERE replay_key = @replay_key AND resolution_json IS NULL`,\n );\n const listPaymentAttemptsStmt = db.prepare<[number], PaymentAttemptRow>(\n 'SELECT * FROM payment_attempts ORDER BY created_at DESC, seq DESC LIMIT ?',\n );\n const listPaymentAttemptsByRequestStmt = db.prepare<[string, number], PaymentAttemptRow>(\n 'SELECT * FROM payment_attempts WHERE request_id = ? ORDER BY created_at DESC, seq DESC LIMIT ?',\n );\n const listUncertainPaymentAttemptsStmt = db.prepare<[number], PaymentAttemptRow>(\n `SELECT * FROM payment_attempts WHERE ${UNCERTAIN} ORDER BY created_at DESC, seq DESC LIMIT ?`,\n );\n const listUncertainPaymentAttemptsByRequestStmt = db.prepare<[string, number], PaymentAttemptRow>(\n `SELECT * FROM payment_attempts WHERE request_id = ? AND ${UNCERTAIN} ORDER BY created_at DESC, seq DESC LIMIT ?`,\n );\n const countUncertainPaymentAttemptsStmt = db.prepare<[], { count: number }>(\n `SELECT COUNT(*) AS count FROM payment_attempts WHERE ${UNCERTAIN}`,\n );\n const paymentAttemptStatusStmt = db.prepare<\n [string],\n Pick<PaymentAttemptRow, 'id' | 'status' | 'resolution_json'>\n >('SELECT id, status, resolution_json FROM payment_attempts WHERE replay_key = ?');\n const insertTransactionStmt = db.prepare(\n `INSERT INTO transactions (id, request_id, resource_id, protocol, state, provider, attempt_id, replay_key, network, asset, authorization_json, reservation_id, locator_json, expires_at, input_digest, config_digest, created_at, updated_at)\n VALUES (@id, @request_id, @resource_id, @protocol, @state, @provider, @attempt_id, @replay_key, @network, @asset, @authorization_json, @reservation_id, @locator_json, @expires_at, @input_digest, @config_digest, @created_at, @created_at)`,\n );\n const getTransactionStmt = db.prepare<[string], TransactionRow>(\n `${TRANSACTION_SELECT} WHERE t.id = ?`,\n );\n // After reclaim, purchases can share a replay key but refer to different\n // attempts. Match attempt_id to refresh only the current attempt's transactions;\n // the replay_key condition also permits an indexed lookup.\n const transactionsOfAttemptStmt = db.prepare<[string], { id: string }>(\n `SELECT t.id FROM payment_attempts a\n JOIN transactions t ON t.replay_key = a.replay_key AND t.attempt_id = a.id\n WHERE a.replay_key = ?`,\n );\n const transactionsOfAttemptIdStmt = db.prepare<[string], { id: string }>(\n `SELECT t.id FROM payment_attempts a\n JOIN transactions t ON t.replay_key = a.replay_key AND t.attempt_id = a.id\n WHERE a.id = ?`,\n );\n const setTransactionStateStmt = db.prepare('UPDATE transactions SET state = ? WHERE id = ?');\n const stepStmts = {\n settling: db.prepare(\n 'UPDATE transactions SET settling_at = @at, updated_at = @at WHERE id = @id',\n ),\n // A transaction without an authorization has no hold to record\n hold: db.prepare(\n `UPDATE transactions SET authorization_json = json_set(authorization_json, '$.hold', @hold), updated_at = @at\n WHERE id = @id AND authorization_json IS NOT NULL`,\n ),\n 'backend-dispatched': db.prepare(\n `UPDATE transactions SET backend_dispatched_at = @at, backend_deadline_at = @deadline_at, updated_at = @at\n WHERE id = @id`,\n ),\n // Preserve a backend result even when the dispatch step was not saved:\n // use the result time for missing dispatch and deadline timestamps\n 'backend-result': db.prepare(\n `UPDATE transactions SET backend_status = @status, backend_error_code = @error_code,\n backend_dispatched_at = COALESCE(backend_dispatched_at, @at),\n backend_deadline_at = COALESCE(backend_deadline_at, @at), updated_at = @at\n WHERE id = @id`,\n ),\n receipt: db.prepare(\n 'UPDATE transactions SET receipt_id = @receipt_id, updated_at = @at WHERE id = @id',\n ),\n completed: db.prepare(\n `UPDATE transactions SET completed_at = @at, persistence_failures_json = @persistence_failures_json, updated_at = @at\n WHERE id = @id`,\n ),\n } satisfies Record<TransactionStep['kind'], unknown>;\n const getPaymentAttemptStmt = db.prepare<[string], PaymentAttemptRow>(\n 'SELECT * FROM payment_attempts WHERE id = ?',\n );\n const getArchivedPaymentAttemptStmt = db.prepare<[string], PaymentAttemptRow>(\n 'SELECT * FROM payment_attempt_history WHERE id = ? ORDER BY seq DESC LIMIT 1',\n );\n // Check status and missing resolution in the UPDATE so only one resolver\n // succeeds. Only reconciliation may replace the attempt's reference; manual\n // references belong in resolution_json.\n const resolvePaymentAttemptStmt = db.prepare(\n `UPDATE payment_attempts SET status = @status,\n rejection_reason = COALESCE(@rejection_reason, rejection_reason),\n external_reference = COALESCE(@external_reference, external_reference),\n resolution_json = @resolution_json, updated_at = @updated_at\n WHERE id = @id AND status = @expected_status AND resolution_json IS NULL`,\n );\n\n // Refresh the cached state inside the transaction that updated its inputs\n function refreshTransactionState(id: string, now: IsoTimestamp): void {\n const row = getTransactionStmt.get(id);\n if (row !== undefined) setTransactionStateStmt.run(rowToTransaction(row, now).state, id);\n }\n\n // IMMEDIATE locks before checking the replay key. Once a rejected attempt is\n // reserved again, a competing claim hits the unique replay-key constraint.\n // Journal creation shares the transaction, so a failure also rolls back the\n // attempt insert or reclaim.\n const reserve = db.transaction(\n (\n reservation: PaymentAttemptReservation,\n params: Record<string, unknown>,\n ): PaymentAttemptRow => {\n let row: PaymentAttemptRow | undefined;\n // Reclaim only if the rail permits reusing an authorization whose\n // attempt was rejected. NEAR client retries reuse the access key's\n // next nonce and therefore the replay key.\n if (reservation.reclaimRejected === true) {\n archiveRejectedAttemptStmt.run(params);\n row = reclaimPaymentAttemptStmt.get(params);\n }\n // RETURNING on a successful insert always yields the inserted row\n row ??= insertPaymentAttemptStmt.get({\n ...params,\n status: 'reserved' satisfies PaymentAttempt['status'],\n external_reference: null,\n rejection_reason: null,\n }) as PaymentAttemptRow;\n const txn = reservation.transaction;\n if (txn !== undefined) {\n const { reservationId, ...authorization } = txn.authorization ?? {};\n insertTransactionStmt.run({\n id: txn.id,\n request_id: reservation.requestId,\n resource_id: reservation.resourceId,\n protocol: txn.protocol,\n state: transactionState({ payment: { status: 'reserved' } }, row.created_at),\n provider: reservation.provider,\n attempt_id: row.id,\n replay_key: reservation.replayKey,\n network: txn.network ?? null,\n asset: txn.asset ?? null,\n authorization_json:\n reservationId !== undefined\n ? JSON.stringify(redact({ ...authorization, hold: 'reserved' }))\n : null,\n reservation_id: reservationId ?? null,\n locator_json: txn.locator !== undefined ? JSON.stringify(redact(txn.locator)) : null,\n expires_at: txn.expiresAt ?? null,\n input_digest: txn.inputDigest ?? null,\n config_digest: txn.configDigest,\n created_at: row.created_at,\n });\n }\n return row;\n },\n );\n const updateAttempt = db.transaction((update: PaymentAttemptUpdate, now: IsoTimestamp) => {\n const { changes } = updatePaymentAttemptStmt.run({\n replay_key: update.replayKey,\n status: update.status,\n external_reference: update.externalReference ?? null,\n rejection_reason: update.rejectionReason ?? null,\n updated_at: now,\n });\n for (const { id } of transactionsOfAttemptStmt.all(update.replayKey)) {\n refreshTransactionState(id, now);\n }\n return changes;\n });\n const recordStep = db.transaction((step: TransactionStep): number => {\n const base = { id: step.transactionId, at: step.at };\n let changes: number;\n switch (step.kind) {\n case 'settling':\n changes = stepStmts.settling.run(base).changes;\n break;\n case 'hold':\n changes = stepStmts.hold.run({ ...base, hold: step.hold }).changes;\n break;\n case 'backend-dispatched':\n changes = stepStmts['backend-dispatched'].run({\n ...base,\n deadline_at: step.deadlineAt,\n }).changes;\n break;\n case 'backend-result':\n changes = stepStmts['backend-result'].run({\n ...base,\n status: step.status,\n error_code: step.errorCode ?? null,\n }).changes;\n break;\n case 'receipt':\n changes = stepStmts.receipt.run({ ...base, receipt_id: step.receiptId }).changes;\n break;\n case 'completed':\n changes = stepStmts.completed.run({\n ...base,\n persistence_failures_json:\n step.persistenceFailures !== undefined && step.persistenceFailures.length > 0\n ? JSON.stringify(step.persistenceFailures)\n : null,\n }).changes;\n break;\n }\n if (changes > 0) refreshTransactionState(step.transactionId, step.at);\n return changes;\n });\n const resolveAttempt = db.transaction(\n (request: PaymentAttemptResolveRequest, now: IsoTimestamp): boolean => {\n const { changes } = resolvePaymentAttemptStmt.run({\n id: request.attemptId,\n expected_status: request.expectedStatus,\n status: request.status,\n rejection_reason: request.rejectionReason ?? null,\n // An operator's reference is unverified; it stays in resolution_json only\n external_reference:\n request.resolution.method === 'reconcile'\n ? (request.resolution.externalReference ?? null)\n : null,\n resolution_json: JSON.stringify(redact(request.resolution)),\n updated_at: now,\n });\n if (changes === 0) return false;\n for (const { id } of transactionsOfAttemptIdStmt.all(request.attemptId)) {\n refreshTransactionState(id, now);\n }\n return true;\n },\n );\n\n let closed = false;\n\n const descriptor: AdapterDescriptor = {\n name: 'sqlite-receipt-store',\n kind: 'storage',\n implementationVersion: PACKAGE_VERSION,\n supportedSpec: `sqlite-schema-v${SCHEMA_VERSION}`,\n capabilities: ['receipts', 'events', 'payment-attempts', 'transactions'],\n status: 'stable',\n };\n\n const store: ReceiptStore = {\n async init(): Promise<void> {\n // Migration already ran synchronously at construction. This check keeps\n // init() a real, idempotent gate for callers that await it first.\n const version = db.pragma('user_version', { simple: true }) as number;\n if (version !== SCHEMA_VERSION) {\n throw new CommerceError(\n 'STORAGE_ERROR',\n `Receipt store schema version mismatch (found ${version}, expected ${SCHEMA_VERSION})`,\n );\n }\n },\n\n async appendEvent(event: CommerceEvent): Promise<void> {\n try {\n insertEventStmt.run(eventToRow(event));\n } catch (err) {\n logger.warn(\n {\n err: err instanceof Error ? err.message : String(err),\n eventId: event.id,\n requestId: event.requestId,\n },\n 'receipt-store: failed to persist commerce event',\n );\n }\n },\n\n async reservePaymentAttempt(reservation: PaymentAttemptReservation): Promise<PaymentAttempt> {\n const now = clock.nowIso();\n const params = {\n id: ids.next('attempt'),\n request_id: reservation.requestId,\n resource_id: reservation.resourceId,\n provider: reservation.provider,\n replay_key: reservation.replayKey,\n amount: reservation.amount,\n currency: reservation.currency,\n payer: reservation.payer ?? null,\n payee: reservation.payee ?? null,\n created_at: now,\n updated_at: now,\n transaction_id: reservation.transaction?.id ?? null,\n };\n let row: PaymentAttemptRow;\n try {\n row = reserve.immediate(reservation, params);\n } catch (err) {\n if (isUniqueConstraintOn(err, 'payment_attempts.replay_key')) {\n // Report the existing status so the pipeline can choose a replay response\n const existing = paymentAttemptStatusStmt.get(reservation.replayKey);\n throw new CommerceError(\n 'PAYMENT_REPLAYED',\n `Payment authorization has already been used (replayKey=${reservation.replayKey})`,\n {\n requestId: reservation.requestId,\n resourceId: reservation.resourceId,\n ...(existing !== undefined ? { details: { attemptStatus: existing.status } } : {}),\n },\n );\n }\n throw toCommerceError(err, 'STORAGE_ERROR', 'Failed to reserve payment attempt');\n }\n return rowToPaymentAttempt(row);\n },\n\n async updatePaymentAttempt(update: PaymentAttemptUpdate): Promise<void> {\n try {\n if (updateAttempt(update, clock.nowIso()) === 0) {\n const existing = paymentAttemptStatusStmt.get(update.replayKey);\n if (existing?.resolution_json != null) {\n // Log the rejected update at warn level while preserving the resolution\n logger.warn(\n {\n attemptId: existing.id,\n resolvedStatus: existing.status,\n status: update.status,\n ...(update.externalReference !== undefined\n ? { externalReference: update.externalReference }\n : {}),\n },\n 'receipt-store: payment attempt already resolved; update not applied',\n );\n throw new CommerceError(\n 'STORAGE_ERROR',\n `Payment attempt ${existing.id} is already resolved; the update was not applied`,\n );\n }\n throw new CommerceError(\n 'STORAGE_ERROR',\n `No payment attempt found for replayKey=${update.replayKey}`,\n );\n }\n } catch (err) {\n throw toCommerceError(err, 'STORAGE_ERROR', 'Failed to update payment attempt');\n }\n },\n\n async saveReceipt(receipt: CommerceReceipt): Promise<void> {\n try {\n insertReceiptStmt.run(receiptToRow(receipt));\n } catch (err) {\n throw toCommerceError(err, 'STORAGE_ERROR', 'Failed to save receipt');\n }\n },\n\n async getReceipt(id: string): Promise<CommerceReceipt | undefined> {\n const row = getReceiptStmt.get(id);\n return row !== undefined ? rowToReceipt(row) : undefined;\n },\n\n async listReceipts(listOptions: ReceiptListOptions = {}): Promise<readonly CommerceReceipt[]> {\n const limit = clampListLimit(listOptions.limit);\n const undelivered = listOptions.undelivered === true;\n const rows =\n listOptions.requestId !== undefined\n ? (undelivered ? listUndeliveredReceiptsByRequestStmt : listReceiptsByRequestStmt).all(\n listOptions.requestId,\n limit,\n )\n : (undelivered ? listUndeliveredReceiptsStmt : listReceiptsStmt).all(limit);\n return rows.map(rowToReceipt);\n },\n\n // Exact total, not bounded by the listReceipts clamp (see ReceiptStore.countReceipts)\n async countReceipts(): Promise<number> {\n return countReceiptsStmt.get()?.count ?? 0;\n },\n\n // Exact count of non-2xx receipts (see ReceiptStore.countUndeliveredReceipts)\n async countUndeliveredReceipts(): Promise<number> {\n return countUndeliveredReceiptsStmt.get()?.count ?? 0;\n },\n\n async countUncertainPaymentAttempts(): Promise<number> {\n return countUncertainPaymentAttemptsStmt.get()?.count ?? 0;\n },\n\n async listEvents(listOptions: ListOptions = {}): Promise<readonly CommerceEvent[]> {\n const limit = clampListLimit(listOptions.limit);\n const rows =\n listOptions.requestId !== undefined\n ? listEventsByRequestStmt.all(listOptions.requestId, limit)\n : listEventsStmt.all(limit);\n return rows.map(rowToEvent);\n },\n\n async listPaymentAttempts(\n listOptions: PaymentAttemptListOptions = {},\n ): Promise<readonly PaymentAttempt[]> {\n const limit = clampListLimit(listOptions.limit);\n const uncertain = listOptions.uncertain === true;\n const rows =\n listOptions.requestId !== undefined\n ? (uncertain\n ? listUncertainPaymentAttemptsByRequestStmt\n : listPaymentAttemptsByRequestStmt\n ).all(listOptions.requestId, limit)\n : (uncertain ? listUncertainPaymentAttemptsStmt : listPaymentAttemptsStmt).all(limit);\n return rows.map(rowToPaymentAttempt);\n },\n\n async recordTransactionStep(step: TransactionStep): Promise<void> {\n try {\n if (recordStep(step) === 0) {\n throw new CommerceError(\n 'STORAGE_ERROR',\n step.kind === 'hold'\n ? `No transaction with an authorization hold found for transactionId=${step.transactionId}`\n : `No transaction found for transactionId=${step.transactionId}`,\n );\n }\n } catch (err) {\n throw toCommerceError(err, 'STORAGE_ERROR', 'Failed to record transaction step');\n }\n },\n\n async getTransaction(id: string): Promise<Transaction | undefined> {\n const row = getTransactionStmt.get(id);\n return row !== undefined ? rowToTransaction(row, clock.nowIso()) : undefined;\n },\n\n async listTransactions(listOptions: TransactionListOptions = {}): Promise<TransactionPage> {\n const { cursor, state, unresolved, pendingHold, requestId } = listOptions;\n if (cursor !== undefined && !CURSOR_PATTERN.test(cursor)) {\n throw new CommerceError('INPUT_INVALID', 'Malformed transaction list cursor');\n }\n const limit = clampListLimit(listOptions.limit);\n const where: string[] = [];\n const params: unknown[] = [];\n if (cursor !== undefined) {\n where.push('t.seq < ?');\n params.push(Number(cursor));\n }\n if (requestId !== undefined) {\n where.push('t.request_id = ?');\n params.push(requestId);\n }\n if (state !== undefined) {\n where.push(`(t.state = ? OR (${TIME_DEPENDENT}))`);\n params.push(state);\n }\n if (pendingHold === true) {\n where.push(\"json_extract(t.authorization_json, '$.hold') IN ('reserved', 'uncertain')\");\n }\n if (unresolved === true) {\n where.push(`t.state NOT IN (${RESOLVED_STATES.map(() => '?').join(', ')})`);\n params.push(...RESOLVED_STATES);\n }\n const rows = db\n .prepare<unknown[], TransactionRow>(\n `${TRANSACTION_SELECT}${where.length > 0 ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY t.seq DESC LIMIT ?`,\n )\n .all(...params, limit);\n const now = clock.nowIso();\n const resolved: readonly string[] = RESOLVED_STATES;\n const transactions = rows\n .map((row) => rowToTransaction(row, now))\n .filter(\n (t) =>\n (state === undefined || t.state === state) &&\n (unresolved !== true || !resolved.includes(t.state)),\n );\n // Base the cursor on candidate rows, including any removed by the filter\n const last = rows.at(-1);\n return {\n transactions,\n ...(rows.length === limit && last !== undefined ? { nextCursor: String(last.seq) } : {}),\n };\n },\n\n async getPaymentAttempt(id: string): Promise<PaymentAttempt | undefined> {\n const row = getPaymentAttemptStmt.get(id) ?? getArchivedPaymentAttemptStmt.get(id);\n return row !== undefined ? rowToPaymentAttempt(row) : undefined;\n },\n\n async resolvePaymentAttempt(request: PaymentAttemptResolveRequest): Promise<boolean> {\n try {\n return resolveAttempt(request, clock.nowIso());\n } catch (err) {\n throw toCommerceError(err, 'STORAGE_ERROR', 'Failed to resolve payment attempt');\n }\n },\n\n descriptor,\n\n async health(): Promise<AdapterHealth> {\n // `detail` comes from a fixed vocabulary, never from a caught error: an\n // accessSync error carries an absolute host path, e.g. \"EACCES:\n // permission denied, access '/workspace/data/receipts.sqlite'\". The raw\n // message goes to the logger at warn.\n const startedAt = clock.monotonicMs();\n if (closed) {\n return { status: 'fail', detail: 'store-unavailable', checkedAt: clock.nowIso() };\n }\n if (isFileBacked) {\n try {\n accessSync(path, fsConstants.W_OK);\n } catch (err) {\n logger.warn(\n { err: err instanceof Error ? err.message : String(err) },\n 'receipt-store: health check failed (store not writable)',\n );\n return {\n status: 'fail',\n detail: 'store-unwritable',\n checkedAt: clock.nowIso(),\n durationMs: clock.monotonicMs() - startedAt,\n };\n }\n }\n try {\n const version = db.pragma('user_version', { simple: true }) as number;\n if (version !== SCHEMA_VERSION) {\n logger.warn(\n { found: version, expected: SCHEMA_VERSION },\n 'receipt-store: health check failed (schema version mismatch)',\n );\n return {\n status: 'fail',\n detail: 'store-schema-mismatch',\n checkedAt: clock.nowIso(),\n durationMs: clock.monotonicMs() - startedAt,\n };\n }\n return {\n status: 'pass',\n detail: `sqlite schema v${SCHEMA_VERSION} writable`,\n checkedAt: clock.nowIso(),\n durationMs: clock.monotonicMs() - startedAt,\n };\n } catch (err) {\n logger.warn(\n { err: err instanceof Error ? err.message : String(err) },\n 'receipt-store: health check failed (store unavailable)',\n );\n return {\n status: 'fail',\n detail: 'store-unavailable',\n checkedAt: clock.nowIso(),\n durationMs: clock.monotonicMs() - startedAt,\n };\n }\n },\n\n async close(): Promise<void> {\n if (!closed) {\n closed = true;\n db.close();\n }\n },\n };\n\n return store;\n}\n"]}
@@ -0,0 +1,105 @@
1
+ // src/core/errors/codes.ts
2
+ var COMMERCE_ERROR_HTTP_STATUS = {
3
+ CONFIG_INVALID: 500,
4
+ RESOURCE_NOT_FOUND: 404,
5
+ INPUT_INVALID: 400,
6
+ PAYMENT_REQUIRED: 402,
7
+ PAYMENT_INVALID: 402,
8
+ PAYMENT_REPLAYED: 409,
9
+ PAYMENT_PROVIDER_UNAVAILABLE: 503,
10
+ PAYMENT_SETTLEMENT_FAILED: 502,
11
+ // 403, not 402: a 402 tells a client to pay and retry, which cannot fix a
12
+ // missing or rejected mandate, and a client that auto-pays on 402 would be
13
+ // charged for a request that was never going to be delivered
14
+ AUTHORIZATION_REQUIRED: 403,
15
+ AUTHORIZATION_INVALID: 403,
16
+ AUTHORIZATION_REPLAYED: 409,
17
+ AUTHORIZATION_PROVIDER_UNAVAILABLE: 503,
18
+ BACKEND_TIMEOUT: 504,
19
+ BACKEND_ERROR: 502,
20
+ PROTOCOL_UNSUPPORTED: 501,
21
+ GATEWAY_BUSY: 503,
22
+ STORAGE_ERROR: 500,
23
+ INTERNAL_ERROR: 500
24
+ };
25
+ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
26
+ "PAYMENT_PROVIDER_UNAVAILABLE",
27
+ // The verifier or replay store was unreachable, so the mandate got no
28
+ // verdict. The same proof can verify once the outage clears.
29
+ "AUTHORIZATION_PROVIDER_UNAVAILABLE",
30
+ "BACKEND_TIMEOUT",
31
+ // Load shedding is transient: the caller should back off and try again.
32
+ // Clients act on this flag, and leaving it out would make a momentary
33
+ // throttle look like a permanent failure.
34
+ "GATEWAY_BUSY"
35
+ ]);
36
+
37
+ // src/core/errors/commerce-error.ts
38
+ var CommerceError = class _CommerceError extends Error {
39
+ code;
40
+ httpStatus;
41
+ retryable;
42
+ details;
43
+ requestId;
44
+ resourceId;
45
+ constructor(code, message, options = {}) {
46
+ super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
47
+ this.name = "CommerceError";
48
+ this.code = code;
49
+ this.httpStatus = options.httpStatus ?? COMMERCE_ERROR_HTTP_STATUS[code];
50
+ this.retryable = RETRYABLE_ERROR_CODES.has(code);
51
+ if (options.details !== void 0) this.details = options.details;
52
+ if (options.requestId !== void 0) this.requestId = options.requestId;
53
+ if (options.resourceId !== void 0) this.resourceId = options.resourceId;
54
+ Error.captureStackTrace?.(this, _CommerceError);
55
+ }
56
+ /** Non-secret, client-safe representation. `cause` is never included */
57
+ toInfo() {
58
+ return {
59
+ code: this.code,
60
+ message: this.message,
61
+ httpStatus: this.httpStatus,
62
+ retryable: this.retryable,
63
+ ...this.requestId !== void 0 ? { requestId: this.requestId } : {},
64
+ ...this.resourceId !== void 0 ? { resourceId: this.resourceId } : {},
65
+ ...this.details !== void 0 ? { details: this.details } : {}
66
+ };
67
+ }
68
+ };
69
+ function isCommerceError(value) {
70
+ return value instanceof CommerceError;
71
+ }
72
+ function toCommerceError(value, fallbackCode = "INTERNAL_ERROR", fallbackMessage = "Unexpected internal error") {
73
+ if (isCommerceError(value)) return value;
74
+ return new CommerceError(fallbackCode, fallbackMessage, { cause: value });
75
+ }
76
+ function describeError(error) {
77
+ if (error instanceof Error) return { message: error.message, name: error.name };
78
+ return { message: String(error) };
79
+ }
80
+ function redactedErrorText(value) {
81
+ const text = value instanceof Error ? `${value.name}: ${value.message}` : String(value);
82
+ return text.replace(/\b[a-z][a-z\d+.-]*:\/\/[^\s"'<>]+/gi, (match) => {
83
+ try {
84
+ const url = new URL(match);
85
+ return `${url.protocol}//${url.host}`;
86
+ } catch {
87
+ return "[unparseable URL]";
88
+ }
89
+ });
90
+ }
91
+ var MAX_LOGGED_CAUSES = 5;
92
+ function toLogInfo(value) {
93
+ const error = toCommerceError(value);
94
+ const cause = [];
95
+ let current = error.cause;
96
+ while (current !== void 0 && cause.length < MAX_LOGGED_CAUSES) {
97
+ cause.push(redactedErrorText(current));
98
+ current = current instanceof Error ? current.cause : void 0;
99
+ }
100
+ return cause.length > 0 ? { ...error.toInfo(), cause } : error.toInfo();
101
+ }
102
+
103
+ export { CommerceError, describeError, isCommerceError, redactedErrorText, toCommerceError, toLogInfo };
104
+ //# sourceMappingURL=chunk-SYQLJXIU.js.map
105
+ //# sourceMappingURL=chunk-SYQLJXIU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/errors/codes.ts","../../src/core/errors/commerce-error.ts"],"names":[],"mappings":"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","file":"chunk-SYQLJXIU.js","sourcesContent":["/**\n * Typed domain error codes. FROZEN CONTRACT. Protocol adapters map them into\n * their own response format. Never throw a bare string or untyped Error across\n * an area boundary.\n */\nexport const COMMERCE_ERROR_CODES = [\n 'CONFIG_INVALID',\n 'RESOURCE_NOT_FOUND',\n 'INPUT_INVALID',\n 'PAYMENT_REQUIRED',\n 'PAYMENT_INVALID',\n 'PAYMENT_REPLAYED',\n 'PAYMENT_PROVIDER_UNAVAILABLE',\n 'PAYMENT_SETTLEMENT_FAILED',\n 'AUTHORIZATION_REQUIRED',\n 'AUTHORIZATION_INVALID',\n 'AUTHORIZATION_REPLAYED',\n 'AUTHORIZATION_PROVIDER_UNAVAILABLE',\n 'BACKEND_TIMEOUT',\n 'BACKEND_ERROR',\n 'PROTOCOL_UNSUPPORTED',\n 'GATEWAY_BUSY',\n 'STORAGE_ERROR',\n 'INTERNAL_ERROR',\n] as const;\n\nexport type CommerceErrorCode = (typeof COMMERCE_ERROR_CODES)[number];\n\n/**\n * Canonical HTTP status for each error code. Every payment failure maps to a\n * 4xx or 5xx status, never to a delivery.\n */\nexport const COMMERCE_ERROR_HTTP_STATUS: Readonly<Record<CommerceErrorCode, number>> = {\n CONFIG_INVALID: 500,\n RESOURCE_NOT_FOUND: 404,\n INPUT_INVALID: 400,\n PAYMENT_REQUIRED: 402,\n PAYMENT_INVALID: 402,\n PAYMENT_REPLAYED: 409,\n PAYMENT_PROVIDER_UNAVAILABLE: 503,\n PAYMENT_SETTLEMENT_FAILED: 502,\n // 403, not 402: a 402 tells a client to pay and retry, which cannot fix a\n // missing or rejected mandate, and a client that auto-pays on 402 would be\n // charged for a request that was never going to be delivered\n AUTHORIZATION_REQUIRED: 403,\n AUTHORIZATION_INVALID: 403,\n AUTHORIZATION_REPLAYED: 409,\n AUTHORIZATION_PROVIDER_UNAVAILABLE: 503,\n BACKEND_TIMEOUT: 504,\n BACKEND_ERROR: 502,\n PROTOCOL_UNSUPPORTED: 501,\n GATEWAY_BUSY: 503,\n STORAGE_ERROR: 500,\n INTERNAL_ERROR: 500,\n};\n\n/** Codes for which a client may reasonably retry the same request */\nexport const RETRYABLE_ERROR_CODES: ReadonlySet<CommerceErrorCode> = new Set([\n 'PAYMENT_PROVIDER_UNAVAILABLE',\n // The verifier or replay store was unreachable, so the mandate got no\n // verdict. The same proof can verify once the outage clears.\n 'AUTHORIZATION_PROVIDER_UNAVAILABLE',\n 'BACKEND_TIMEOUT',\n // Load shedding is transient: the caller should back off and try again.\n // Clients act on this flag, and leaving it out would make a momentary\n // throttle look like a permanent failure.\n 'GATEWAY_BUSY',\n]);\n","// The single error type crossing area boundaries. FROZEN CONTRACT\nimport { COMMERCE_ERROR_HTTP_STATUS, type CommerceErrorCode, RETRYABLE_ERROR_CODES } from './codes';\n\nexport interface CommerceErrorOptions {\n /** Structured, non-secret detail safe to return to a client */\n readonly details?: Readonly<Record<string, unknown>>;\n readonly cause?: unknown;\n readonly requestId?: string;\n readonly resourceId?: string;\n /** Override the default HTTP status for this code */\n readonly httpStatus?: number;\n}\n\n/** Serializable wire form of a CommerceError */\nexport interface CommerceErrorInfo {\n readonly code: CommerceErrorCode;\n readonly message: string;\n readonly httpStatus: number;\n readonly retryable: boolean;\n readonly requestId?: string;\n readonly resourceId?: string;\n readonly details?: Readonly<Record<string, unknown>>;\n}\n\nexport class CommerceError extends Error {\n readonly code: CommerceErrorCode;\n readonly httpStatus: number;\n readonly retryable: boolean;\n readonly details?: Readonly<Record<string, unknown>>;\n readonly requestId?: string;\n readonly resourceId?: string;\n\n constructor(code: CommerceErrorCode, message: string, options: CommerceErrorOptions = {}) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = 'CommerceError';\n this.code = code;\n this.httpStatus = options.httpStatus ?? COMMERCE_ERROR_HTTP_STATUS[code];\n this.retryable = RETRYABLE_ERROR_CODES.has(code);\n if (options.details !== undefined) this.details = options.details;\n if (options.requestId !== undefined) this.requestId = options.requestId;\n if (options.resourceId !== undefined) this.resourceId = options.resourceId;\n Error.captureStackTrace?.(this, CommerceError);\n }\n\n /** Non-secret, client-safe representation. `cause` is never included */\n toInfo(): CommerceErrorInfo {\n return {\n code: this.code,\n message: this.message,\n httpStatus: this.httpStatus,\n retryable: this.retryable,\n ...(this.requestId !== undefined ? { requestId: this.requestId } : {}),\n ...(this.resourceId !== undefined ? { resourceId: this.resourceId } : {}),\n ...(this.details !== undefined ? { details: this.details } : {}),\n };\n }\n}\n\nexport function isCommerceError(value: unknown): value is CommerceError {\n return value instanceof CommerceError;\n}\n\n/**\n * Passes an existing CommerceError through. Other thrown values become a\n * CommerceError with a caller-chosen message, for example\n * `toCommerceError(err, 'BACKEND_ERROR', 'Backend call failed')`.\n *\n * Arbitrary Error messages may contain internal URLs, hostnames or file paths.\n * The original value remains on `cause` for internal logging; `toInfo()` and\n * `toErrorEnvelope()` do not serialize it.\n */\nexport function toCommerceError(\n value: unknown,\n fallbackCode: CommerceErrorCode = 'INTERNAL_ERROR',\n fallbackMessage = 'Unexpected internal error',\n): CommerceError {\n if (isCommerceError(value)) return value;\n return new CommerceError(fallbackCode, fallbackMessage, { cause: value });\n}\n\n/** A thrown value reduced to its message and name, for a log line */\nexport function describeError(error: unknown): { message: string; name?: string } {\n if (error instanceof Error) return { message: error.message, name: error.name };\n return { message: String(error) };\n}\n\n/**\n * `Name: message` for a log line, with every URL cut to its scheme and host,\n * because a backend or RPC URL can carry a credential in its userinfo, path or\n * query\n */\nexport function redactedErrorText(value: unknown): string {\n const text = value instanceof Error ? `${value.name}: ${value.message}` : String(value);\n return text.replace(/\\b[a-z][a-z\\d+.-]*:\\/\\/[^\\s\"'<>]+/gi, (match) => {\n try {\n const url = new URL(match);\n return `${url.protocol}//${url.host}`;\n } catch {\n return '[unparseable URL]';\n }\n });\n}\n\n// Deepest `cause` level toLogInfo() follows, which also ends a cyclic chain\nconst MAX_LOGGED_CAUSES = 5;\n\n/**\n * An error for an operator log line: the `toInfo()` fields plus the `cause`\n * chain that `toInfo()` leaves out, each level as `redactedErrorText()` gives it\n */\nexport function toLogInfo(\n value: unknown,\n): CommerceErrorInfo & { readonly cause?: readonly string[] } {\n const error = toCommerceError(value);\n const cause: string[] = [];\n let current = error.cause;\n while (current !== undefined && cause.length < MAX_LOGGED_CAUSES) {\n cause.push(redactedErrorText(current));\n current = current instanceof Error ? current.cause : undefined;\n }\n return cause.length > 0 ? { ...error.toInfo(), cause } : error.toInfo();\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { CommerceError } from './chunk-QQ3WY5L3.js';
1
+ import { CommerceError } from './chunk-SYQLJXIU.js';
2
2
  import { existsSync, accessSync, constants, closeSync, openSync, mkdirSync, chmodSync } from 'fs';
3
3
  import { dirname } from 'path';
4
4
  import Database from 'better-sqlite3';
@@ -66,5 +66,5 @@ function restrictDatabasePermissions(path, label, logger) {
66
66
  }
67
67
 
68
68
  export { openSqliteDatabase };
69
- //# sourceMappingURL=chunk-3MGAJ5FX.js.map
70
- //# sourceMappingURL=chunk-3MGAJ5FX.js.map
69
+ //# sourceMappingURL=chunk-VJ4EO4LE.js.map
70
+ //# sourceMappingURL=chunk-VJ4EO4LE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/storage/sqlite.ts"],"names":["fsConstants"],"mappings":"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","file":"chunk-3MGAJ5FX.js","sourcesContent":["/**\n * Opens a SQLite file with owner-only permissions. Every store that keeps a\n * SQLite file opens it here, so the hardening below exists once: the file is\n * pre-created at 0600, the WAL sidecars are narrowed once WAL creates them, and\n * an existing but unwritable file fails at startup.\n *\n * Callers own their schema; this function migrates nothing.\n */\nimport {\n accessSync,\n chmodSync,\n closeSync,\n existsSync,\n constants as fsConstants,\n mkdirSync,\n openSync,\n} from 'node:fs';\nimport { dirname } from 'node:path';\nimport Database from 'better-sqlite3';\nimport { CommerceError, type Logger } from '../core';\n\nexport interface OpenSqliteOptions {\n /** File path, or ':memory:' for tests. Parent directory is created if missing */\n readonly path: string;\n /** Names the database in operator-facing errors, e.g. \"Receipt database\" */\n readonly label: string;\n readonly logger: Logger;\n}\n\nexport function openSqliteDatabase(options: OpenSqliteOptions): Database.Database {\n const { path, label, logger } = options;\n const isFileBacked = path !== ':memory:';\n\n if (isFileBacked) {\n try {\n createDirectory(dirname(path));\n } catch (cause) {\n throw new CommerceError(\n 'STORAGE_ERROR',\n `${label} directory \"${dirname(path)}\" cannot be created: ${cause instanceof Error ? cause.message : String(cause)}`,\n { details: { path }, cause },\n );\n }\n }\n\n // Check write access at startup. SQLite can open a read-only database and\n // report the problem only when the first write is attempted.\n if (isFileBacked && existsSync(path)) {\n try {\n accessSync(path, fsConstants.W_OK);\n } catch {\n throw new CommerceError(\n 'STORAGE_ERROR',\n `${label} \"${path}\" exists but is not writable by this process (uid ${typeof process.getuid === 'function' ? process.getuid() : 'unknown'}). If this is a container that recently changed user, an existing volume may still be owned by the previous one - recreate it (docker compose down -v) or fix its ownership.`,\n { details: { path } },\n );\n }\n }\n\n // Create the file at 0600 before SQLite creates it at 0644 (less the umask).\n // A chmod afterwards is too late: a local user who opens the file in between\n // keeps a readable descriptor across the chmod. SQLite copies the main file's\n // mode onto the -wal/-shm sidecars, so this covers all three. `mode` applies\n // on creation only; an existing file is left to the chmod below.\n if (isFileBacked) {\n try {\n closeSync(openSync(path, 'a', 0o600));\n } catch {\n // Silent on purpose: `new Database` reports the same cause with a better\n // message, or the failure is harmless and the chmod below covers it\n }\n }\n\n const db = new Database(path);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n if (isFileBacked) restrictDatabasePermissions(path, label, logger);\n return db;\n}\n\n// Create missing directory levels at 0700, preserving existing modes. Explicit\n// recursion avoids the recursive mkdir hang observed on Node 24.8.0 when /proc\n// returns ENOENT for a directory it cannot create.\nfunction createDirectory(dir: string): void {\n if (existsSync(dir)) return;\n const parent = dirname(dir);\n if (parent !== dir) createDirectory(parent);\n try {\n mkdirSync(dir, { mode: 0o700 });\n } catch (error) {\n // Created by someone else in the meantime\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n }\n}\n\n/**\n * Narrows the database and its WAL sidecars to owner-only. The receipt ledger\n * holds no key material, but it does hold payer and payee addresses, amounts,\n * settlement hashes, replay keys and the commerce event log.\n *\n * Runs after `journal_mode = WAL`, which creates the sidecars, and also\n * tightens a database that already existed with a looser mode. A failure only\n * warns: a filesystem without POSIX modes must not stop the gateway starting.\n */\nfunction restrictDatabasePermissions(path: string, label: string, logger: Logger): void {\n for (const file of [path, `${path}-wal`, `${path}-shm`]) {\n try {\n chmodSync(file, 0o600);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') continue;\n logger.warn(\n { file, label, error: code ?? 'unknown' },\n 'could not restrict database permissions to owner-only',\n );\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/storage/sqlite.ts"],"names":["fsConstants"],"mappings":"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","file":"chunk-VJ4EO4LE.js","sourcesContent":["/**\n * Opens a SQLite file with owner-only permissions. Every store that keeps a\n * SQLite file opens it here, so the hardening below exists once: the file is\n * pre-created at 0600, the WAL sidecars are narrowed once WAL creates them, and\n * an existing but unwritable file fails at startup.\n *\n * Callers own their schema; this function migrates nothing.\n */\nimport {\n accessSync,\n chmodSync,\n closeSync,\n existsSync,\n constants as fsConstants,\n mkdirSync,\n openSync,\n} from 'node:fs';\nimport { dirname } from 'node:path';\nimport Database from 'better-sqlite3';\nimport { CommerceError, type Logger } from '../core';\n\nexport interface OpenSqliteOptions {\n /** File path, or ':memory:' for tests. Parent directory is created if missing */\n readonly path: string;\n /** Names the database in operator-facing errors, e.g. \"Receipt database\" */\n readonly label: string;\n readonly logger: Logger;\n}\n\nexport function openSqliteDatabase(options: OpenSqliteOptions): Database.Database {\n const { path, label, logger } = options;\n const isFileBacked = path !== ':memory:';\n\n if (isFileBacked) {\n try {\n createDirectory(dirname(path));\n } catch (cause) {\n throw new CommerceError(\n 'STORAGE_ERROR',\n `${label} directory \"${dirname(path)}\" cannot be created: ${cause instanceof Error ? cause.message : String(cause)}`,\n { details: { path }, cause },\n );\n }\n }\n\n // Check write access at startup. SQLite can open a read-only database and\n // report the problem only when the first write is attempted.\n if (isFileBacked && existsSync(path)) {\n try {\n accessSync(path, fsConstants.W_OK);\n } catch {\n throw new CommerceError(\n 'STORAGE_ERROR',\n `${label} \"${path}\" exists but is not writable by this process (uid ${typeof process.getuid === 'function' ? process.getuid() : 'unknown'}). If this is a container that recently changed user, an existing volume may still be owned by the previous one - recreate it (docker compose down -v) or fix its ownership.`,\n { details: { path } },\n );\n }\n }\n\n // Create the file at 0600 before SQLite creates it at 0644 (less the umask).\n // A chmod afterwards is too late: a local user who opens the file in between\n // keeps a readable descriptor across the chmod. SQLite copies the main file's\n // mode onto the -wal/-shm sidecars, so this covers all three. `mode` applies\n // on creation only; an existing file is left to the chmod below.\n if (isFileBacked) {\n try {\n closeSync(openSync(path, 'a', 0o600));\n } catch {\n // Silent on purpose: `new Database` reports the same cause with a better\n // message, or the failure is harmless and the chmod below covers it\n }\n }\n\n const db = new Database(path);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n if (isFileBacked) restrictDatabasePermissions(path, label, logger);\n return db;\n}\n\n// Create missing directory levels at 0700, preserving existing modes. Explicit\n// recursion avoids the recursive mkdir hang observed on Node 24.8.0 when /proc\n// returns ENOENT for a directory it cannot create.\nfunction createDirectory(dir: string): void {\n if (existsSync(dir)) return;\n const parent = dirname(dir);\n if (parent !== dir) createDirectory(parent);\n try {\n mkdirSync(dir, { mode: 0o700 });\n } catch (error) {\n // Created by someone else in the meantime\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n }\n}\n\n/**\n * Narrows the database and its WAL sidecars to owner-only. The receipt ledger\n * holds no key material, but it does hold payer and payee addresses, amounts,\n * settlement hashes, replay keys and the commerce event log.\n *\n * Runs after `journal_mode = WAL`, which creates the sidecars, and also\n * tightens a database that already existed with a looser mode. A failure only\n * warns: a filesystem without POSIX modes must not stop the gateway starting.\n */\nfunction restrictDatabasePermissions(path: string, label: string, logger: Logger): void {\n for (const file of [path, `${path}-wal`, `${path}-shm`]) {\n try {\n chmodSync(file, 0o600);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') continue;\n logger.warn(\n { file, label, error: code ?? 'unknown' },\n 'could not restrict database permissions to owner-only',\n );\n }\n }\n}\n"]}