@absolutejs/billing 0.8.0 → 0.9.1

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 CHANGED
@@ -6,6 +6,18 @@ This file is generated by `absolute-changelog` from the entries in
6
6
  `changelog/`. Edit an entry, not this file — and add new ones under
7
7
  `changelog/unreleased/`.
8
8
 
9
+ ## 0.9.1 — 2026-09-11
10
+
11
+ ### Fixed
12
+
13
+ - **Bind serialized ledger, work and checkout JSON as text before casting, preventing PostgreSQL drivers from double-encoding state**
14
+
15
+ ## 0.9.0 — 2026-09-11
16
+
17
+ ### Added
18
+
19
+ - **Add bounded customer billing reports, receipt cursors and reconciled usage projections**
20
+
9
21
  ## 0.8.0 — 2026-09-11
10
22
 
11
23
  ### Added
package/README.md CHANGED
@@ -102,3 +102,5 @@ BSL-1.1 with named carveout against hosted SaaS billing platforms
102
102
  ## Secure credit checkout
103
103
 
104
104
  [Purchase-only handoffs](docs/checkout-handoffs.md) provide expiring, single-use browser capabilities without a full login session.
105
+
106
+ `@absolutejs/billing/reports` provides customer-facing status, receipt pagination, and usage contracts. `parseUsageRange` uses a half-open UTC date interval of at most 90 days (default: the last 30 days including today). Receipt cursors are positions, never authorization: bind every query, reversal join, and cursor to the caller's account. Readers must project through the public helpers, which omit provider costs, payment tokens and vault references. Usage day and feature totals must reconcile exactly; purchased dollars and consumed service credits are different measures. Automatic refill is currently unsupported.
package/changelog.json CHANGED
@@ -2,6 +2,26 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/billing",
4
4
  "releases": [
5
+ {
6
+ "version": "0.9.1",
7
+ "date": "2026-09-11",
8
+ "changes": [
9
+ {
10
+ "kind": "fixed",
11
+ "summary": "Bind serialized ledger, work and checkout JSON as text before casting, preventing PostgreSQL drivers from double-encoding state"
12
+ }
13
+ ]
14
+ },
15
+ {
16
+ "version": "0.9.0",
17
+ "date": "2026-09-11",
18
+ "changes": [
19
+ {
20
+ "kind": "added",
21
+ "summary": "Add bounded customer billing reports, receipt cursors and reconciled usage projections"
22
+ }
23
+ ]
24
+ },
5
25
  {
6
26
  "version": "0.8.0",
7
27
  "date": "2026-09-11",
@@ -58,7 +58,7 @@ var createPostgresCheckoutHandoffs = (db, now = Date.now) => ({
58
58
  const code = secret();
59
59
  const id = randomUUID();
60
60
  const expiresAt = now() + lifetimeMs;
61
- await db.query(`INSERT INTO billing_checkout.handoffs(id,account_id,quote,code_hash,expires_at) VALUES ($1,$2,$3::jsonb,$4,$5)`, [id, accountId, JSON.stringify(quote), hash(code), expiresAt]);
61
+ await db.query(`INSERT INTO billing_checkout.handoffs(id,account_id,quote,code_hash,expires_at) VALUES ($1,$2,$3::text::jsonb,$4,$5)`, [id, accountId, JSON.stringify(quote), hash(code), expiresAt]);
62
62
  return { id, code, expiresAt };
63
63
  },
64
64
  async exchange(code, browserAccountId = null) {
@@ -104,5 +104,5 @@ export {
104
104
  createPostgresCheckoutHandoffs
105
105
  };
106
106
 
107
- //# debugId=F96D1D9F1E1CA67A64756E2164756E21
107
+ //# debugId=13C01DD2B5D2689364756E2164756E21
108
108
  //# sourceMappingURL=checkoutHandoff.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/checkoutHandoff.ts"],
4
4
  "sourcesContent": [
5
- "import { createHash, randomBytes, randomUUID } from \"node:crypto\";\nimport type { CreditSqlClient } from \"./prepaidPostgres\";\n\nexport type CheckoutQuote = {\n productId: string;\n amountCents: number;\n currency: string;\n credits: number;\n};\nexport type CheckoutHandoff = {\n id: string;\n accountId: string;\n quote: CheckoutQuote;\n expiresAt: number;\n};\nconst lifetimeMs = 15 * 60 * 1000;\nconst hash = (secret: string) =>\n createHash(\"sha256\").update(secret).digest(\"hex\");\nconst secret = () => randomBytes(32).toString(\"base64url\");\nconst validSecret = (value: string) => /^[A-Za-z0-9_-]{43}$/.test(value);\nconst validQuote = (value: CheckoutQuote) =>\n typeof value.productId === \"string\" &&\n value.productId.length > 0 &&\n value.productId.length <= 128 &&\n Number.isSafeInteger(value.amountCents) &&\n value.amountCents > 0 &&\n /^[A-Z]{3}$/.test(value.currency) &&\n Number.isSafeInteger(value.credits) &&\n value.credits > 0;\n\n/** Apply explicitly during deployment. Secrets are stored only as SHA-256 hashes. */\nexport const checkoutHandoffPostgresSchemaSql = () => `\nCREATE SCHEMA IF NOT EXISTS billing_checkout;\nCREATE TABLE IF NOT EXISTS billing_checkout.handoffs (\n id text PRIMARY KEY, account_id text NOT NULL, quote jsonb NOT NULL,\n code_hash text NOT NULL UNIQUE, expires_at bigint NOT NULL,\n session_hash text UNIQUE, csrf_hash text, session_expires_at bigint\n);\nCREATE INDEX IF NOT EXISTS handoffs_account ON billing_checkout.handoffs(account_id, id);\n`;\nconst decode = (\n row: Record<string, unknown> | undefined,\n): CheckoutHandoff | null => {\n if (!row) return null;\n const quote = row.quote;\n if (\n !quote ||\n typeof quote !== \"object\" ||\n !(\"productId\" in quote) ||\n !(\"amountCents\" in quote) ||\n !(\"currency\" in quote) ||\n !(\"credits\" in quote) ||\n typeof quote.productId !== \"string\" ||\n typeof quote.amountCents !== \"number\" ||\n typeof quote.currency !== \"string\" ||\n typeof quote.credits !== \"number\" ||\n typeof row.id !== \"string\" ||\n typeof row.account_id !== \"string\"\n )\n throw new Error(\"Invalid checkout record\");\n const parsed = {\n productId: quote.productId,\n amountCents: quote.amountCents,\n currency: quote.currency,\n credits: quote.credits,\n };\n if (!validQuote(parsed)) throw new Error(\"Invalid checkout quote\");\n return {\n id: row.id,\n accountId: row.account_id,\n quote: parsed,\n expiresAt: Number(row.expires_at),\n };\n};\n/** This is a purchase-only capability, never a login or a saved-card authorization.\n * The application binds the authenticated account and prices the quote. */\nexport const createPostgresCheckoutHandoffs = (\n db: CreditSqlClient,\n now = Date.now,\n) => ({\n async issue(accountId: string, quote: CheckoutQuote) {\n if (!accountId || !validQuote(quote))\n throw new Error(\"Invalid checkout request\");\n const code = secret();\n const id = randomUUID();\n const expiresAt = now() + lifetimeMs;\n await db.query(\n `INSERT INTO billing_checkout.handoffs(id,account_id,quote,code_hash,expires_at) VALUES ($1,$2,$3::jsonb,$4,$5)`,\n [id, accountId, JSON.stringify(quote), hash(code), expiresAt],\n );\n return { id, code, expiresAt };\n },\n /** Invoke only on a deliberate same-origin POST, never a preview GET.\n * Atomic exchange makes concurrent redemption and link replay fail closed. */\n async exchange(code: string, browserAccountId: string | null = null) {\n if (!validSecret(code)) return null;\n const session = secret();\n const csrf = secret();\n const expiresAt = now() + lifetimeMs;\n const { rows } = await db.query(\n `UPDATE billing_checkout.handoffs SET session_hash=$2,csrf_hash=$3,session_expires_at=$4 WHERE code_hash=$1 AND session_hash IS NULL AND expires_at>$5 AND ($6::text IS NULL OR account_id=$6) RETURNING *`,\n [\n hash(code),\n hash(session),\n hash(csrf),\n expiresAt,\n now(),\n browserAccountId,\n ],\n );\n const handoff = decode(rows[0]);\n return handoff ? { handoff, session, csrf, expiresAt } : null;\n },\n async authorize(\n session: string,\n csrf: string,\n browserAccountId: string | null = null,\n ) {\n if (!validSecret(session) || !validSecret(csrf)) return null;\n const { rows } = await db.query(\n `SELECT * FROM billing_checkout.handoffs WHERE session_hash=$1 AND csrf_hash=$2 AND session_expires_at>$3 AND ($4::text IS NULL OR account_id=$4)`,\n [hash(session), hash(csrf), now(), browserAccountId],\n );\n return decode(rows[0]);\n },\n /** Public IDs are not capabilities: status always requires account binding. */\n async get(accountId: string, id: string) {\n const { rows } = await db.query(\n `SELECT * FROM billing_checkout.handoffs WHERE id=$1 AND account_id=$2`,\n [id, accountId],\n );\n return decode(rows[0]);\n },\n});\n\n/** Put the code in a fragment so previews, servers and referrers don't receive it.\n * The landing page must remove it before loading any third-party code. */\nexport const checkoutHandoffUrl = (landingUrl: string, code: string) => {\n const url = new URL(landingUrl);\n if (\n url.protocol !== \"https:\" ||\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n !validSecret(code)\n )\n throw new Error(\"Invalid checkout landing URL\");\n url.hash = code;\n return url.href;\n};\nexport const checkoutSameOrigin = (request: Request, origin: string) =>\n request.method === \"POST\" &&\n request.headers.get(\"origin\") === new URL(origin).origin &&\n [null, \"same-origin\"].includes(request.headers.get(\"sec-fetch-site\"));\n"
5
+ "import { createHash, randomBytes, randomUUID } from \"node:crypto\";\nimport type { CreditSqlClient } from \"./prepaidPostgres\";\n\nexport type CheckoutQuote = {\n productId: string;\n amountCents: number;\n currency: string;\n credits: number;\n};\nexport type CheckoutHandoff = {\n id: string;\n accountId: string;\n quote: CheckoutQuote;\n expiresAt: number;\n};\nconst lifetimeMs = 15 * 60 * 1000;\nconst hash = (secret: string) =>\n createHash(\"sha256\").update(secret).digest(\"hex\");\nconst secret = () => randomBytes(32).toString(\"base64url\");\nconst validSecret = (value: string) => /^[A-Za-z0-9_-]{43}$/.test(value);\nconst validQuote = (value: CheckoutQuote) =>\n typeof value.productId === \"string\" &&\n value.productId.length > 0 &&\n value.productId.length <= 128 &&\n Number.isSafeInteger(value.amountCents) &&\n value.amountCents > 0 &&\n /^[A-Z]{3}$/.test(value.currency) &&\n Number.isSafeInteger(value.credits) &&\n value.credits > 0;\n\n/** Apply explicitly during deployment. Secrets are stored only as SHA-256 hashes. */\nexport const checkoutHandoffPostgresSchemaSql = () => `\nCREATE SCHEMA IF NOT EXISTS billing_checkout;\nCREATE TABLE IF NOT EXISTS billing_checkout.handoffs (\n id text PRIMARY KEY, account_id text NOT NULL, quote jsonb NOT NULL,\n code_hash text NOT NULL UNIQUE, expires_at bigint NOT NULL,\n session_hash text UNIQUE, csrf_hash text, session_expires_at bigint\n);\nCREATE INDEX IF NOT EXISTS handoffs_account ON billing_checkout.handoffs(account_id, id);\n`;\nconst decode = (\n row: Record<string, unknown> | undefined,\n): CheckoutHandoff | null => {\n if (!row) return null;\n const quote = row.quote;\n if (\n !quote ||\n typeof quote !== \"object\" ||\n !(\"productId\" in quote) ||\n !(\"amountCents\" in quote) ||\n !(\"currency\" in quote) ||\n !(\"credits\" in quote) ||\n typeof quote.productId !== \"string\" ||\n typeof quote.amountCents !== \"number\" ||\n typeof quote.currency !== \"string\" ||\n typeof quote.credits !== \"number\" ||\n typeof row.id !== \"string\" ||\n typeof row.account_id !== \"string\"\n )\n throw new Error(\"Invalid checkout record\");\n const parsed = {\n productId: quote.productId,\n amountCents: quote.amountCents,\n currency: quote.currency,\n credits: quote.credits,\n };\n if (!validQuote(parsed)) throw new Error(\"Invalid checkout quote\");\n return {\n id: row.id,\n accountId: row.account_id,\n quote: parsed,\n expiresAt: Number(row.expires_at),\n };\n};\n/** This is a purchase-only capability, never a login or a saved-card authorization.\n * The application binds the authenticated account and prices the quote. */\nexport const createPostgresCheckoutHandoffs = (\n db: CreditSqlClient,\n now = Date.now,\n) => ({\n async issue(accountId: string, quote: CheckoutQuote) {\n if (!accountId || !validQuote(quote))\n throw new Error(\"Invalid checkout request\");\n const code = secret();\n const id = randomUUID();\n const expiresAt = now() + lifetimeMs;\n await db.query(\n `INSERT INTO billing_checkout.handoffs(id,account_id,quote,code_hash,expires_at) VALUES ($1,$2,$3::text::jsonb,$4,$5)`,\n [id, accountId, JSON.stringify(quote), hash(code), expiresAt],\n );\n return { id, code, expiresAt };\n },\n /** Invoke only on a deliberate same-origin POST, never a preview GET.\n * Atomic exchange makes concurrent redemption and link replay fail closed. */\n async exchange(code: string, browserAccountId: string | null = null) {\n if (!validSecret(code)) return null;\n const session = secret();\n const csrf = secret();\n const expiresAt = now() + lifetimeMs;\n const { rows } = await db.query(\n `UPDATE billing_checkout.handoffs SET session_hash=$2,csrf_hash=$3,session_expires_at=$4 WHERE code_hash=$1 AND session_hash IS NULL AND expires_at>$5 AND ($6::text IS NULL OR account_id=$6) RETURNING *`,\n [\n hash(code),\n hash(session),\n hash(csrf),\n expiresAt,\n now(),\n browserAccountId,\n ],\n );\n const handoff = decode(rows[0]);\n return handoff ? { handoff, session, csrf, expiresAt } : null;\n },\n async authorize(\n session: string,\n csrf: string,\n browserAccountId: string | null = null,\n ) {\n if (!validSecret(session) || !validSecret(csrf)) return null;\n const { rows } = await db.query(\n `SELECT * FROM billing_checkout.handoffs WHERE session_hash=$1 AND csrf_hash=$2 AND session_expires_at>$3 AND ($4::text IS NULL OR account_id=$4)`,\n [hash(session), hash(csrf), now(), browserAccountId],\n );\n return decode(rows[0]);\n },\n /** Public IDs are not capabilities: status always requires account binding. */\n async get(accountId: string, id: string) {\n const { rows } = await db.query(\n `SELECT * FROM billing_checkout.handoffs WHERE id=$1 AND account_id=$2`,\n [id, accountId],\n );\n return decode(rows[0]);\n },\n});\n\n/** Put the code in a fragment so previews, servers and referrers don't receive it.\n * The landing page must remove it before loading any third-party code. */\nexport const checkoutHandoffUrl = (landingUrl: string, code: string) => {\n const url = new URL(landingUrl);\n if (\n url.protocol !== \"https:\" ||\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n !validSecret(code)\n )\n throw new Error(\"Invalid checkout landing URL\");\n url.hash = code;\n return url.href;\n};\nexport const checkoutSameOrigin = (request: Request, origin: string) =>\n request.method === \"POST\" &&\n request.headers.get(\"origin\") === new URL(origin).origin &&\n [null, \"same-origin\"].includes(request.headers.get(\"sec-fetch-site\"));\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "F96D1D9F1E1CA67A64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "13C01DD2B5D2689364756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -295,7 +295,7 @@ var createPostgresCreditAccountStore = (client, schema = "billing_credits") => {
295
295
  return {
296
296
  initialize: async (accountId, seed) => {
297
297
  validateCreditAccount(seed);
298
- await client.query(`INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::jsonb, $2::jsonb) ON CONFLICT DO NOTHING`, [accountId, JSON.stringify(seed)]);
298
+ await client.query(`INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::text::jsonb, $2::text::jsonb) ON CONFLICT DO NOTHING`, [accountId, JSON.stringify(seed)]);
299
299
  },
300
300
  read: (accountId) => read(client, accountId),
301
301
  transaction: (accountId, run) => client.transaction(async (sql) => {
@@ -313,14 +313,14 @@ var createPostgresCreditAccountStore = (client, schema = "billing_credits") => {
313
313
  return rows[0]?.state ?? null;
314
314
  },
315
315
  save: async (operationId, receipt) => {
316
- await sql.query(`UPDATE ${n}.accounts SET state = $2::jsonb, updated_at = now() WHERE account_id = $1`, [accountId, JSON.stringify(receipt.account)]);
316
+ await sql.query(`UPDATE ${n}.accounts SET state = $2::text::jsonb, updated_at = now() WHERE account_id = $1`, [accountId, JSON.stringify(receipt.account)]);
317
317
  if (receipt.reservation)
318
- await sql.query(`INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`, [
318
+ await sql.query(`INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::text::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`, [
319
319
  accountId,
320
320
  receipt.reservation.id,
321
321
  JSON.stringify(receipt.reservation)
322
322
  ]);
323
- await sql.query(`INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::jsonb)`, [accountId, operationId, JSON.stringify(receipt)]);
323
+ await sql.query(`INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::text::jsonb)`, [accountId, operationId, JSON.stringify(receipt)]);
324
324
  }
325
325
  });
326
326
  })
@@ -360,7 +360,7 @@ var createPostgresCreditWork = (client, schema = "billing_credits") => {
360
360
  const { rows } = await sql.query(`SELECT state FROM ${n}.work WHERE account_id = $1 AND work_id = $2 FOR UPDATE`, [accountId, workId]);
361
361
  return rows[0]?.state;
362
362
  };
363
- const save = (sql, accountId, workId, state) => sql.query(`UPDATE ${n}.work SET state = $3::jsonb, updated_at = now() WHERE account_id = $1 AND work_id = $2`, [accountId, workId, JSON.stringify(state)]);
363
+ const save = (sql, accountId, workId, state) => sql.query(`UPDATE ${n}.work SET state = $3::text::jsonb, updated_at = now() WHERE account_id = $1 AND work_id = $2`, [accountId, workId, JSON.stringify(state)]);
364
364
  const ledger = (sql) => createCreditAccountLedger(createPostgresCreditAccountStore({ ...sql, transaction: (run) => run(sql) }, schema));
365
365
  return {
366
366
  get: (accountId, workId) => {
@@ -395,7 +395,7 @@ var createPostgresCreditWork = (client, schema = "billing_credits") => {
395
395
  status: "running",
396
396
  result: null
397
397
  };
398
- await sql.query(`INSERT INTO ${n}.work (account_id, work_id, state) VALUES ($1, $2, $3::jsonb)`, [accountId, workId, JSON.stringify(work)]);
398
+ await sql.query(`INSERT INTO ${n}.work (account_id, work_id, state) VALUES ($1, $2, $3::text::jsonb)`, [accountId, workId, JSON.stringify(work)]);
399
399
  return { fresh: true, work };
400
400
  });
401
401
  },
@@ -456,5 +456,5 @@ export {
456
456
  creditWorkPostgresSchemaSql
457
457
  };
458
458
 
459
- //# debugId=AA6CCB44B9F64D8964756E2164756E21
459
+ //# debugId=C694B3533E4362E464756E2164756E21
460
460
  //# sourceMappingURL=creditWork.js.map
@@ -3,10 +3,10 @@
3
3
  "sources": ["../src/prepaid.ts", "../src/prepaidPostgres.ts", "../src/creditWork.ts"],
4
4
  "sourcesContent": [
5
5
  "/** Service-credit accounting. Host authorization and payment verification happen\n * before these commands. Every command and its receipt commit in one transaction. */\nexport type CreditAccount = {\n periodId: string;\n periodEnd: string | null;\n periodAllowance: number;\n periodRemaining: number;\n purchasedRemaining: number;\n promotionalRemaining: number;\n debt: number;\n reserved: number;\n consumed: number;\n};\nexport type CreditAllocation = {\n period: number;\n purchased: number;\n promotional: number;\n};\nexport type CreditReservation = {\n id: string;\n periodId: string;\n allocation: CreditAllocation;\n status: \"reserved\" | \"settled\" | \"released\";\n charged: number | null;\n};\nexport type CreditCommand =\n | { kind: \"grant\"; bucket: \"purchased\" | \"promotional\"; credits: number }\n | { kind: \"reverse\"; bucket: \"purchased\" | \"promotional\"; credits: number }\n | {\n kind: \"period\";\n periodId: string;\n periodEnd: string | null;\n allowance: number;\n }\n | { kind: \"reserve\"; reservationId: string; credits: number }\n | { kind: \"settle\"; reservationId: string; credits: number }\n | { kind: \"release\"; reservationId: string }\n | { kind: \"debit\"; credits: number; allowDebt?: boolean; reference?: string };\nexport type CreditReceipt = {\n command: CreditCommand;\n account: CreditAccount;\n reservation?: CreditReservation;\n};\nexport type CreditTransaction = {\n account: CreditAccount;\n receipt: (operationId: string) => Promise<CreditReceipt | null>;\n reservation: (id: string) => Promise<CreditReservation | null>;\n save: (operationId: string, receipt: CreditReceipt) => Promise<void>;\n};\nexport type CreditAccountStore = {\n /** Persist seed once. Never overwrite an existing account. */\n initialize: (accountId: string, seed: CreditAccount) => Promise<void>;\n read: (accountId: string) => Promise<CreditAccount | null>;\n /** Lock one account, serialize its commands across processes, and roll back\n * account, reservation and receipt together if the callback fails. */\n transaction: <T>(\n accountId: string,\n run: (tx: CreditTransaction) => Promise<T>,\n ) => Promise<T>;\n};\nconst integer = (value: number) => {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new Error(\"Credits must be nonnegative safe integers\");\n return value;\n};\nconst periodIdentity = (value: string) => {\n if (\n !Number.isFinite(Date.parse(value)) ||\n new Date(value).toISOString() !== value\n )\n throw new Error(\"Credit period ID must be an ISO UTC start timestamp\");\n return value;\n};\nconst identity = (value: string) => {\n if (!value || value.length > 256)\n throw new Error(\"Credit identity is invalid\");\n return value;\n};\nexport const validateCreditAccount = (account: CreditAccount) => {\n periodIdentity(account.periodId);\n if (\n account.periodEnd !== null &&\n periodIdentity(account.periodEnd) <= account.periodId\n )\n throw new Error(\"Credit period end must follow its start\");\n for (const key of [\n \"periodAllowance\",\n \"periodRemaining\",\n \"purchasedRemaining\",\n \"promotionalRemaining\",\n \"debt\",\n \"reserved\",\n \"consumed\",\n ] as const)\n integer(account[key]);\n integer(\n account.periodRemaining +\n account.purchasedRemaining +\n account.promotionalRemaining,\n );\n if (account.periodRemaining > account.periodAllowance)\n throw new Error(\"Period balance exceeds its allowance\");\n};\nexport const availableCredits = (account: CreditAccount) =>\n Math.max(\n 0,\n account.periodRemaining +\n account.purchasedRemaining +\n account.promotionalRemaining -\n account.debt,\n );\n\n/** Preserve the currently observable legacy balance without inventing purchase\n * provenance. Historical mixed bonus grants remain promotional carryover. */\nexport const migrateLegacyCreditAccount = (input: {\n periodId: string;\n periodEnd?: string | null;\n periodAllowance: number;\n bonusCredits: number;\n consumed: number;\n}): CreditAccount => {\n integer(input.periodAllowance);\n integer(input.consumed);\n if (!Number.isSafeInteger(input.bonusCredits))\n throw new Error(\"Invalid legacy bonus credits\");\n const account: CreditAccount = {\n periodId: input.periodId,\n periodEnd: input.periodEnd ?? null,\n periodAllowance: input.periodAllowance,\n periodRemaining: Math.max(0, input.periodAllowance - input.consumed),\n purchasedRemaining: 0,\n promotionalRemaining: Math.max(\n 0,\n input.bonusCredits - Math.max(0, input.consumed - input.periodAllowance),\n ),\n debt:\n Math.max(0, -input.bonusCredits) +\n Math.max(\n 0,\n input.consumed -\n input.periodAllowance -\n Math.max(0, input.bonusCredits),\n ),\n reserved: 0,\n consumed: input.consumed,\n };\n validateCreditAccount(account);\n return account;\n};\nconst allocationTotal = (value: CreditAllocation) =>\n value.period + value.promotional + value.purchased;\nconst take = (account: CreditAccount, credits: number): CreditAllocation => {\n // A reversal liability consumes existing available credits exactly once.\n let owed = account.debt;\n for (const key of [\n \"periodRemaining\",\n \"promotionalRemaining\",\n \"purchasedRemaining\",\n ] as const) {\n const covered = Math.min(account[key], owed);\n account[key] -= covered;\n owed -= covered;\n }\n account.debt = owed;\n const period = Math.min(account.periodRemaining, credits);\n const promotional = Math.min(account.promotionalRemaining, credits - period);\n const purchased = Math.min(\n account.purchasedRemaining,\n credits - period - promotional,\n );\n account.periodRemaining -= period;\n account.promotionalRemaining -= promotional;\n account.purchasedRemaining -= purchased;\n return { period, promotional, purchased };\n};\nconst grant = (\n account: CreditAccount,\n key: \"periodRemaining\" | \"purchasedRemaining\" | \"promotionalRemaining\",\n credits: number,\n) => {\n const covered = Math.min(account.debt, credits);\n account.debt -= covered;\n account[key] = integer(account[key] + credits - covered);\n};\nconst normalize = (command: CreditCommand): CreditCommand => {\n switch (command.kind) {\n case \"grant\":\n case \"reverse\":\n if (command.bucket !== \"purchased\" && command.bucket !== \"promotional\")\n throw new Error(\"Invalid credit bucket\");\n return {\n kind: command.kind,\n bucket: command.bucket,\n credits: integer(command.credits),\n };\n case \"period\":\n return {\n kind: command.kind,\n periodId: periodIdentity(command.periodId),\n periodEnd:\n command.periodEnd === null ? null : periodIdentity(command.periodEnd),\n allowance: integer(command.allowance),\n };\n case \"reserve\":\n case \"settle\":\n return {\n kind: command.kind,\n reservationId: identity(command.reservationId),\n credits: integer(command.credits),\n };\n case \"release\":\n return {\n kind: command.kind,\n reservationId: identity(command.reservationId),\n };\n case \"debit\":\n return {\n kind: command.kind,\n credits: integer(command.credits),\n allowDebt: command.allowDebt === true,\n ...(command.reference === undefined\n ? {}\n : { reference: identity(command.reference) }),\n };\n default:\n throw new Error(\"Invalid credit command\");\n }\n};\nexport const createCreditAccountLedger = (store: CreditAccountStore) => ({\n initialize: async (accountId: string, seed: CreditAccount) => {\n identity(accountId);\n validateCreditAccount(seed);\n await store.initialize(accountId, seed);\n },\n receipt: (accountId: string, operationId: string) =>\n store.transaction(identity(accountId), (tx) =>\n tx.receipt(identity(operationId)),\n ),\n balance: (accountId: string) => store.read(identity(accountId)),\n execute: async (\n accountId: string,\n operationId: string,\n input: CreditCommand,\n ): Promise<CreditReceipt> => {\n identity(accountId);\n identity(operationId);\n const command = normalize(input);\n return store.transaction(accountId, async (tx) => {\n const previous = await tx.receipt(operationId);\n if (previous) {\n if (\n JSON.stringify(normalize(previous.command)) !==\n JSON.stringify(command)\n )\n throw new Error(\"Credit operation ID reused with different input\");\n return previous;\n }\n const account = { ...tx.account };\n validateCreditAccount(account);\n let reservation: CreditReservation | undefined;\n switch (command.kind) {\n case \"grant\":\n grant(\n account,\n command.bucket === \"purchased\"\n ? \"purchasedRemaining\"\n : \"promotionalRemaining\",\n command.credits,\n );\n break;\n case \"reverse\": {\n const key =\n command.bucket === \"purchased\"\n ? \"purchasedRemaining\"\n : \"promotionalRemaining\";\n const covered = Math.min(account[key], command.credits);\n account[key] -= covered;\n account.debt += command.credits - covered;\n break;\n }\n case \"period\":\n if (command.periodId < account.periodId)\n throw new Error(\"Credit period cannot move backwards\");\n if (command.periodId === account.periodId) {\n // Upgrades add only the increase. Downgrades take effect next period.\n const increase = Math.max(\n 0,\n command.allowance - account.periodAllowance,\n );\n if (account.periodEnd === null)\n account.periodEnd = command.periodEnd;\n account.periodAllowance += increase;\n grant(account, \"periodRemaining\", increase);\n } else {\n account.periodId = command.periodId;\n account.periodEnd = command.periodEnd;\n account.periodAllowance = command.allowance;\n account.periodRemaining = 0;\n account.consumed = 0;\n grant(account, \"periodRemaining\", command.allowance);\n }\n break;\n case \"debit\": {\n if (!command.allowDebt && availableCredits(account) < command.credits)\n throw new Error(\"Insufficient credits\");\n const used = allocationTotal(take(account, command.credits));\n account.debt += command.credits - used;\n account.consumed += command.credits;\n break;\n }\n case \"reserve\":\n if (command.credits === 0)\n throw new Error(\"Reservation must be positive\");\n if (await tx.reservation(command.reservationId))\n throw new Error(\"Credit reservation already exists\");\n if (availableCredits(account) < command.credits)\n throw new Error(\"Insufficient credits\");\n reservation = {\n id: command.reservationId,\n periodId: account.periodId,\n allocation: take(account, command.credits),\n status: \"reserved\",\n charged: null,\n };\n account.reserved += command.credits;\n break;\n case \"settle\":\n case \"release\": {\n const held = await tx.reservation(command.reservationId);\n if (!held || held.status !== \"reserved\")\n throw new Error(\"Credit reservation is not active\");\n const total = allocationTotal(held.allocation);\n const charged = command.kind === \"settle\" ? command.credits : 0;\n if (charged > total)\n throw new Error(\"Charge exceeds reserved credits\");\n let refund = total - charged;\n for (const [bucket, key] of [\n [\"purchased\", \"purchasedRemaining\"],\n [\"promotional\", \"promotionalRemaining\"],\n [\"period\", \"periodRemaining\"],\n ] as const) {\n const restored = Math.min(held.allocation[bucket], refund);\n refund -= restored;\n if (bucket !== \"period\" || held.periodId === account.periodId)\n grant(account, key, restored);\n }\n account.reserved -= total;\n if (held.periodId === account.periodId) account.consumed += charged;\n reservation = {\n ...held,\n status: command.kind === \"settle\" ? \"settled\" : \"released\",\n charged,\n };\n break;\n }\n }\n validateCreditAccount(account);\n const receipt: CreditReceipt = {\n command,\n account,\n ...(reservation ? { reservation } : {}),\n };\n await tx.save(operationId, receipt);\n return receipt;\n });\n },\n});\n\n/** Portal access and funded MCP access are independent. Period-only free\n * allowances do not silently unlock prepaid work; carryover grants may. */\nexport const creditEntitlements = (input: {\n subscribed: boolean;\n prepaidEnabled: boolean;\n account: CreditAccount | null;\n}) => {\n const funded =\n input.prepaidEnabled &&\n input.account !== null &&\n input.account.purchasedRemaining + input.account.promotionalRemaining > 0 &&\n availableCredits(input.account) > 0;\n return {\n portal: input.subscribed,\n mcp: input.subscribed\n ? (\"member\" as const)\n : funded\n ? (\"prepaid\" as const)\n : (\"free\" as const),\n };\n};\n",
6
- "import {\n validateCreditAccount,\n type CreditAccount,\n type CreditAccountStore,\n type CreditReceipt,\n type CreditReservation,\n} from \"./prepaid\";\nexport type CreditSql = {\n query: (\n query: string,\n parameters: readonly unknown[],\n ) => Promise<{ rows: Record<string, unknown>[] }>;\n};\nexport type CreditSqlClient = CreditSql & {\n transaction: <T>(run: (sql: CreditSql) => Promise<T>) => Promise<T>;\n};\nconst namespace = (value: string) => {\n if (!/^[a-z][a-z0-9_]*$/.test(value))\n throw new Error(\"Invalid credit schema name\");\n return value;\n};\nexport const creditAccountPostgresSchemaSql = (schema = \"billing_credits\") => {\n const n = namespace(schema);\n return `CREATE SCHEMA IF NOT EXISTS ${n};\nCREATE TABLE IF NOT EXISTS ${n}.accounts (\n account_id text PRIMARY KEY, state jsonb NOT NULL, initial_state jsonb NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE TABLE IF NOT EXISTS ${n}.operations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), operation_id text NOT NULL,\n receipt jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, operation_id)\n);\nCREATE TABLE IF NOT EXISTS ${n}.reservations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), reservation_id text NOT NULL,\n state jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, reservation_id)\n);`;\n};\nexport const createPostgresCreditAccountStore = (\n client: CreditSqlClient,\n schema = \"billing_credits\",\n): CreditAccountStore => {\n const n = namespace(schema);\n const read = async (sql: CreditSql, accountId: string, lock = false) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.accounts WHERE account_id = $1${lock ? \" FOR UPDATE\" : \"\"}`,\n [accountId],\n );\n if (!rows[0]) return null;\n const state = rows[0].state as CreditAccount;\n validateCreditAccount(state);\n return state;\n };\n return {\n initialize: async (accountId, seed) => {\n validateCreditAccount(seed);\n await client.query(\n `INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::jsonb, $2::jsonb) ON CONFLICT DO NOTHING`,\n [accountId, JSON.stringify(seed)],\n );\n },\n read: (accountId) => read(client, accountId),\n transaction: (accountId, run) =>\n client.transaction(async (sql) => {\n const account = await read(sql, accountId, true);\n if (!account) throw new Error(\"Credit account is not initialized\");\n return run({\n account,\n receipt: async (operationId) => {\n const { rows } = await sql.query(\n `SELECT receipt FROM ${n}.operations WHERE account_id = $1 AND operation_id = $2`,\n [accountId, operationId],\n );\n return (rows[0]?.receipt as CreditReceipt | undefined) ?? null;\n },\n reservation: async (id) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.reservations WHERE account_id = $1 AND reservation_id = $2`,\n [accountId, id],\n );\n return (rows[0]?.state as CreditReservation | undefined) ?? null;\n },\n save: async (operationId, receipt) => {\n await sql.query(\n `UPDATE ${n}.accounts SET state = $2::jsonb, updated_at = now() WHERE account_id = $1`,\n [accountId, JSON.stringify(receipt.account)],\n );\n if (receipt.reservation)\n await sql.query(\n `INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`,\n [\n accountId,\n receipt.reservation.id,\n JSON.stringify(receipt.reservation),\n ],\n );\n await sql.query(\n `INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::jsonb)`,\n [accountId, operationId, JSON.stringify(receipt)],\n );\n },\n });\n }),\n };\n};\n",
7
- "import { createCreditAccountLedger } from \"./prepaid\";\nimport {\n createPostgresCreditAccountStore,\n type CreditSql,\n type CreditSqlClient,\n} from \"./prepaidPostgres\";\nexport type CreditWork = {\n request: string;\n budget: number;\n charged: number;\n absorbed: number;\n status: \"running\" | \"completed\" | \"failed\";\n result: string | null;\n};\nconst namespace = (value: string) => {\n if (!/^[a-z][a-z0-9_]*$/.test(value))\n throw new Error(\"Invalid credit schema name\");\n return value;\n};\nconst id = (value: string) => {\n if (!value || value.length > 128) throw new Error(\"Invalid credit work ID\");\n};\nconst integer = (value: number) => {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new Error(\"Invalid credit work amount\");\n};\nexport const creditWorkPostgresSchemaSql = (schema = \"billing_credits\") => {\n const n = namespace(schema);\n return `CREATE TABLE IF NOT EXISTS ${n}.work (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), work_id text NOT NULL,\n state jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY(account_id, work_id)\n );\n CREATE TABLE IF NOT EXISTS ${n}.work_usage (\n account_id text NOT NULL, work_id text NOT NULL, event_id text NOT NULL,\n credits bigint NOT NULL CHECK (credits >= 0), charged bigint NOT NULL CHECK (charged >= 0 AND charged <= credits), reference text NOT NULL,\n PRIMARY KEY(account_id, work_id, event_id), FOREIGN KEY(account_id, work_id) REFERENCES ${n}.work(account_id, work_id)\n );`;\n};\n/** Durable execution claim + capped customer charge. A crash leaves work running\n * and its credits held: never automatically rerun an uncertain external effect.\n * Provider cost over the agreed budget is recorded as absorbed, not customer debt. */\nexport const createPostgresCreditWork = (\n client: CreditSqlClient,\n schema = \"billing_credits\",\n) => {\n const n = namespace(schema);\n const read = async (sql: CreditSql, accountId: string, workId: string) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.work WHERE account_id = $1 AND work_id = $2 FOR UPDATE`,\n [accountId, workId],\n );\n return rows[0]?.state as CreditWork | undefined;\n };\n const save = (\n sql: CreditSql,\n accountId: string,\n workId: string,\n state: CreditWork,\n ) =>\n sql.query(\n `UPDATE ${n}.work SET state = $3::jsonb, updated_at = now() WHERE account_id = $1 AND work_id = $2`,\n [accountId, workId, JSON.stringify(state)],\n );\n const ledger = (sql: CreditSql) =>\n createCreditAccountLedger(\n createPostgresCreditAccountStore(\n { ...sql, transaction: (run) => run(sql) },\n schema,\n ),\n );\n return {\n get: (accountId: string, workId: string) => {\n id(accountId);\n id(workId);\n return client.transaction(\n async (sql) => (await read(sql, accountId, workId)) ?? null,\n );\n },\n begin: (\n accountId: string,\n workId: string,\n request: string,\n budget: number,\n ) => {\n id(accountId);\n id(workId);\n integer(budget);\n if (!budget || !request || request.length > 256)\n throw new Error(\"Invalid credit work request\");\n return client.transaction(async (sql) => {\n // One account lock serializes competing claims, including absent work rows.\n await sql.query(\n `SELECT account_id FROM ${n}.accounts WHERE account_id = $1 FOR UPDATE`,\n [accountId],\n );\n const existing = await read(sql, accountId, workId);\n if (existing) {\n if (existing.request !== request || existing.budget !== budget)\n throw new Error(\"Credit work ID reused with different input\");\n return { fresh: false, work: existing };\n }\n await ledger(sql).execute(accountId, `work-reserve:${workId}`, {\n kind: \"reserve\",\n reservationId: `work:${workId}`,\n credits: budget,\n });\n const work: CreditWork = {\n request,\n budget,\n charged: 0,\n absorbed: 0,\n status: \"running\",\n result: null,\n };\n await sql.query(\n `INSERT INTO ${n}.work (account_id, work_id, state) VALUES ($1, $2, $3::jsonb)`,\n [accountId, workId, JSON.stringify(work)],\n );\n return { fresh: true, work };\n });\n },\n record: (\n accountId: string,\n workId: string,\n eventId: string,\n credits: number,\n reference: string,\n ) => {\n id(accountId);\n id(workId);\n id(eventId);\n integer(credits);\n id(reference);\n return client.transaction(async (sql) => {\n const work = await read(sql, accountId, workId);\n if (!work) throw new Error(\"Credit work does not exist\");\n const { rows } = await sql.query(\n `SELECT credits, charged, reference FROM ${n}.work_usage WHERE account_id = $1 AND work_id = $2 AND event_id = $3`,\n [accountId, workId, eventId],\n );\n const previous = rows[0];\n if (previous) {\n if (\n Number(previous.credits) !== credits ||\n previous.reference !== reference\n )\n throw new Error(\"Credit work event reused with different input\");\n return { charged: Number(previous.charged), fresh: false };\n }\n if (work.status !== \"running\")\n throw new Error(\"Credit work already finished\");\n const charged = Math.min(credits, work.budget - work.charged);\n work.charged += charged;\n work.absorbed += credits - charged;\n integer(work.charged);\n integer(work.absorbed);\n await sql.query(\n `INSERT INTO ${n}.work_usage (account_id, work_id, event_id, credits, charged, reference) VALUES ($1, $2, $3, $4, $5, $6)`,\n [accountId, workId, eventId, credits, charged, reference],\n );\n await save(sql, accountId, workId, work);\n return { charged, fresh: true };\n });\n },\n finish: (\n accountId: string,\n workId: string,\n result: string,\n failed = false,\n ) => {\n id(accountId);\n id(workId);\n return client.transaction(async (sql) => {\n // Match begin's account -> work lock order to avoid a retry/finalize deadlock.\n await sql.query(\n `SELECT account_id FROM ${n}.accounts WHERE account_id = $1 FOR UPDATE`,\n [accountId],\n );\n const work = await read(sql, accountId, workId);\n if (!work) throw new Error(\"Credit work does not exist\");\n if (work.status !== \"running\") return work;\n await ledger(sql).execute(accountId, `work-settle:${workId}`, {\n kind: \"settle\",\n reservationId: `work:${workId}`,\n credits: work.charged,\n });\n work.status = failed ? \"failed\" : \"completed\";\n work.result = result;\n await save(sql, accountId, workId, work);\n return work;\n });\n },\n };\n};\n"
6
+ "import {\n validateCreditAccount,\n type CreditAccount,\n type CreditAccountStore,\n type CreditReceipt,\n type CreditReservation,\n} from \"./prepaid\";\nexport type CreditSql = {\n query: (\n query: string,\n parameters: readonly unknown[],\n ) => Promise<{ rows: Record<string, unknown>[] }>;\n};\nexport type CreditSqlClient = CreditSql & {\n transaction: <T>(run: (sql: CreditSql) => Promise<T>) => Promise<T>;\n};\nconst namespace = (value: string) => {\n if (!/^[a-z][a-z0-9_]*$/.test(value))\n throw new Error(\"Invalid credit schema name\");\n return value;\n};\nexport const creditAccountPostgresSchemaSql = (schema = \"billing_credits\") => {\n const n = namespace(schema);\n return `CREATE SCHEMA IF NOT EXISTS ${n};\nCREATE TABLE IF NOT EXISTS ${n}.accounts (\n account_id text PRIMARY KEY, state jsonb NOT NULL, initial_state jsonb NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE TABLE IF NOT EXISTS ${n}.operations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), operation_id text NOT NULL,\n receipt jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, operation_id)\n);\nCREATE TABLE IF NOT EXISTS ${n}.reservations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), reservation_id text NOT NULL,\n state jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, reservation_id)\n);`;\n};\nexport const createPostgresCreditAccountStore = (\n client: CreditSqlClient,\n schema = \"billing_credits\",\n): CreditAccountStore => {\n const n = namespace(schema);\n const read = async (sql: CreditSql, accountId: string, lock = false) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.accounts WHERE account_id = $1${lock ? \" FOR UPDATE\" : \"\"}`,\n [accountId],\n );\n if (!rows[0]) return null;\n const state = rows[0].state as CreditAccount;\n validateCreditAccount(state);\n return state;\n };\n return {\n initialize: async (accountId, seed) => {\n validateCreditAccount(seed);\n await client.query(\n `INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::text::jsonb, $2::text::jsonb) ON CONFLICT DO NOTHING`,\n [accountId, JSON.stringify(seed)],\n );\n },\n read: (accountId) => read(client, accountId),\n transaction: (accountId, run) =>\n client.transaction(async (sql) => {\n const account = await read(sql, accountId, true);\n if (!account) throw new Error(\"Credit account is not initialized\");\n return run({\n account,\n receipt: async (operationId) => {\n const { rows } = await sql.query(\n `SELECT receipt FROM ${n}.operations WHERE account_id = $1 AND operation_id = $2`,\n [accountId, operationId],\n );\n return (rows[0]?.receipt as CreditReceipt | undefined) ?? null;\n },\n reservation: async (id) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.reservations WHERE account_id = $1 AND reservation_id = $2`,\n [accountId, id],\n );\n return (rows[0]?.state as CreditReservation | undefined) ?? null;\n },\n save: async (operationId, receipt) => {\n await sql.query(\n `UPDATE ${n}.accounts SET state = $2::text::jsonb, updated_at = now() WHERE account_id = $1`,\n [accountId, JSON.stringify(receipt.account)],\n );\n if (receipt.reservation)\n await sql.query(\n `INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::text::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`,\n [\n accountId,\n receipt.reservation.id,\n JSON.stringify(receipt.reservation),\n ],\n );\n await sql.query(\n `INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::text::jsonb)`,\n [accountId, operationId, JSON.stringify(receipt)],\n );\n },\n });\n }),\n };\n};\n",
7
+ "import { createCreditAccountLedger } from \"./prepaid\";\nimport {\n createPostgresCreditAccountStore,\n type CreditSql,\n type CreditSqlClient,\n} from \"./prepaidPostgres\";\nexport type CreditWork = {\n request: string;\n budget: number;\n charged: number;\n absorbed: number;\n status: \"running\" | \"completed\" | \"failed\";\n result: string | null;\n};\nconst namespace = (value: string) => {\n if (!/^[a-z][a-z0-9_]*$/.test(value))\n throw new Error(\"Invalid credit schema name\");\n return value;\n};\nconst id = (value: string) => {\n if (!value || value.length > 128) throw new Error(\"Invalid credit work ID\");\n};\nconst integer = (value: number) => {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new Error(\"Invalid credit work amount\");\n};\nexport const creditWorkPostgresSchemaSql = (schema = \"billing_credits\") => {\n const n = namespace(schema);\n return `CREATE TABLE IF NOT EXISTS ${n}.work (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), work_id text NOT NULL,\n state jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY(account_id, work_id)\n );\n CREATE TABLE IF NOT EXISTS ${n}.work_usage (\n account_id text NOT NULL, work_id text NOT NULL, event_id text NOT NULL,\n credits bigint NOT NULL CHECK (credits >= 0), charged bigint NOT NULL CHECK (charged >= 0 AND charged <= credits), reference text NOT NULL,\n PRIMARY KEY(account_id, work_id, event_id), FOREIGN KEY(account_id, work_id) REFERENCES ${n}.work(account_id, work_id)\n );`;\n};\n/** Durable execution claim + capped customer charge. A crash leaves work running\n * and its credits held: never automatically rerun an uncertain external effect.\n * Provider cost over the agreed budget is recorded as absorbed, not customer debt. */\nexport const createPostgresCreditWork = (\n client: CreditSqlClient,\n schema = \"billing_credits\",\n) => {\n const n = namespace(schema);\n const read = async (sql: CreditSql, accountId: string, workId: string) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.work WHERE account_id = $1 AND work_id = $2 FOR UPDATE`,\n [accountId, workId],\n );\n return rows[0]?.state as CreditWork | undefined;\n };\n const save = (\n sql: CreditSql,\n accountId: string,\n workId: string,\n state: CreditWork,\n ) =>\n sql.query(\n `UPDATE ${n}.work SET state = $3::text::jsonb, updated_at = now() WHERE account_id = $1 AND work_id = $2`,\n [accountId, workId, JSON.stringify(state)],\n );\n const ledger = (sql: CreditSql) =>\n createCreditAccountLedger(\n createPostgresCreditAccountStore(\n { ...sql, transaction: (run) => run(sql) },\n schema,\n ),\n );\n return {\n get: (accountId: string, workId: string) => {\n id(accountId);\n id(workId);\n return client.transaction(\n async (sql) => (await read(sql, accountId, workId)) ?? null,\n );\n },\n begin: (\n accountId: string,\n workId: string,\n request: string,\n budget: number,\n ) => {\n id(accountId);\n id(workId);\n integer(budget);\n if (!budget || !request || request.length > 256)\n throw new Error(\"Invalid credit work request\");\n return client.transaction(async (sql) => {\n // One account lock serializes competing claims, including absent work rows.\n await sql.query(\n `SELECT account_id FROM ${n}.accounts WHERE account_id = $1 FOR UPDATE`,\n [accountId],\n );\n const existing = await read(sql, accountId, workId);\n if (existing) {\n if (existing.request !== request || existing.budget !== budget)\n throw new Error(\"Credit work ID reused with different input\");\n return { fresh: false, work: existing };\n }\n await ledger(sql).execute(accountId, `work-reserve:${workId}`, {\n kind: \"reserve\",\n reservationId: `work:${workId}`,\n credits: budget,\n });\n const work: CreditWork = {\n request,\n budget,\n charged: 0,\n absorbed: 0,\n status: \"running\",\n result: null,\n };\n await sql.query(\n `INSERT INTO ${n}.work (account_id, work_id, state) VALUES ($1, $2, $3::text::jsonb)`,\n [accountId, workId, JSON.stringify(work)],\n );\n return { fresh: true, work };\n });\n },\n record: (\n accountId: string,\n workId: string,\n eventId: string,\n credits: number,\n reference: string,\n ) => {\n id(accountId);\n id(workId);\n id(eventId);\n integer(credits);\n id(reference);\n return client.transaction(async (sql) => {\n const work = await read(sql, accountId, workId);\n if (!work) throw new Error(\"Credit work does not exist\");\n const { rows } = await sql.query(\n `SELECT credits, charged, reference FROM ${n}.work_usage WHERE account_id = $1 AND work_id = $2 AND event_id = $3`,\n [accountId, workId, eventId],\n );\n const previous = rows[0];\n if (previous) {\n if (\n Number(previous.credits) !== credits ||\n previous.reference !== reference\n )\n throw new Error(\"Credit work event reused with different input\");\n return { charged: Number(previous.charged), fresh: false };\n }\n if (work.status !== \"running\")\n throw new Error(\"Credit work already finished\");\n const charged = Math.min(credits, work.budget - work.charged);\n work.charged += charged;\n work.absorbed += credits - charged;\n integer(work.charged);\n integer(work.absorbed);\n await sql.query(\n `INSERT INTO ${n}.work_usage (account_id, work_id, event_id, credits, charged, reference) VALUES ($1, $2, $3, $4, $5, $6)`,\n [accountId, workId, eventId, credits, charged, reference],\n );\n await save(sql, accountId, workId, work);\n return { charged, fresh: true };\n });\n },\n finish: (\n accountId: string,\n workId: string,\n result: string,\n failed = false,\n ) => {\n id(accountId);\n id(workId);\n return client.transaction(async (sql) => {\n // Match begin's account -> work lock order to avoid a retry/finalize deadlock.\n await sql.query(\n `SELECT account_id FROM ${n}.accounts WHERE account_id = $1 FOR UPDATE`,\n [accountId],\n );\n const work = await read(sql, accountId, workId);\n if (!work) throw new Error(\"Credit work does not exist\");\n if (work.status !== \"running\") return work;\n await ledger(sql).execute(accountId, `work-settle:${workId}`, {\n kind: \"settle\",\n reservationId: `work:${workId}`,\n credits: work.charged,\n });\n work.status = failed ? \"failed\" : \"completed\";\n work.result = result;\n await save(sql, accountId, workId, work);\n return work;\n });\n },\n };\n};\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "AA6CCB44B9F64D8964756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "C694B3533E4362E464756E2164756E21",
11
11
  "names": []
12
12
  }
@@ -295,7 +295,7 @@ var createPostgresCreditAccountStore = (client, schema = "billing_credits") => {
295
295
  return {
296
296
  initialize: async (accountId, seed) => {
297
297
  validateCreditAccount(seed);
298
- await client.query(`INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::jsonb, $2::jsonb) ON CONFLICT DO NOTHING`, [accountId, JSON.stringify(seed)]);
298
+ await client.query(`INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::text::jsonb, $2::text::jsonb) ON CONFLICT DO NOTHING`, [accountId, JSON.stringify(seed)]);
299
299
  },
300
300
  read: (accountId) => read(client, accountId),
301
301
  transaction: (accountId, run) => client.transaction(async (sql) => {
@@ -313,14 +313,14 @@ var createPostgresCreditAccountStore = (client, schema = "billing_credits") => {
313
313
  return rows[0]?.state ?? null;
314
314
  },
315
315
  save: async (operationId, receipt) => {
316
- await sql.query(`UPDATE ${n}.accounts SET state = $2::jsonb, updated_at = now() WHERE account_id = $1`, [accountId, JSON.stringify(receipt.account)]);
316
+ await sql.query(`UPDATE ${n}.accounts SET state = $2::text::jsonb, updated_at = now() WHERE account_id = $1`, [accountId, JSON.stringify(receipt.account)]);
317
317
  if (receipt.reservation)
318
- await sql.query(`INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`, [
318
+ await sql.query(`INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::text::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`, [
319
319
  accountId,
320
320
  receipt.reservation.id,
321
321
  JSON.stringify(receipt.reservation)
322
322
  ]);
323
- await sql.query(`INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::jsonb)`, [accountId, operationId, JSON.stringify(receipt)]);
323
+ await sql.query(`INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::text::jsonb)`, [accountId, operationId, JSON.stringify(receipt)]);
324
324
  }
325
325
  });
326
326
  })
@@ -331,5 +331,5 @@ export {
331
331
  creditAccountPostgresSchemaSql
332
332
  };
333
333
 
334
- //# debugId=D56D4E1DB5A095C464756E2164756E21
334
+ //# debugId=9E43895B9F56D08664756E2164756E21
335
335
  //# sourceMappingURL=prepaidPostgres.js.map
@@ -3,9 +3,9 @@
3
3
  "sources": ["../src/prepaid.ts", "../src/prepaidPostgres.ts"],
4
4
  "sourcesContent": [
5
5
  "/** Service-credit accounting. Host authorization and payment verification happen\n * before these commands. Every command and its receipt commit in one transaction. */\nexport type CreditAccount = {\n periodId: string;\n periodEnd: string | null;\n periodAllowance: number;\n periodRemaining: number;\n purchasedRemaining: number;\n promotionalRemaining: number;\n debt: number;\n reserved: number;\n consumed: number;\n};\nexport type CreditAllocation = {\n period: number;\n purchased: number;\n promotional: number;\n};\nexport type CreditReservation = {\n id: string;\n periodId: string;\n allocation: CreditAllocation;\n status: \"reserved\" | \"settled\" | \"released\";\n charged: number | null;\n};\nexport type CreditCommand =\n | { kind: \"grant\"; bucket: \"purchased\" | \"promotional\"; credits: number }\n | { kind: \"reverse\"; bucket: \"purchased\" | \"promotional\"; credits: number }\n | {\n kind: \"period\";\n periodId: string;\n periodEnd: string | null;\n allowance: number;\n }\n | { kind: \"reserve\"; reservationId: string; credits: number }\n | { kind: \"settle\"; reservationId: string; credits: number }\n | { kind: \"release\"; reservationId: string }\n | { kind: \"debit\"; credits: number; allowDebt?: boolean; reference?: string };\nexport type CreditReceipt = {\n command: CreditCommand;\n account: CreditAccount;\n reservation?: CreditReservation;\n};\nexport type CreditTransaction = {\n account: CreditAccount;\n receipt: (operationId: string) => Promise<CreditReceipt | null>;\n reservation: (id: string) => Promise<CreditReservation | null>;\n save: (operationId: string, receipt: CreditReceipt) => Promise<void>;\n};\nexport type CreditAccountStore = {\n /** Persist seed once. Never overwrite an existing account. */\n initialize: (accountId: string, seed: CreditAccount) => Promise<void>;\n read: (accountId: string) => Promise<CreditAccount | null>;\n /** Lock one account, serialize its commands across processes, and roll back\n * account, reservation and receipt together if the callback fails. */\n transaction: <T>(\n accountId: string,\n run: (tx: CreditTransaction) => Promise<T>,\n ) => Promise<T>;\n};\nconst integer = (value: number) => {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new Error(\"Credits must be nonnegative safe integers\");\n return value;\n};\nconst periodIdentity = (value: string) => {\n if (\n !Number.isFinite(Date.parse(value)) ||\n new Date(value).toISOString() !== value\n )\n throw new Error(\"Credit period ID must be an ISO UTC start timestamp\");\n return value;\n};\nconst identity = (value: string) => {\n if (!value || value.length > 256)\n throw new Error(\"Credit identity is invalid\");\n return value;\n};\nexport const validateCreditAccount = (account: CreditAccount) => {\n periodIdentity(account.periodId);\n if (\n account.periodEnd !== null &&\n periodIdentity(account.periodEnd) <= account.periodId\n )\n throw new Error(\"Credit period end must follow its start\");\n for (const key of [\n \"periodAllowance\",\n \"periodRemaining\",\n \"purchasedRemaining\",\n \"promotionalRemaining\",\n \"debt\",\n \"reserved\",\n \"consumed\",\n ] as const)\n integer(account[key]);\n integer(\n account.periodRemaining +\n account.purchasedRemaining +\n account.promotionalRemaining,\n );\n if (account.periodRemaining > account.periodAllowance)\n throw new Error(\"Period balance exceeds its allowance\");\n};\nexport const availableCredits = (account: CreditAccount) =>\n Math.max(\n 0,\n account.periodRemaining +\n account.purchasedRemaining +\n account.promotionalRemaining -\n account.debt,\n );\n\n/** Preserve the currently observable legacy balance without inventing purchase\n * provenance. Historical mixed bonus grants remain promotional carryover. */\nexport const migrateLegacyCreditAccount = (input: {\n periodId: string;\n periodEnd?: string | null;\n periodAllowance: number;\n bonusCredits: number;\n consumed: number;\n}): CreditAccount => {\n integer(input.periodAllowance);\n integer(input.consumed);\n if (!Number.isSafeInteger(input.bonusCredits))\n throw new Error(\"Invalid legacy bonus credits\");\n const account: CreditAccount = {\n periodId: input.periodId,\n periodEnd: input.periodEnd ?? null,\n periodAllowance: input.periodAllowance,\n periodRemaining: Math.max(0, input.periodAllowance - input.consumed),\n purchasedRemaining: 0,\n promotionalRemaining: Math.max(\n 0,\n input.bonusCredits - Math.max(0, input.consumed - input.periodAllowance),\n ),\n debt:\n Math.max(0, -input.bonusCredits) +\n Math.max(\n 0,\n input.consumed -\n input.periodAllowance -\n Math.max(0, input.bonusCredits),\n ),\n reserved: 0,\n consumed: input.consumed,\n };\n validateCreditAccount(account);\n return account;\n};\nconst allocationTotal = (value: CreditAllocation) =>\n value.period + value.promotional + value.purchased;\nconst take = (account: CreditAccount, credits: number): CreditAllocation => {\n // A reversal liability consumes existing available credits exactly once.\n let owed = account.debt;\n for (const key of [\n \"periodRemaining\",\n \"promotionalRemaining\",\n \"purchasedRemaining\",\n ] as const) {\n const covered = Math.min(account[key], owed);\n account[key] -= covered;\n owed -= covered;\n }\n account.debt = owed;\n const period = Math.min(account.periodRemaining, credits);\n const promotional = Math.min(account.promotionalRemaining, credits - period);\n const purchased = Math.min(\n account.purchasedRemaining,\n credits - period - promotional,\n );\n account.periodRemaining -= period;\n account.promotionalRemaining -= promotional;\n account.purchasedRemaining -= purchased;\n return { period, promotional, purchased };\n};\nconst grant = (\n account: CreditAccount,\n key: \"periodRemaining\" | \"purchasedRemaining\" | \"promotionalRemaining\",\n credits: number,\n) => {\n const covered = Math.min(account.debt, credits);\n account.debt -= covered;\n account[key] = integer(account[key] + credits - covered);\n};\nconst normalize = (command: CreditCommand): CreditCommand => {\n switch (command.kind) {\n case \"grant\":\n case \"reverse\":\n if (command.bucket !== \"purchased\" && command.bucket !== \"promotional\")\n throw new Error(\"Invalid credit bucket\");\n return {\n kind: command.kind,\n bucket: command.bucket,\n credits: integer(command.credits),\n };\n case \"period\":\n return {\n kind: command.kind,\n periodId: periodIdentity(command.periodId),\n periodEnd:\n command.periodEnd === null ? null : periodIdentity(command.periodEnd),\n allowance: integer(command.allowance),\n };\n case \"reserve\":\n case \"settle\":\n return {\n kind: command.kind,\n reservationId: identity(command.reservationId),\n credits: integer(command.credits),\n };\n case \"release\":\n return {\n kind: command.kind,\n reservationId: identity(command.reservationId),\n };\n case \"debit\":\n return {\n kind: command.kind,\n credits: integer(command.credits),\n allowDebt: command.allowDebt === true,\n ...(command.reference === undefined\n ? {}\n : { reference: identity(command.reference) }),\n };\n default:\n throw new Error(\"Invalid credit command\");\n }\n};\nexport const createCreditAccountLedger = (store: CreditAccountStore) => ({\n initialize: async (accountId: string, seed: CreditAccount) => {\n identity(accountId);\n validateCreditAccount(seed);\n await store.initialize(accountId, seed);\n },\n receipt: (accountId: string, operationId: string) =>\n store.transaction(identity(accountId), (tx) =>\n tx.receipt(identity(operationId)),\n ),\n balance: (accountId: string) => store.read(identity(accountId)),\n execute: async (\n accountId: string,\n operationId: string,\n input: CreditCommand,\n ): Promise<CreditReceipt> => {\n identity(accountId);\n identity(operationId);\n const command = normalize(input);\n return store.transaction(accountId, async (tx) => {\n const previous = await tx.receipt(operationId);\n if (previous) {\n if (\n JSON.stringify(normalize(previous.command)) !==\n JSON.stringify(command)\n )\n throw new Error(\"Credit operation ID reused with different input\");\n return previous;\n }\n const account = { ...tx.account };\n validateCreditAccount(account);\n let reservation: CreditReservation | undefined;\n switch (command.kind) {\n case \"grant\":\n grant(\n account,\n command.bucket === \"purchased\"\n ? \"purchasedRemaining\"\n : \"promotionalRemaining\",\n command.credits,\n );\n break;\n case \"reverse\": {\n const key =\n command.bucket === \"purchased\"\n ? \"purchasedRemaining\"\n : \"promotionalRemaining\";\n const covered = Math.min(account[key], command.credits);\n account[key] -= covered;\n account.debt += command.credits - covered;\n break;\n }\n case \"period\":\n if (command.periodId < account.periodId)\n throw new Error(\"Credit period cannot move backwards\");\n if (command.periodId === account.periodId) {\n // Upgrades add only the increase. Downgrades take effect next period.\n const increase = Math.max(\n 0,\n command.allowance - account.periodAllowance,\n );\n if (account.periodEnd === null)\n account.periodEnd = command.periodEnd;\n account.periodAllowance += increase;\n grant(account, \"periodRemaining\", increase);\n } else {\n account.periodId = command.periodId;\n account.periodEnd = command.periodEnd;\n account.periodAllowance = command.allowance;\n account.periodRemaining = 0;\n account.consumed = 0;\n grant(account, \"periodRemaining\", command.allowance);\n }\n break;\n case \"debit\": {\n if (!command.allowDebt && availableCredits(account) < command.credits)\n throw new Error(\"Insufficient credits\");\n const used = allocationTotal(take(account, command.credits));\n account.debt += command.credits - used;\n account.consumed += command.credits;\n break;\n }\n case \"reserve\":\n if (command.credits === 0)\n throw new Error(\"Reservation must be positive\");\n if (await tx.reservation(command.reservationId))\n throw new Error(\"Credit reservation already exists\");\n if (availableCredits(account) < command.credits)\n throw new Error(\"Insufficient credits\");\n reservation = {\n id: command.reservationId,\n periodId: account.periodId,\n allocation: take(account, command.credits),\n status: \"reserved\",\n charged: null,\n };\n account.reserved += command.credits;\n break;\n case \"settle\":\n case \"release\": {\n const held = await tx.reservation(command.reservationId);\n if (!held || held.status !== \"reserved\")\n throw new Error(\"Credit reservation is not active\");\n const total = allocationTotal(held.allocation);\n const charged = command.kind === \"settle\" ? command.credits : 0;\n if (charged > total)\n throw new Error(\"Charge exceeds reserved credits\");\n let refund = total - charged;\n for (const [bucket, key] of [\n [\"purchased\", \"purchasedRemaining\"],\n [\"promotional\", \"promotionalRemaining\"],\n [\"period\", \"periodRemaining\"],\n ] as const) {\n const restored = Math.min(held.allocation[bucket], refund);\n refund -= restored;\n if (bucket !== \"period\" || held.periodId === account.periodId)\n grant(account, key, restored);\n }\n account.reserved -= total;\n if (held.periodId === account.periodId) account.consumed += charged;\n reservation = {\n ...held,\n status: command.kind === \"settle\" ? \"settled\" : \"released\",\n charged,\n };\n break;\n }\n }\n validateCreditAccount(account);\n const receipt: CreditReceipt = {\n command,\n account,\n ...(reservation ? { reservation } : {}),\n };\n await tx.save(operationId, receipt);\n return receipt;\n });\n },\n});\n\n/** Portal access and funded MCP access are independent. Period-only free\n * allowances do not silently unlock prepaid work; carryover grants may. */\nexport const creditEntitlements = (input: {\n subscribed: boolean;\n prepaidEnabled: boolean;\n account: CreditAccount | null;\n}) => {\n const funded =\n input.prepaidEnabled &&\n input.account !== null &&\n input.account.purchasedRemaining + input.account.promotionalRemaining > 0 &&\n availableCredits(input.account) > 0;\n return {\n portal: input.subscribed,\n mcp: input.subscribed\n ? (\"member\" as const)\n : funded\n ? (\"prepaid\" as const)\n : (\"free\" as const),\n };\n};\n",
6
- "import {\n validateCreditAccount,\n type CreditAccount,\n type CreditAccountStore,\n type CreditReceipt,\n type CreditReservation,\n} from \"./prepaid\";\nexport type CreditSql = {\n query: (\n query: string,\n parameters: readonly unknown[],\n ) => Promise<{ rows: Record<string, unknown>[] }>;\n};\nexport type CreditSqlClient = CreditSql & {\n transaction: <T>(run: (sql: CreditSql) => Promise<T>) => Promise<T>;\n};\nconst namespace = (value: string) => {\n if (!/^[a-z][a-z0-9_]*$/.test(value))\n throw new Error(\"Invalid credit schema name\");\n return value;\n};\nexport const creditAccountPostgresSchemaSql = (schema = \"billing_credits\") => {\n const n = namespace(schema);\n return `CREATE SCHEMA IF NOT EXISTS ${n};\nCREATE TABLE IF NOT EXISTS ${n}.accounts (\n account_id text PRIMARY KEY, state jsonb NOT NULL, initial_state jsonb NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE TABLE IF NOT EXISTS ${n}.operations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), operation_id text NOT NULL,\n receipt jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, operation_id)\n);\nCREATE TABLE IF NOT EXISTS ${n}.reservations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), reservation_id text NOT NULL,\n state jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, reservation_id)\n);`;\n};\nexport const createPostgresCreditAccountStore = (\n client: CreditSqlClient,\n schema = \"billing_credits\",\n): CreditAccountStore => {\n const n = namespace(schema);\n const read = async (sql: CreditSql, accountId: string, lock = false) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.accounts WHERE account_id = $1${lock ? \" FOR UPDATE\" : \"\"}`,\n [accountId],\n );\n if (!rows[0]) return null;\n const state = rows[0].state as CreditAccount;\n validateCreditAccount(state);\n return state;\n };\n return {\n initialize: async (accountId, seed) => {\n validateCreditAccount(seed);\n await client.query(\n `INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::jsonb, $2::jsonb) ON CONFLICT DO NOTHING`,\n [accountId, JSON.stringify(seed)],\n );\n },\n read: (accountId) => read(client, accountId),\n transaction: (accountId, run) =>\n client.transaction(async (sql) => {\n const account = await read(sql, accountId, true);\n if (!account) throw new Error(\"Credit account is not initialized\");\n return run({\n account,\n receipt: async (operationId) => {\n const { rows } = await sql.query(\n `SELECT receipt FROM ${n}.operations WHERE account_id = $1 AND operation_id = $2`,\n [accountId, operationId],\n );\n return (rows[0]?.receipt as CreditReceipt | undefined) ?? null;\n },\n reservation: async (id) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.reservations WHERE account_id = $1 AND reservation_id = $2`,\n [accountId, id],\n );\n return (rows[0]?.state as CreditReservation | undefined) ?? null;\n },\n save: async (operationId, receipt) => {\n await sql.query(\n `UPDATE ${n}.accounts SET state = $2::jsonb, updated_at = now() WHERE account_id = $1`,\n [accountId, JSON.stringify(receipt.account)],\n );\n if (receipt.reservation)\n await sql.query(\n `INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`,\n [\n accountId,\n receipt.reservation.id,\n JSON.stringify(receipt.reservation),\n ],\n );\n await sql.query(\n `INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::jsonb)`,\n [accountId, operationId, JSON.stringify(receipt)],\n );\n },\n });\n }),\n };\n};\n"
6
+ "import {\n validateCreditAccount,\n type CreditAccount,\n type CreditAccountStore,\n type CreditReceipt,\n type CreditReservation,\n} from \"./prepaid\";\nexport type CreditSql = {\n query: (\n query: string,\n parameters: readonly unknown[],\n ) => Promise<{ rows: Record<string, unknown>[] }>;\n};\nexport type CreditSqlClient = CreditSql & {\n transaction: <T>(run: (sql: CreditSql) => Promise<T>) => Promise<T>;\n};\nconst namespace = (value: string) => {\n if (!/^[a-z][a-z0-9_]*$/.test(value))\n throw new Error(\"Invalid credit schema name\");\n return value;\n};\nexport const creditAccountPostgresSchemaSql = (schema = \"billing_credits\") => {\n const n = namespace(schema);\n return `CREATE SCHEMA IF NOT EXISTS ${n};\nCREATE TABLE IF NOT EXISTS ${n}.accounts (\n account_id text PRIMARY KEY, state jsonb NOT NULL, initial_state jsonb NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE TABLE IF NOT EXISTS ${n}.operations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), operation_id text NOT NULL,\n receipt jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, operation_id)\n);\nCREATE TABLE IF NOT EXISTS ${n}.reservations (\n account_id text NOT NULL REFERENCES ${n}.accounts(account_id), reservation_id text NOT NULL,\n state jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (account_id, reservation_id)\n);`;\n};\nexport const createPostgresCreditAccountStore = (\n client: CreditSqlClient,\n schema = \"billing_credits\",\n): CreditAccountStore => {\n const n = namespace(schema);\n const read = async (sql: CreditSql, accountId: string, lock = false) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.accounts WHERE account_id = $1${lock ? \" FOR UPDATE\" : \"\"}`,\n [accountId],\n );\n if (!rows[0]) return null;\n const state = rows[0].state as CreditAccount;\n validateCreditAccount(state);\n return state;\n };\n return {\n initialize: async (accountId, seed) => {\n validateCreditAccount(seed);\n await client.query(\n `INSERT INTO ${n}.accounts (account_id, state, initial_state) VALUES ($1, $2::text::jsonb, $2::text::jsonb) ON CONFLICT DO NOTHING`,\n [accountId, JSON.stringify(seed)],\n );\n },\n read: (accountId) => read(client, accountId),\n transaction: (accountId, run) =>\n client.transaction(async (sql) => {\n const account = await read(sql, accountId, true);\n if (!account) throw new Error(\"Credit account is not initialized\");\n return run({\n account,\n receipt: async (operationId) => {\n const { rows } = await sql.query(\n `SELECT receipt FROM ${n}.operations WHERE account_id = $1 AND operation_id = $2`,\n [accountId, operationId],\n );\n return (rows[0]?.receipt as CreditReceipt | undefined) ?? null;\n },\n reservation: async (id) => {\n const { rows } = await sql.query(\n `SELECT state FROM ${n}.reservations WHERE account_id = $1 AND reservation_id = $2`,\n [accountId, id],\n );\n return (rows[0]?.state as CreditReservation | undefined) ?? null;\n },\n save: async (operationId, receipt) => {\n await sql.query(\n `UPDATE ${n}.accounts SET state = $2::text::jsonb, updated_at = now() WHERE account_id = $1`,\n [accountId, JSON.stringify(receipt.account)],\n );\n if (receipt.reservation)\n await sql.query(\n `INSERT INTO ${n}.reservations (account_id, reservation_id, state) VALUES ($1, $2, $3::text::jsonb) ON CONFLICT (account_id, reservation_id) DO UPDATE SET state = EXCLUDED.state, updated_at = now()`,\n [\n accountId,\n receipt.reservation.id,\n JSON.stringify(receipt.reservation),\n ],\n );\n await sql.query(\n `INSERT INTO ${n}.operations (account_id, operation_id, receipt) VALUES ($1, $2, $3::text::jsonb)`,\n [accountId, operationId, JSON.stringify(receipt)],\n );\n },\n });\n }),\n };\n};\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "D56D4E1DB5A095C464756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "9E43895B9F56D08664756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -0,0 +1,65 @@
1
+ /** Public customer reports. No payment tokens, vault references, or provider costs. */
2
+ export type BillingStatus = {
3
+ portalAccess: boolean;
4
+ subscription: {
5
+ status: string;
6
+ renewsAt: string | null;
7
+ cancelAtPeriodEnd: boolean;
8
+ } | null;
9
+ credits: {
10
+ remaining: number;
11
+ reserved: number;
12
+ purchased: number;
13
+ promotional: number;
14
+ debt: number;
15
+ };
16
+ automaticRefill: false;
17
+ };
18
+ export type BillingReceipt = {
19
+ id: string;
20
+ issuedAt: string;
21
+ currency: string;
22
+ amountCents: number;
23
+ refundedAmountCents: number;
24
+ source: "credit_purchase" | "initial" | "plan_change" | "renewal";
25
+ status: "paid" | "partially_refunded" | "refunded";
26
+ };
27
+ export type ReceiptCursor = {
28
+ at: string;
29
+ id: string;
30
+ };
31
+ export type ReceiptPageRequest = {
32
+ limit: number;
33
+ cursor: ReceiptCursor | null;
34
+ };
35
+ export type ReceiptPage = {
36
+ receipts: BillingReceipt[];
37
+ nextCursor: string | null;
38
+ };
39
+ export type UsageRange = {
40
+ from: string;
41
+ to: string;
42
+ };
43
+ export type CustomerUsageReport = {
44
+ from: string;
45
+ to: string;
46
+ creditsConsumed: number;
47
+ events: number;
48
+ byDay: {
49
+ day: string;
50
+ credits: number;
51
+ events: number;
52
+ }[];
53
+ byFeature: {
54
+ feature: string;
55
+ credits: number;
56
+ events: number;
57
+ }[];
58
+ };
59
+ export declare const parseUsageRange: (input: unknown, now?: Date) => UsageRange;
60
+ /** Cursor is a position, never authorization. Consumers must filter by account. */
61
+ export declare const encodeReceiptCursor: (cursor: ReceiptCursor) => string;
62
+ export declare const parseReceiptPage: (input: unknown) => ReceiptPageRequest;
63
+ export declare const projectBillingStatus: (value: BillingStatus) => BillingStatus;
64
+ export declare const projectReceiptPage: (value: ReceiptPage, limit: number) => ReceiptPage;
65
+ export declare const projectUsageReport: (value: CustomerUsageReport, range: UsageRange) => CustomerUsageReport;
@@ -0,0 +1,146 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/reports.ts
18
+ var DAY_MS = 86400000;
19
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20
+ var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21
+ var only = (input, keys) => {
22
+ if (!record(input) || Object.keys(input).some((key) => !keys.includes(key)))
23
+ throw new Error("Invalid report input");
24
+ return input;
25
+ };
26
+ var integer = (value) => {
27
+ if (!Number.isSafeInteger(value) || value < 0)
28
+ throw new Error("Invalid report amount");
29
+ return value;
30
+ };
31
+ var iso = (value) => {
32
+ if (!Number.isFinite(Date.parse(value)) || new Date(value).toISOString() !== value)
33
+ throw new Error("Invalid report timestamp");
34
+ return value;
35
+ };
36
+ var day = (value) => {
37
+ if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value) || new Date(value + "T00:00:00.000Z").toISOString().slice(0, 10) !== value)
38
+ throw new Error("Expected a UTC calendar date");
39
+ return value;
40
+ };
41
+ var parseUsageRange = (input, now = new Date) => {
42
+ const args = only(input, ["from", "to"]);
43
+ const tomorrow = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()) + DAY_MS);
44
+ const to = day(args.to ?? tomorrow.toISOString().slice(0, 10));
45
+ const from = day(args.from ?? new Date(Date.parse(to) - 30 * DAY_MS).toISOString().slice(0, 10));
46
+ const duration = Date.parse(to) - Date.parse(from);
47
+ if (duration <= 0 || duration > 90 * DAY_MS || Date.parse(to) > tomorrow.getTime())
48
+ throw new Error("Choose a range of 1\u201390 UTC days, ending no later than tomorrow");
49
+ return { from, to };
50
+ };
51
+ var encodeReceiptCursor = (cursor) => {
52
+ if (!UUID.test(cursor.id))
53
+ throw new Error("Invalid receipt cursor");
54
+ return Buffer.from(JSON.stringify({ at: iso(cursor.at), id: cursor.id })).toString("base64url");
55
+ };
56
+ var parseReceiptPage = (input) => {
57
+ const args = only(input, ["limit", "cursor"]);
58
+ const limit = args.limit ?? 20;
59
+ if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > 50)
60
+ throw new Error("Receipt limit must be 1\u201350");
61
+ if (args.cursor === undefined || args.cursor === null)
62
+ return { limit, cursor: null };
63
+ if (typeof args.cursor !== "string" || args.cursor.length > 256 || !/^[A-Za-z0-9_-]+$/.test(args.cursor))
64
+ throw new Error("Invalid receipt cursor");
65
+ const decoded = JSON.parse(Buffer.from(args.cursor, "base64url").toString("utf8"));
66
+ if (!record(decoded) || typeof decoded.at !== "string" || typeof decoded.id !== "string" || !UUID.test(decoded.id))
67
+ throw new Error("Invalid receipt cursor");
68
+ return { limit, cursor: { at: iso(decoded.at), id: decoded.id } };
69
+ };
70
+ var projectBillingStatus = (value) => ({
71
+ portalAccess: value.portalAccess === true,
72
+ subscription: value.subscription ? {
73
+ status: String(value.subscription.status).slice(0, 64),
74
+ renewsAt: value.subscription.renewsAt === null ? null : iso(value.subscription.renewsAt),
75
+ cancelAtPeriodEnd: value.subscription.cancelAtPeriodEnd === true
76
+ } : null,
77
+ credits: {
78
+ remaining: integer(value.credits.remaining),
79
+ reserved: integer(value.credits.reserved),
80
+ purchased: integer(value.credits.purchased),
81
+ promotional: integer(value.credits.promotional),
82
+ debt: integer(value.credits.debt)
83
+ },
84
+ automaticRefill: false
85
+ });
86
+ var projectReceiptPage = (value, limit) => {
87
+ if (value.receipts.length > limit)
88
+ throw new Error("Receipt page exceeds its limit");
89
+ if (value.nextCursor !== null)
90
+ parseReceiptPage({ cursor: value.nextCursor });
91
+ return {
92
+ receipts: value.receipts.map((row) => {
93
+ if (!UUID.test(row.id) || !/^[A-Z]{3}$/.test(row.currency) || !["credit_purchase", "initial", "plan_change", "renewal"].includes(row.source) || !["paid", "partially_refunded", "refunded"].includes(row.status) || row.refundedAmountCents > row.amountCents)
94
+ throw new Error("Invalid receipt");
95
+ return {
96
+ id: row.id,
97
+ issuedAt: iso(row.issuedAt),
98
+ currency: row.currency,
99
+ amountCents: integer(row.amountCents),
100
+ refundedAmountCents: integer(row.refundedAmountCents),
101
+ source: row.source,
102
+ status: row.status
103
+ };
104
+ }),
105
+ nextCursor: value.nextCursor
106
+ };
107
+ };
108
+ var projectUsageReport = (value, range) => {
109
+ if (value.from !== range.from || value.to !== range.to || value.byDay.length > 90 || value.byFeature.length > 21)
110
+ throw new Error("Invalid usage report bounds");
111
+ const byDay = value.byDay.map((row) => ({
112
+ day: day(row.day),
113
+ credits: integer(row.credits),
114
+ events: integer(row.events)
115
+ }));
116
+ const byFeature = value.byFeature.map((row) => ({
117
+ feature: row.feature.slice(0, 128),
118
+ credits: integer(row.credits),
119
+ events: integer(row.events)
120
+ }));
121
+ const credits = integer(value.creditsConsumed), events = integer(value.events);
122
+ for (const rows of [byDay, byFeature])
123
+ if (rows.reduce((sum, row) => sum + row.credits, 0) !== credits || rows.reduce((sum, row) => sum + row.events, 0) !== events)
124
+ throw new Error("Usage breakdown does not reconcile");
125
+ if (new Set(byDay.map((row) => row.day)).size !== byDay.length || byDay.some((row) => row.day < range.from || row.day >= range.to))
126
+ throw new Error("Invalid usage days");
127
+ return {
128
+ from: range.from,
129
+ to: range.to,
130
+ creditsConsumed: credits,
131
+ events,
132
+ byDay,
133
+ byFeature
134
+ };
135
+ };
136
+ export {
137
+ encodeReceiptCursor,
138
+ parseReceiptPage,
139
+ parseUsageRange,
140
+ projectBillingStatus,
141
+ projectReceiptPage,
142
+ projectUsageReport
143
+ };
144
+
145
+ //# debugId=1CCF8D983CF6711964756E2164756E21
146
+ //# sourceMappingURL=reports.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/reports.ts"],
4
+ "sourcesContent": [
5
+ "/** Public customer reports. No payment tokens, vault references, or provider costs. */\nexport type BillingStatus = {\n portalAccess: boolean;\n subscription: {\n status: string;\n renewsAt: string | null;\n cancelAtPeriodEnd: boolean;\n } | null;\n credits: {\n remaining: number;\n reserved: number;\n purchased: number;\n promotional: number;\n debt: number;\n };\n automaticRefill: false;\n};\nexport type BillingReceipt = {\n id: string;\n issuedAt: string;\n currency: string;\n amountCents: number;\n refundedAmountCents: number;\n source: \"credit_purchase\" | \"initial\" | \"plan_change\" | \"renewal\";\n status: \"paid\" | \"partially_refunded\" | \"refunded\";\n};\nexport type ReceiptCursor = { at: string; id: string };\nexport type ReceiptPageRequest = {\n limit: number;\n cursor: ReceiptCursor | null;\n};\nexport type ReceiptPage = {\n receipts: BillingReceipt[];\n nextCursor: string | null;\n};\nexport type UsageRange = { from: string; to: string };\nexport type CustomerUsageReport = {\n from: string;\n to: string;\n creditsConsumed: number;\n events: number;\n byDay: { day: string; credits: number; events: number }[];\n byFeature: { feature: string; credits: number; events: number }[];\n};\nconst DAY_MS = 86_400_000;\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst record = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\nconst only = (input: unknown, keys: readonly string[]) => {\n if (!record(input) || Object.keys(input).some((key) => !keys.includes(key)))\n throw new Error(\"Invalid report input\");\n return input;\n};\nconst integer = (value: number) => {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new Error(\"Invalid report amount\");\n return value;\n};\nconst iso = (value: string) => {\n if (\n !Number.isFinite(Date.parse(value)) ||\n new Date(value).toISOString() !== value\n )\n throw new Error(\"Invalid report timestamp\");\n return value;\n};\nconst day = (value: unknown) => {\n if (\n typeof value !== \"string\" ||\n !/^\\d{4}-\\d{2}-\\d{2}$/.test(value) ||\n new Date(value + \"T00:00:00.000Z\").toISOString().slice(0, 10) !== value\n )\n throw new Error(\"Expected a UTC calendar date\");\n return value;\n};\nexport const parseUsageRange = (\n input: unknown,\n now = new Date(),\n): UsageRange => {\n const args = only(input, [\"from\", \"to\"]);\n const tomorrow = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()) +\n DAY_MS,\n );\n const to = day(args.to ?? tomorrow.toISOString().slice(0, 10));\n const from = day(\n args.from ??\n new Date(Date.parse(to) - 30 * DAY_MS).toISOString().slice(0, 10),\n );\n const duration = Date.parse(to) - Date.parse(from);\n if (\n duration <= 0 ||\n duration > 90 * DAY_MS ||\n Date.parse(to) > tomorrow.getTime()\n )\n throw new Error(\n \"Choose a range of 1–90 UTC days, ending no later than tomorrow\",\n );\n return { from, to };\n};\n/** Cursor is a position, never authorization. Consumers must filter by account. */\nexport const encodeReceiptCursor = (cursor: ReceiptCursor) => {\n if (!UUID.test(cursor.id)) throw new Error(\"Invalid receipt cursor\");\n return Buffer.from(\n JSON.stringify({ at: iso(cursor.at), id: cursor.id }),\n ).toString(\"base64url\");\n};\nexport const parseReceiptPage = (input: unknown): ReceiptPageRequest => {\n const args = only(input, [\"limit\", \"cursor\"]);\n const limit = args.limit ?? 20;\n if (\n typeof limit !== \"number\" ||\n !Number.isInteger(limit) ||\n limit < 1 ||\n limit > 50\n )\n throw new Error(\"Receipt limit must be 1–50\");\n if (args.cursor === undefined || args.cursor === null)\n return { limit, cursor: null };\n if (\n typeof args.cursor !== \"string\" ||\n args.cursor.length > 256 ||\n !/^[A-Za-z0-9_-]+$/.test(args.cursor)\n )\n throw new Error(\"Invalid receipt cursor\");\n const decoded: unknown = JSON.parse(\n Buffer.from(args.cursor, \"base64url\").toString(\"utf8\"),\n );\n if (\n !record(decoded) ||\n typeof decoded.at !== \"string\" ||\n typeof decoded.id !== \"string\" ||\n !UUID.test(decoded.id)\n )\n throw new Error(\"Invalid receipt cursor\");\n return { limit, cursor: { at: iso(decoded.at), id: decoded.id } };\n};\nexport const projectBillingStatus = (value: BillingStatus): BillingStatus => ({\n portalAccess: value.portalAccess === true,\n subscription: value.subscription\n ? {\n status: String(value.subscription.status).slice(0, 64),\n renewsAt:\n value.subscription.renewsAt === null\n ? null\n : iso(value.subscription.renewsAt),\n cancelAtPeriodEnd: value.subscription.cancelAtPeriodEnd === true,\n }\n : null,\n credits: {\n remaining: integer(value.credits.remaining),\n reserved: integer(value.credits.reserved),\n purchased: integer(value.credits.purchased),\n promotional: integer(value.credits.promotional),\n debt: integer(value.credits.debt),\n },\n automaticRefill: false,\n});\nexport const projectReceiptPage = (\n value: ReceiptPage,\n limit: number,\n): ReceiptPage => {\n if (value.receipts.length > limit)\n throw new Error(\"Receipt page exceeds its limit\");\n if (value.nextCursor !== null) parseReceiptPage({ cursor: value.nextCursor });\n return {\n receipts: value.receipts.map((row) => {\n if (\n !UUID.test(row.id) ||\n !/^[A-Z]{3}$/.test(row.currency) ||\n ![\"credit_purchase\", \"initial\", \"plan_change\", \"renewal\"].includes(\n row.source,\n ) ||\n ![\"paid\", \"partially_refunded\", \"refunded\"].includes(row.status) ||\n row.refundedAmountCents > row.amountCents\n )\n throw new Error(\"Invalid receipt\");\n return {\n id: row.id,\n issuedAt: iso(row.issuedAt),\n currency: row.currency,\n amountCents: integer(row.amountCents),\n refundedAmountCents: integer(row.refundedAmountCents),\n source: row.source,\n status: row.status,\n };\n }),\n nextCursor: value.nextCursor,\n };\n};\nexport const projectUsageReport = (\n value: CustomerUsageReport,\n range: UsageRange,\n): CustomerUsageReport => {\n if (\n value.from !== range.from ||\n value.to !== range.to ||\n value.byDay.length > 90 ||\n value.byFeature.length > 21\n )\n throw new Error(\"Invalid usage report bounds\");\n const byDay = value.byDay.map((row) => ({\n day: day(row.day),\n credits: integer(row.credits),\n events: integer(row.events),\n }));\n const byFeature = value.byFeature.map((row) => ({\n feature: row.feature.slice(0, 128),\n credits: integer(row.credits),\n events: integer(row.events),\n }));\n const credits = integer(value.creditsConsumed),\n events = integer(value.events);\n for (const rows of [byDay, byFeature])\n if (\n rows.reduce((sum, row) => sum + row.credits, 0) !== credits ||\n rows.reduce((sum, row) => sum + row.events, 0) !== events\n )\n throw new Error(\"Usage breakdown does not reconcile\");\n if (\n new Set(byDay.map((row) => row.day)).size !== byDay.length ||\n byDay.some((row) => row.day < range.from || row.day >= range.to)\n )\n throw new Error(\"Invalid usage days\");\n return {\n from: range.from,\n to: range.to,\n creditsConsumed: credits,\n events,\n byDay,\n byFeature,\n };\n};\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;AA4CA,IAAM,SAAS;AACf,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,UACd,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AACrE,IAAM,OAAO,CAAC,OAAgB,SAA4B;AAAA,EACxD,IAAI,CAAC,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,SAAS,GAAG,CAAC;AAAA,IACxE,MAAM,IAAI,MAAM,sBAAsB;AAAA,EACxC,OAAO;AAAA;AAET,IAAM,UAAU,CAAC,UAAkB;AAAA,EACjC,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ;AAAA,IAC1C,MAAM,IAAI,MAAM,uBAAuB;AAAA,EACzC,OAAO;AAAA;AAET,IAAM,MAAM,CAAC,UAAkB;AAAA,EAC7B,IACE,CAAC,OAAO,SAAS,KAAK,MAAM,KAAK,CAAC,KAClC,IAAI,KAAK,KAAK,EAAE,YAAY,MAAM;AAAA,IAElC,MAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C,OAAO;AAAA;AAET,IAAM,MAAM,CAAC,UAAmB;AAAA,EAC9B,IACE,OAAO,UAAU,YACjB,CAAC,sBAAsB,KAAK,KAAK,KACjC,IAAI,KAAK,QAAQ,gBAAgB,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,MAAM;AAAA,IAElE,MAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD,OAAO;AAAA;AAEF,IAAM,kBAAkB,CAC7B,OACA,MAAM,IAAI,SACK;AAAA,EACf,MAAM,OAAO,KAAK,OAAO,CAAC,QAAQ,IAAI,CAAC;AAAA,EACvC,MAAM,WAAW,IAAI,KACnB,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,GAAG,IAAI,WAAW,CAAC,IAChE,MACJ;AAAA,EACA,MAAM,KAAK,IAAI,KAAK,MAAM,SAAS,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,EAC7D,MAAM,OAAO,IACX,KAAK,QACH,IAAI,KAAK,KAAK,MAAM,EAAE,IAAI,KAAK,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CACpE;AAAA,EACA,MAAM,WAAW,KAAK,MAAM,EAAE,IAAI,KAAK,MAAM,IAAI;AAAA,EACjD,IACE,YAAY,KACZ,WAAW,KAAK,UAChB,KAAK,MAAM,EAAE,IAAI,SAAS,QAAQ;AAAA,IAElC,MAAM,IAAI,MACR,qEACF;AAAA,EACF,OAAO,EAAE,MAAM,GAAG;AAAA;AAGb,IAAM,sBAAsB,CAAC,WAA0B;AAAA,EAC5D,IAAI,CAAC,KAAK,KAAK,OAAO,EAAE;AAAA,IAAG,MAAM,IAAI,MAAM,wBAAwB;AAAA,EACnE,OAAO,OAAO,KACZ,KAAK,UAAU,EAAE,IAAI,IAAI,OAAO,EAAE,GAAG,IAAI,OAAO,GAAG,CAAC,CACtD,EAAE,SAAS,WAAW;AAAA;AAEjB,IAAM,mBAAmB,CAAC,UAAuC;AAAA,EACtE,MAAM,OAAO,KAAK,OAAO,CAAC,SAAS,QAAQ,CAAC;AAAA,EAC5C,MAAM,QAAQ,KAAK,SAAS;AAAA,EAC5B,IACE,OAAO,UAAU,YACjB,CAAC,OAAO,UAAU,KAAK,KACvB,QAAQ,KACR,QAAQ;AAAA,IAER,MAAM,IAAI,MAAM,iCAA4B;AAAA,EAC9C,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW;AAAA,IAC/C,OAAO,EAAE,OAAO,QAAQ,KAAK;AAAA,EAC/B,IACE,OAAO,KAAK,WAAW,YACvB,KAAK,OAAO,SAAS,OACrB,CAAC,mBAAmB,KAAK,KAAK,MAAM;AAAA,IAEpC,MAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C,MAAM,UAAmB,KAAK,MAC5B,OAAO,KAAK,KAAK,QAAQ,WAAW,EAAE,SAAS,MAAM,CACvD;AAAA,EACA,IACE,CAAC,OAAO,OAAO,KACf,OAAO,QAAQ,OAAO,YACtB,OAAO,QAAQ,OAAO,YACtB,CAAC,KAAK,KAAK,QAAQ,EAAE;AAAA,IAErB,MAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C,OAAO,EAAE,OAAO,QAAQ,EAAE,IAAI,IAAI,QAAQ,EAAE,GAAG,IAAI,QAAQ,GAAG,EAAE;AAAA;AAE3D,IAAM,uBAAuB,CAAC,WAAyC;AAAA,EAC5E,cAAc,MAAM,iBAAiB;AAAA,EACrC,cAAc,MAAM,eAChB;AAAA,IACE,QAAQ,OAAO,MAAM,aAAa,MAAM,EAAE,MAAM,GAAG,EAAE;AAAA,IACrD,UACE,MAAM,aAAa,aAAa,OAC5B,OACA,IAAI,MAAM,aAAa,QAAQ;AAAA,IACrC,mBAAmB,MAAM,aAAa,sBAAsB;AAAA,EAC9D,IACA;AAAA,EACJ,SAAS;AAAA,IACP,WAAW,QAAQ,MAAM,QAAQ,SAAS;AAAA,IAC1C,UAAU,QAAQ,MAAM,QAAQ,QAAQ;AAAA,IACxC,WAAW,QAAQ,MAAM,QAAQ,SAAS;AAAA,IAC1C,aAAa,QAAQ,MAAM,QAAQ,WAAW;AAAA,IAC9C,MAAM,QAAQ,MAAM,QAAQ,IAAI;AAAA,EAClC;AAAA,EACA,iBAAiB;AACnB;AACO,IAAM,qBAAqB,CAChC,OACA,UACgB;AAAA,EAChB,IAAI,MAAM,SAAS,SAAS;AAAA,IAC1B,MAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD,IAAI,MAAM,eAAe;AAAA,IAAM,iBAAiB,EAAE,QAAQ,MAAM,WAAW,CAAC;AAAA,EAC5E,OAAO;AAAA,IACL,UAAU,MAAM,SAAS,IAAI,CAAC,QAAQ;AAAA,MACpC,IACE,CAAC,KAAK,KAAK,IAAI,EAAE,KACjB,CAAC,aAAa,KAAK,IAAI,QAAQ,KAC/B,CAAC,CAAC,mBAAmB,WAAW,eAAe,SAAS,EAAE,SACxD,IAAI,MACN,KACA,CAAC,CAAC,QAAQ,sBAAsB,UAAU,EAAE,SAAS,IAAI,MAAM,KAC/D,IAAI,sBAAsB,IAAI;AAAA,QAE9B,MAAM,IAAI,MAAM,iBAAiB;AAAA,MACnC,OAAO;AAAA,QACL,IAAI,IAAI;AAAA,QACR,UAAU,IAAI,IAAI,QAAQ;AAAA,QAC1B,UAAU,IAAI;AAAA,QACd,aAAa,QAAQ,IAAI,WAAW;AAAA,QACpC,qBAAqB,QAAQ,IAAI,mBAAmB;AAAA,QACpD,QAAQ,IAAI;AAAA,QACZ,QAAQ,IAAI;AAAA,MACd;AAAA,KACD;AAAA,IACD,YAAY,MAAM;AAAA,EACpB;AAAA;AAEK,IAAM,qBAAqB,CAChC,OACA,UACwB;AAAA,EACxB,IACE,MAAM,SAAS,MAAM,QACrB,MAAM,OAAO,MAAM,MACnB,MAAM,MAAM,SAAS,MACrB,MAAM,UAAU,SAAS;AAAA,IAEzB,MAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C,MAAM,QAAQ,MAAM,MAAM,IAAI,CAAC,SAAS;AAAA,IACtC,KAAK,IAAI,IAAI,GAAG;AAAA,IAChB,SAAS,QAAQ,IAAI,OAAO;AAAA,IAC5B,QAAQ,QAAQ,IAAI,MAAM;AAAA,EAC5B,EAAE;AAAA,EACF,MAAM,YAAY,MAAM,UAAU,IAAI,CAAC,SAAS;AAAA,IAC9C,SAAS,IAAI,QAAQ,MAAM,GAAG,GAAG;AAAA,IACjC,SAAS,QAAQ,IAAI,OAAO;AAAA,IAC5B,QAAQ,QAAQ,IAAI,MAAM;AAAA,EAC5B,EAAE;AAAA,EACF,MAAM,UAAU,QAAQ,MAAM,eAAe,GAC3C,SAAS,QAAQ,MAAM,MAAM;AAAA,EAC/B,WAAW,QAAQ,CAAC,OAAO,SAAS;AAAA,IAClC,IACE,KAAK,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM,WACpD,KAAK,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC,MAAM;AAAA,MAEnD,MAAM,IAAI,MAAM,oCAAoC;AAAA,EACxD,IACE,IAAI,IAAI,MAAM,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC,EAAE,SAAS,MAAM,UACpD,MAAM,KAAK,CAAC,QAAQ,IAAI,MAAM,MAAM,QAAQ,IAAI,OAAO,MAAM,EAAE;AAAA,IAE/D,MAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC,OAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,IAAI,MAAM;AAAA,IACV,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;",
8
+ "debugId": "1CCF8D983CF6711964756E2164756E21",
9
+ "names": []
10
+ }
@@ -27,3 +27,9 @@ A process crash or unknown effect outcome deliberately leaves work running and i
27
27
  `migrateLegacyCreditAccount` preserves `max(0, periodAllowance + bonusCredits - consumed)` without inventing historical purchase provenance. It first allocates known current-period consumption against period allowance, carries only the residual bonus balance, and records any deficit. Legacy carryover is classified as promotional because the old mixed column cannot prove which credits were purchased. Keep the original row and its payment/usage history as immutable migration evidence. Future verified purchases go to the purchased bucket.
28
28
 
29
29
  Dry-run all accounts, compare current availability before and after, and separately review ambiguous historical purchase allocation before changing customer balances or claiming a reconstructed purchase balance. Run under a writer drain or account-level locks; block legacy writers after cutover. Never roll back by re-enabling the old balance formula, which would restore spent permanent credits. An operational rollback should disable new paid work while keeping the new ledger and recovery reads available.
30
+
31
+ ## PostgreSQL driver JSON encoding
32
+
33
+ Version 0.9.1 binds serialized JSON through `::text::jsonb`. Direct postgres.js infers JSON parameters and otherwise JSON-encodes strings a second time; adapters such as Drizzle override that serializer, which can hide the defect in adapter-only tests. This applies to account state, immutable operation receipts, reservations, work records and checkout quotes.
34
+
35
+ For a real-driver regression, set `BILLING_TEST_DATABASE_URL` to a test database and run `bun test tests/postgresJson.test.ts`. It creates and removes a unique schema containing only synthetic records. The ordinary suite skips this test when the database is absent. Existing double-encoded rows need a separately reviewed repair; updating the package does not rewrite financial records. Preserve originals and verify semantic value equality before committing any repair.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/billing",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Provider-neutral pricing and invoice computation used by the hosted AbsoluteJS.ai platform. Converts metered usage into exact integer-micro line items with tiers and allowances.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -52,6 +52,11 @@
52
52
  "types": "./dist/checkoutHandoff.d.ts",
53
53
  "import": "./dist/checkoutHandoff.js",
54
54
  "default": "./dist/checkoutHandoff.js"
55
+ },
56
+ "./reports": {
57
+ "types": "./dist/reports.d.ts",
58
+ "import": "./dist/reports.js",
59
+ "default": "./dist/reports.js"
55
60
  }
56
61
  },
57
62
  "publishConfig": {
@@ -66,7 +71,7 @@
66
71
  "docs/checkout-handoffs.md"
67
72
  ],
68
73
  "scripts": {
69
- "build": "rm -rf dist && bun build src/index.ts src/ledger.ts src/prepaid.ts src/prepaidPostgres.ts src/creditWork.ts src/checkoutHandoff.ts src/manifest.ts --root ./src --outdir dist --sourcemap --target=bun && tsc --project tsconfig.build.json && absolute-manifest emit",
74
+ "build": "rm -rf dist && bun build src/index.ts src/ledger.ts src/prepaid.ts src/prepaidPostgres.ts src/creditWork.ts src/checkoutHandoff.ts src/reports.ts src/manifest.ts --root ./src --outdir dist --sourcemap --target=bun && tsc --project tsconfig.build.json && absolute-manifest emit",
70
75
  "test": "bun test tests/",
71
76
  "typecheck": "tsc --noEmit",
72
77
  "format": "prettier --write \"./**/*.{ts,json,md}\"",
@@ -85,10 +90,11 @@
85
90
  ],
86
91
  "devDependencies": {
87
92
  "@absolutejs/changelog": "^0.6.0",
93
+ "@electric-sql/pglite": "^0.3.14",
88
94
  "@types/bun": "^1.3.14",
95
+ "postgres": "3.4.9",
89
96
  "prettier": "^3.8.3",
90
- "typescript": "^5.9.0",
91
- "@electric-sql/pglite": "^0.3.14"
97
+ "typescript": "^5.9.0"
92
98
  },
93
99
  "dependencies": {
94
100
  "@absolutejs/manifest": "^0.9.0",