@drm3/sdk 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/LICENSE +75 -0
  2. package/README.md +426 -0
  3. package/bin/drm3.js +255 -0
  4. package/dist/alerts-bell.d.ts +7 -0
  5. package/dist/alerts-bell.d.ts.map +1 -0
  6. package/dist/alerts-bell.js +118 -0
  7. package/dist/alerts-bell.js.map +1 -0
  8. package/dist/alerts-fetch.d.ts +41 -0
  9. package/dist/alerts-fetch.d.ts.map +1 -0
  10. package/dist/alerts-fetch.js +60 -0
  11. package/dist/alerts-fetch.js.map +1 -0
  12. package/dist/alerts-styles.d.ts +4 -0
  13. package/dist/alerts-styles.d.ts.map +1 -0
  14. package/dist/alerts-styles.js +49 -0
  15. package/dist/alerts-styles.js.map +1 -0
  16. package/dist/alerts.d.ts +3 -0
  17. package/dist/alerts.d.ts.map +1 -0
  18. package/dist/alerts.global.js +206 -0
  19. package/dist/alerts.global.text.js +2 -0
  20. package/dist/alerts.js +21 -0
  21. package/dist/alerts.js.map +1 -0
  22. package/dist/index.d.ts +64 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +117 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/keys-canonical.d.ts +7 -0
  27. package/dist/keys-canonical.d.ts.map +1 -0
  28. package/dist/keys-canonical.js +57 -0
  29. package/dist/keys-canonical.js.map +1 -0
  30. package/dist/keys-generate.d.ts +39 -0
  31. package/dist/keys-generate.d.ts.map +1 -0
  32. package/dist/keys-generate.js +39 -0
  33. package/dist/keys-generate.js.map +1 -0
  34. package/dist/keys-verify.d.ts +62 -0
  35. package/dist/keys-verify.d.ts.map +1 -0
  36. package/dist/keys-verify.js +68 -0
  37. package/dist/keys-verify.js.map +1 -0
  38. package/dist/keys.d.ts +4 -0
  39. package/dist/keys.d.ts.map +1 -0
  40. package/dist/keys.js +22 -0
  41. package/dist/keys.js.map +1 -0
  42. package/dist/meter/adapter.d.ts +35 -0
  43. package/dist/meter/adapter.d.ts.map +1 -0
  44. package/dist/meter/adapter.js +0 -0
  45. package/dist/meter/adapter.js.map +1 -0
  46. package/dist/meter/cascade.d.ts +56 -0
  47. package/dist/meter/cascade.d.ts.map +1 -0
  48. package/dist/meter/cascade.js +64 -0
  49. package/dist/meter/cascade.js.map +1 -0
  50. package/dist/meter/client.d.ts +78 -0
  51. package/dist/meter/client.d.ts.map +1 -0
  52. package/dist/meter/client.js +84 -0
  53. package/dist/meter/client.js.map +1 -0
  54. package/dist/meter/core.d.ts +37 -0
  55. package/dist/meter/core.d.ts.map +1 -0
  56. package/dist/meter/core.js +49 -0
  57. package/dist/meter/core.js.map +1 -0
  58. package/dist/meter/gate-client.d.ts +94 -0
  59. package/dist/meter/gate-client.d.ts.map +1 -0
  60. package/dist/meter/gate-client.js +77 -0
  61. package/dist/meter/gate-client.js.map +1 -0
  62. package/dist/meter/gate-decide.d.ts +61 -0
  63. package/dist/meter/gate-decide.d.ts.map +1 -0
  64. package/dist/meter/gate-decide.js +54 -0
  65. package/dist/meter/gate-decide.js.map +1 -0
  66. package/dist/meter/gate.d.ts +10 -0
  67. package/dist/meter/gate.d.ts.map +1 -0
  68. package/dist/meter/gate.js +10 -0
  69. package/dist/meter/gate.js.map +1 -0
  70. package/dist/meter/index.d.ts +28 -0
  71. package/dist/meter/index.d.ts.map +1 -0
  72. package/dist/meter/index.js +28 -0
  73. package/dist/meter/index.js.map +1 -0
  74. package/dist/meter/meters-buckets.d.ts +56 -0
  75. package/dist/meter/meters-buckets.d.ts.map +1 -0
  76. package/dist/meter/meters-buckets.js +111 -0
  77. package/dist/meter/meters-buckets.js.map +1 -0
  78. package/dist/meter/meters-charge.d.ts +13 -0
  79. package/dist/meter/meters-charge.d.ts.map +1 -0
  80. package/dist/meter/meters-charge.js +76 -0
  81. package/dist/meter/meters-charge.js.map +1 -0
  82. package/dist/meter/meters.d.ts +63 -0
  83. package/dist/meter/meters.d.ts.map +1 -0
  84. package/dist/meter/meters.js +44 -0
  85. package/dist/meter/meters.js.map +1 -0
  86. package/dist/meter/plans.d.ts +63 -0
  87. package/dist/meter/plans.d.ts.map +1 -0
  88. package/dist/meter/plans.js +114 -0
  89. package/dist/meter/plans.js.map +1 -0
  90. package/dist/meter/pricebook.d.ts +32 -0
  91. package/dist/meter/pricebook.d.ts.map +1 -0
  92. package/dist/meter/pricebook.js +38 -0
  93. package/dist/meter/pricebook.js.map +1 -0
  94. package/dist/meter/resolve.d.ts +103 -0
  95. package/dist/meter/resolve.d.ts.map +1 -0
  96. package/dist/meter/resolve.js +100 -0
  97. package/dist/meter/resolve.js.map +1 -0
  98. package/dist/meter/sink.d.ts +26 -0
  99. package/dist/meter/sink.d.ts.map +1 -0
  100. package/dist/meter/sink.js +9 -0
  101. package/dist/meter/sink.js.map +1 -0
  102. package/dist/meter/sql/d1.d.ts +32 -0
  103. package/dist/meter/sql/d1.d.ts.map +1 -0
  104. package/dist/meter/sql/d1.js +41 -0
  105. package/dist/meter/sql/d1.js.map +1 -0
  106. package/dist/meter/sql/driver.d.ts +34 -0
  107. package/dist/meter/sql/driver.d.ts.map +1 -0
  108. package/dist/meter/sql/driver.js +17 -0
  109. package/dist/meter/sql/driver.js.map +1 -0
  110. package/dist/meter/sql/postgres.d.ts +23 -0
  111. package/dist/meter/sql/postgres.d.ts.map +1 -0
  112. package/dist/meter/sql/postgres.js +36 -0
  113. package/dist/meter/sql/postgres.js.map +1 -0
  114. package/dist/meter/sql/schema.d.ts +13 -0
  115. package/dist/meter/sql/schema.d.ts.map +1 -0
  116. package/dist/meter/sql/schema.js +32 -0
  117. package/dist/meter/sql/schema.js.map +1 -0
  118. package/dist/meter/sql/sql-adapter.d.ts +36 -0
  119. package/dist/meter/sql/sql-adapter.d.ts.map +1 -0
  120. package/dist/meter/sql/sql-adapter.js +124 -0
  121. package/dist/meter/sql/sql-adapter.js.map +1 -0
  122. package/dist/meter/sql/statements.d.ts +30 -0
  123. package/dist/meter/sql/statements.d.ts.map +1 -0
  124. package/dist/meter/sql/statements.js +24 -0
  125. package/dist/meter/sql/statements.js.map +1 -0
  126. package/dist/meter/types.d.ts +48 -0
  127. package/dist/meter/types.d.ts.map +1 -0
  128. package/dist/meter/types.js +8 -0
  129. package/dist/meter/types.js.map +1 -0
  130. package/dist/meter/units.d.ts +33 -0
  131. package/dist/meter/units.d.ts.map +1 -0
  132. package/dist/meter/units.js +45 -0
  133. package/dist/meter/units.js.map +1 -0
  134. package/dist/meter/wallet-buckets.d.ts +63 -0
  135. package/dist/meter/wallet-buckets.d.ts.map +1 -0
  136. package/dist/meter/wallet-buckets.js +131 -0
  137. package/dist/meter/wallet-buckets.js.map +1 -0
  138. package/dist/meter/wallet-charge.d.ts +15 -0
  139. package/dist/meter/wallet-charge.d.ts.map +1 -0
  140. package/dist/meter/wallet-charge.js +64 -0
  141. package/dist/meter/wallet-charge.js.map +1 -0
  142. package/dist/meter/wallet.d.ts +68 -0
  143. package/dist/meter/wallet.d.ts.map +1 -0
  144. package/dist/meter/wallet.js +47 -0
  145. package/dist/meter/wallet.js.map +1 -0
  146. package/dist/receipts-api.d.ts +5 -0
  147. package/dist/receipts-api.d.ts.map +1 -0
  148. package/dist/receipts-api.js +117 -0
  149. package/dist/receipts-api.js.map +1 -0
  150. package/dist/receipts-chain.d.ts +3 -0
  151. package/dist/receipts-chain.d.ts.map +1 -0
  152. package/dist/receipts-chain.js +121 -0
  153. package/dist/receipts-chain.js.map +1 -0
  154. package/dist/receipts-modal.d.ts +14 -0
  155. package/dist/receipts-modal.d.ts.map +1 -0
  156. package/dist/receipts-modal.js +100 -0
  157. package/dist/receipts-modal.js.map +1 -0
  158. package/dist/receipts-registry.d.ts +4 -0
  159. package/dist/receipts-registry.d.ts.map +1 -0
  160. package/dist/receipts-registry.js +54 -0
  161. package/dist/receipts-registry.js.map +1 -0
  162. package/dist/receipts-render.d.ts +9 -0
  163. package/dist/receipts-render.d.ts.map +1 -0
  164. package/dist/receipts-render.js +83 -0
  165. package/dist/receipts-render.js.map +1 -0
  166. package/dist/receipts-types.d.ts +61 -0
  167. package/dist/receipts-types.d.ts.map +1 -0
  168. package/dist/receipts-types.js +12 -0
  169. package/dist/receipts-types.js.map +1 -0
  170. package/dist/receipts-ui.d.ts +10 -0
  171. package/dist/receipts-ui.d.ts.map +1 -0
  172. package/dist/receipts-ui.js +103 -0
  173. package/dist/receipts-ui.js.map +1 -0
  174. package/dist/receipts-verify.d.ts +8 -0
  175. package/dist/receipts-verify.d.ts.map +1 -0
  176. package/dist/receipts-verify.js +113 -0
  177. package/dist/receipts-verify.js.map +1 -0
  178. package/dist/receipts.d.ts +9 -0
  179. package/dist/receipts.d.ts.map +1 -0
  180. package/dist/receipts.global.js +587 -0
  181. package/dist/receipts.global.text.js +2 -0
  182. package/dist/receipts.js +28 -0
  183. package/dist/receipts.js.map +1 -0
  184. package/dist/sso/index.d.ts +4 -0
  185. package/dist/sso/index.d.ts.map +1 -0
  186. package/dist/sso/index.js +10 -0
  187. package/dist/sso/index.js.map +1 -0
  188. package/dist/sso/jwt.d.ts +29 -0
  189. package/dist/sso/jwt.d.ts.map +1 -0
  190. package/dist/sso/jwt.js +73 -0
  191. package/dist/sso/jwt.js.map +1 -0
  192. package/dist/sso/launch.d.ts +70 -0
  193. package/dist/sso/launch.d.ts.map +1 -0
  194. package/dist/sso/launch.js +75 -0
  195. package/dist/sso/launch.js.map +1 -0
  196. package/dist/sso/revocation.d.ts +23 -0
  197. package/dist/sso/revocation.d.ts.map +1 -0
  198. package/dist/sso/revocation.js +53 -0
  199. package/dist/sso/revocation.js.map +1 -0
  200. package/dist/switcher.d.ts +22 -0
  201. package/dist/switcher.d.ts.map +1 -0
  202. package/dist/switcher.js +174 -0
  203. package/dist/switcher.js.map +1 -0
  204. package/dist/x402/chain.d.ts +49 -0
  205. package/dist/x402/chain.d.ts.map +1 -0
  206. package/dist/x402/chain.js +33 -0
  207. package/dist/x402/chain.js.map +1 -0
  208. package/dist/x402/crypto.d.ts +25 -0
  209. package/dist/x402/crypto.d.ts.map +1 -0
  210. package/dist/x402/crypto.js +74 -0
  211. package/dist/x402/crypto.js.map +1 -0
  212. package/dist/x402/eip712.d.ts +20 -0
  213. package/dist/x402/eip712.d.ts.map +1 -0
  214. package/dist/x402/eip712.js +49 -0
  215. package/dist/x402/eip712.js.map +1 -0
  216. package/dist/x402/envelope.d.ts +45 -0
  217. package/dist/x402/envelope.d.ts.map +1 -0
  218. package/dist/x402/envelope.js +50 -0
  219. package/dist/x402/envelope.js.map +1 -0
  220. package/dist/x402/index.d.ts +41 -0
  221. package/dist/x402/index.d.ts.map +1 -0
  222. package/dist/x402/index.js +41 -0
  223. package/dist/x402/index.js.map +1 -0
  224. package/dist/x402/verify.d.ts +51 -0
  225. package/dist/x402/verify.d.ts.map +1 -0
  226. package/dist/x402/verify.js +110 -0
  227. package/dist/x402/verify.js.map +1 -0
  228. package/package.json +100 -0
@@ -0,0 +1,24 @@
1
+ export function buildStatements(greatest) {
2
+ return {
3
+ ensureLedger: `INSERT INTO credits_ledger (tenant_id, app, balance, held) VALUES (?, ?, 0, 0)
4
+ ON CONFLICT (tenant_id, app) DO NOTHING`,
5
+ fund: "UPDATE credits_ledger SET balance = balance + ? WHERE tenant_id = ? AND app = ?",
6
+ insertReservation: `INSERT INTO credits_reservation
7
+ (id, tenant_id, app, held, charged, state, created_at, expires_at, capture_idem_key)
8
+ VALUES (?, ?, ?, ?, 0, 'held', ?, ?, NULL)`,
9
+ placeHold: `UPDATE credits_ledger SET held = held + ?
10
+ WHERE tenant_id = ? AND app = ? AND balance - held >= ?`,
11
+ dropReservation: "DELETE FROM credits_reservation WHERE id = ? AND state = 'held'",
12
+ selectByIdem: "SELECT charged FROM credits_reservation WHERE capture_idem_key = ?",
13
+ captureReservation: `UPDATE credits_reservation SET state = 'captured', charged = ?, capture_idem_key = ?
14
+ WHERE id = ? AND state = 'held'`,
15
+ applyLedgerCapture: `UPDATE credits_ledger SET balance = balance - ?, held = ${greatest}(held - ?, 0)
16
+ WHERE tenant_id = ? AND app = ?`,
17
+ releaseReservation: "UPDATE credits_reservation SET state = ?, charged = 0 WHERE id = ? AND state = 'held'",
18
+ releaseLedgerHold: `UPDATE credits_ledger SET held = ${greatest}(held - ?, 0) WHERE tenant_id = ? AND app = ?`,
19
+ selectBalance: "SELECT balance, held FROM credits_ledger WHERE tenant_id = ? AND app = ?",
20
+ selectExpired: "SELECT id FROM credits_reservation WHERE state = 'held' AND expires_at <= ?",
21
+ selectById: "SELECT * FROM credits_reservation WHERE id = ?",
22
+ };
23
+ }
24
+ //# sourceMappingURL=statements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statements.js","sourceRoot":"","sources":["../../../src/meter/sql/statements.ts"],"names":[],"mappings":"AA6BA,MAAM,UAAU,eAAe,CAAC,QAA4B;IAC1D,OAAO;QACL,YAAY,EAAE;+CAC6B;QAC3C,IAAI,EAAE,iFAAiF;QACvF,iBAAiB,EAAE;;kDAE2B;QAC9C,SAAS,EAAE;gEACiD;QAC5D,eAAe,EAAE,iEAAiE;QAClF,YAAY,EAAE,oEAAoE;QAClF,kBAAkB,EAAE;wCACgB;QACpC,kBAAkB,EAAE,2DAA2D,QAAQ;wCACnD;QACpC,kBAAkB,EAAE,uFAAuF;QAC3G,iBAAiB,EAAE,oCAAoC,QAAQ,+CAA+C;QAC9G,aAAa,EAAE,0EAA0E;QACzF,aAAa,EAAE,6EAA6E;QAC5F,UAAU,EAAE,gDAAgD;KAC7D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Core types. Credits are integer µcredits (base units, bigint) mirroring the
3
+ * on-chain balance - floats never enter the ledger. Errors are values, never
4
+ * thrown in the hot path.
5
+ */
6
+ /** Integer µcredits (base units) - the "wei" of a credit; 1 credit = 10^6 µcredits.
7
+ * "credit" is display-only (see units.ts). Always a whole bigint, so balances
8
+ * never drift. Always >= 0n in any persisted balance/hold. */
9
+ export type Credits = bigint;
10
+ /** A wallet address (crypto user) or account id (non-crypto user). Opaque. */
11
+ export type Tenant = string;
12
+ /** App scope. The empty string is the GLOBAL bucket. */
13
+ export type App = string;
14
+ export declare const GLOBAL: App;
15
+ export type ReservationState = "held" | "captured" | "voided" | "expired";
16
+ export interface Reservation {
17
+ id: string;
18
+ tenant: Tenant;
19
+ /** The scope the hold draws from after app→global fallback is resolved. */
20
+ app: App;
21
+ held: Credits;
22
+ charged: Credits;
23
+ state: ReservationState;
24
+ createdAt: number;
25
+ expiresAt: number;
26
+ /** Set on capture; unique, so a retried capture can never double-charge. */
27
+ captureIdemKey?: string;
28
+ }
29
+ export interface Balance {
30
+ /** Total credits funded into this scope, minus what's been captured. */
31
+ balance: Credits;
32
+ /** Currently reserved by in-flight holds. */
33
+ held: Credits;
34
+ /** balance - held. Never negative. What a new request may draw on. */
35
+ available: Credits;
36
+ }
37
+ /** Typed error values. The hot path returns these instead of throwing. */
38
+ export type MeterErrorCode = "INSUFFICIENT_FUNDS" | "RESERVATION_NOT_FOUND" | "ALREADY_FINALIZED";
39
+ export interface MeterError {
40
+ ok: false;
41
+ code: MeterErrorCode;
42
+ message: string;
43
+ }
44
+ export type Result<T> = ({
45
+ ok: true;
46
+ } & T) | MeterError;
47
+ export declare const err: (code: MeterErrorCode, message: string) => MeterError;
48
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/meter/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;+DAE+D;AAC/D,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,8EAA8E;AAC9E,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,wDAAwD;AACxD,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC;AACzB,eAAO,MAAM,MAAM,EAAE,GAAQ,CAAC;AAE9B,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,6CAA6C;IAC7C,IAAI,EAAE,OAAO,CAAC;IACd,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,mBAAmB,CAAC;AAElG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;AAExD,eAAO,MAAM,GAAG,GAAI,MAAM,cAAc,EAAE,SAAS,MAAM,KAAG,UAA4C,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Core types. Credits are integer µcredits (base units, bigint) mirroring the
3
+ * on-chain balance - floats never enter the ledger. Errors are values, never
4
+ * thrown in the hot path.
5
+ */
6
+ export const GLOBAL = "";
7
+ export const err = (code, message) => ({ ok: false, code, message });
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/meter/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,MAAM,CAAC,MAAM,MAAM,GAAQ,EAAE,CAAC;AAsC9B,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAoB,EAAE,OAAe,EAAc,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Credit denomination - two tiers, modeled on ETH/wei but scaled like USDC.
3
+ *
4
+ * credit ⟷ ETH/USDC - the denomination humans price and discuss in
5
+ * µcredit ⟷ wei - the smallest indivisible unit; the ONLY thing stored
6
+ *
7
+ * Everything in the system - the off-chain meter ledger AND the on-chain
8
+ * contract - counts integer µcredits (BASE UNITS). "credit" is a DISPLAY
9
+ * convention only, exactly like ETH: the chain never stores "ETH", it stores
10
+ * wei, and a `decimals` value tells a wallet where to put the point. Same here.
11
+ *
12
+ * Because the stored unit is the smallest one, no real price is ever below
13
+ * 1 µcredit, so the ledger only ever adds and subtracts whole integers -
14
+ * rounding is impossible by construction (no floats anywhere; see PriceBook).
15
+ *
16
+ * 1 credit = 10^6 µcredits (6 decimals - DELIBERATELY identical to USDC, so
17
+ * credit↔USDC settlement is 1:1 integer math, also exact, no rounding). 6 is
18
+ * fine enough that real prices are whole integers, e.g. 1 credit / 10k tokens
19
+ * = exactly 100 µcredits/token. uint256 on-chain holds it with vast headroom.
20
+ *
21
+ * "µcredit" is a working name for the base unit (ETH's is "wei"); rebrand at
22
+ * will - it's a display label, it changes no math.
23
+ */
24
+ /** Decimal places between a µcredit and a credit (USDC uses 6; we match it). */
25
+ export declare const CREDIT_DECIMALS = 6;
26
+ /** Base units in one credit. The scale factor; never a divisor in the hot path. */
27
+ export declare const CREDIT: bigint;
28
+ /** Format integer base units as a human "credit" string (DISPLAY ONLY - pure
29
+ * integer math, no float ever touches a balance). e.g. 1_500…0n → "1.5". */
30
+ export declare function formatCredits(baseUnits: bigint, decimals?: number): string;
31
+ /** Parse a decimal "credit" string into exact integer base units (no float). */
32
+ export declare function parseCredits(credits: string): bigint;
33
+ //# sourceMappingURL=units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/meter/units.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,gFAAgF;AAChF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,mFAAmF;AACnF,eAAO,MAAM,MAAM,QAAiC,CAAC;AAErD;6EAC6E;AAC7E,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAOrE;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKpD"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Credit denomination - two tiers, modeled on ETH/wei but scaled like USDC.
3
+ *
4
+ * credit ⟷ ETH/USDC - the denomination humans price and discuss in
5
+ * µcredit ⟷ wei - the smallest indivisible unit; the ONLY thing stored
6
+ *
7
+ * Everything in the system - the off-chain meter ledger AND the on-chain
8
+ * contract - counts integer µcredits (BASE UNITS). "credit" is a DISPLAY
9
+ * convention only, exactly like ETH: the chain never stores "ETH", it stores
10
+ * wei, and a `decimals` value tells a wallet where to put the point. Same here.
11
+ *
12
+ * Because the stored unit is the smallest one, no real price is ever below
13
+ * 1 µcredit, so the ledger only ever adds and subtracts whole integers -
14
+ * rounding is impossible by construction (no floats anywhere; see PriceBook).
15
+ *
16
+ * 1 credit = 10^6 µcredits (6 decimals - DELIBERATELY identical to USDC, so
17
+ * credit↔USDC settlement is 1:1 integer math, also exact, no rounding). 6 is
18
+ * fine enough that real prices are whole integers, e.g. 1 credit / 10k tokens
19
+ * = exactly 100 µcredits/token. uint256 on-chain holds it with vast headroom.
20
+ *
21
+ * "µcredit" is a working name for the base unit (ETH's is "wei"); rebrand at
22
+ * will - it's a display label, it changes no math.
23
+ */
24
+ /** Decimal places between a µcredit and a credit (USDC uses 6; we match it). */
25
+ export const CREDIT_DECIMALS = 6;
26
+ /** Base units in one credit. The scale factor; never a divisor in the hot path. */
27
+ export const CREDIT = 10n ** BigInt(CREDIT_DECIMALS);
28
+ /** Format integer base units as a human "credit" string (DISPLAY ONLY - pure
29
+ * integer math, no float ever touches a balance). e.g. 1_500…0n → "1.5". */
30
+ export function formatCredits(baseUnits, decimals = 4) {
31
+ const neg = baseUnits < 0n;
32
+ const v = neg ? -baseUnits : baseUnits;
33
+ const whole = v / CREDIT;
34
+ const frac = ((v % CREDIT) * 10n ** BigInt(decimals)) / CREDIT;
35
+ const fracStr = decimals > 0 ? `.${frac.toString().padStart(decimals, "0")}` : "";
36
+ return `${neg ? "-" : ""}${whole}${fracStr}`;
37
+ }
38
+ /** Parse a decimal "credit" string into exact integer base units (no float). */
39
+ export function parseCredits(credits) {
40
+ const [whole, frac = ""] = credits.trim().replace(/^-/, "").split(".");
41
+ const fracPadded = (frac + "0".repeat(CREDIT_DECIMALS)).slice(0, CREDIT_DECIMALS);
42
+ const units = BigInt(whole || "0") * CREDIT + BigInt(fracPadded || "0");
43
+ return credits.trim().startsWith("-") ? -units : units;
44
+ }
45
+ //# sourceMappingURL=units.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/meter/units.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,mFAAmF;AACnF,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,eAAe,CAAC,CAAC;AAErD;6EAC6E;AAC7E,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,QAAQ,GAAG,CAAC;IAC3D,MAAM,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACvC,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC;IACzB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC;IAC/D,MAAM,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,GAAG,OAAO,EAAE,CAAC;AAC/C,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC;IACxE,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * wallet-buckets - the allowance bucket layer: types, refill, and reads.
3
+ *
4
+ * Free functions over a SqlDriver so the Wallet class (wallet.ts) can compose them
5
+ * without a second copy of the logic. Covers the allowance/DDL/refill/read half;
6
+ * the cross-bucket charge lives in wallet-charge.ts.
7
+ */
8
+ import type { SqlDriver } from "./sql/driver.js";
9
+ export type Cadence = "daily" | "weekly" | "monthly" | "none";
10
+ /** Config for one allowance bucket. The purse is implicit (always last, always
11
+ * overdraftable) and is never described here. */
12
+ export interface BucketDef {
13
+ /** Bucket name, unique within a (tenant, scope). e.g. 'daily', 'monthly'. */
14
+ name: string;
15
+ /** Refill ceiling in µcredits. */
16
+ cap: bigint;
17
+ /** Auto-refill cadence. 'none' = defined but dormant (never auto-refills). */
18
+ cadence: Cadence;
19
+ /** Burn order, lowest first. Allowances burn before the purse regardless. */
20
+ priority: number;
21
+ /** Optional upstream allocation this bucket is backed by (e.g. 'venice'). */
22
+ source?: string;
23
+ }
24
+ export interface BucketBalance {
25
+ name: string;
26
+ balance: bigint;
27
+ cap: bigint;
28
+ cadence: Cadence;
29
+ priority: number;
30
+ source: string | null;
31
+ }
32
+ export interface WalletBalance {
33
+ /** Spendable total: sum of allowance balances (each floored at 0) + purse. The
34
+ * purse may be negative (overdraft), pulling the aggregate down. */
35
+ aggregate: bigint;
36
+ /** The existing credits_ledger balance for this scope (can be negative). */
37
+ purse: bigint;
38
+ /** Allowance buckets, burn order (priority asc). */
39
+ buckets: BucketBalance[];
40
+ }
41
+ export declare const GLOBAL = "";
42
+ /** Period key for a cadence at a given instant (UTC). Refill fires when the
43
+ * stored key differs from the current one. */
44
+ export declare function periodKey(cadence: Cadence, nowMs: number): string;
45
+ /** Idempotent DDL: the allowance buckets + the charge idempotency guard. The
46
+ * purse (credits_ledger) is created by the base adapter's init - not here. */
47
+ export declare function init(d: SqlDriver): Promise<void>;
48
+ /** Define / update an allowance bucket's config (cap, cadence, priority). Does
49
+ * not change the live balance; the next refill() applies the cap. */
50
+ export declare function setBucket(d: SqlDriver, tenant: string, scope: string, def: BucketDef): Promise<void>;
51
+ /** Refill every allowance whose epoch has rolled to its cap (reset-to-cap, not
52
+ * additive). Returns the buckets topped and the total µcredits granted. */
53
+ export declare function refill(d: SqlDriver, now: () => number, tenant: string, scope: string): Promise<{
54
+ topped: string[];
55
+ granted: bigint;
56
+ }>;
57
+ /** Read the wallet: aggregate, purse, and per-bucket allowance balances. */
58
+ export declare function balance(d: SqlDriver, tenant: string, scope: string): Promise<WalletBalance>;
59
+ /** Add µcredits to the PURSE (Stripe deposit / admin top-up). The purse is the
60
+ * existing ledger; this nets against any overdraft automatically. */
61
+ export declare function deposit(d: SqlDriver, tenant: string, scope: string, ucredits: bigint): Promise<void>;
62
+ export declare function ensurePurse(d: SqlDriver, tenant: string, scope: string): Promise<void>;
63
+ //# sourceMappingURL=wallet-buckets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet-buckets.d.ts","sourceRoot":"","sources":["../../src/meter/wallet-buckets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9D;kDACkD;AAClD,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B;yEACqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,MAAM,KAAK,CAAC;AAEzB;+CAC+C;AAC/C,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED;+EAC+E;AAC/E,wBAAsB,IAAI,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BtD;AAED;sEACsE;AACtE,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB1G;AAED;4EAC4E;AAC5E,wBAAsB,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA0B3I;AAED,4EAA4E;AAC5E,wBAAsB,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAoBjG;AAED;sEACsE;AACtE,wBAAsB,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK1G;AAED,wBAAsB,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5F"}
@@ -0,0 +1,131 @@
1
+ import { toBig } from "./sql/driver.js";
2
+ export const GLOBAL = "";
3
+ /** Period key for a cadence at a given instant (UTC). Refill fires when the
4
+ * stored key differs from the current one. */
5
+ export function periodKey(cadence, nowMs) {
6
+ if (cadence === "none")
7
+ return "none";
8
+ const d = new Date(nowMs);
9
+ const iso = d.toISOString(); // YYYY-MM-DDTHH:MM:SS.sssZ
10
+ if (cadence === "monthly")
11
+ return iso.slice(0, 7); // YYYY-MM
12
+ if (cadence === "daily")
13
+ return iso.slice(0, 10); // YYYY-MM-DD
14
+ // weekly: UTC year + zero-based week index (Mon-anchored not needed for a cap reset)
15
+ const start = Date.UTC(d.getUTCFullYear(), 0, 1);
16
+ const week = Math.floor((nowMs - start) / (7 * 24 * 3600 * 1000));
17
+ return `${d.getUTCFullYear()}-W${week}`;
18
+ }
19
+ /** Idempotent DDL: the allowance buckets + the charge idempotency guard. The
20
+ * purse (credits_ledger) is created by the base adapter's init - not here. */
21
+ export async function init(d) {
22
+ const INT = d.dialect === "postgres" ? "BIGINT" : "INTEGER";
23
+ await d.exec(`CREATE TABLE IF NOT EXISTS credits_allowance (
24
+ tenant_id TEXT NOT NULL,
25
+ scope TEXT NOT NULL,
26
+ bucket TEXT NOT NULL,
27
+ balance ${INT} NOT NULL DEFAULT 0,
28
+ cap ${INT} NOT NULL DEFAULT 0,
29
+ cadence TEXT NOT NULL DEFAULT 'none',
30
+ priority ${INT} NOT NULL DEFAULT 100,
31
+ source TEXT,
32
+ period TEXT,
33
+ refilled_at ${INT},
34
+ PRIMARY KEY (tenant_id, scope, bucket)
35
+ )`);
36
+ await d.exec(`CREATE TABLE IF NOT EXISTS wallet_txn (
37
+ idem_key TEXT PRIMARY KEY,
38
+ tenant_id TEXT NOT NULL,
39
+ scope TEXT NOT NULL,
40
+ charged ${INT} NOT NULL,
41
+ at ${INT} NOT NULL
42
+ )`);
43
+ }
44
+ /** Define / update an allowance bucket's config (cap, cadence, priority). Does
45
+ * not change the live balance; the next refill() applies the cap. */
46
+ export async function setBucket(d, tenant, scope, def) {
47
+ const s = scope || GLOBAL;
48
+ const existing = await d.all("SELECT bucket FROM credits_allowance WHERE tenant_id=? AND scope=? AND bucket=?", [tenant, s, def.name]);
49
+ if (existing.length) {
50
+ await d.run("UPDATE credits_allowance SET cap=?, cadence=?, priority=?, source=? WHERE tenant_id=? AND scope=? AND bucket=?", [
51
+ def.cap,
52
+ def.cadence,
53
+ def.priority,
54
+ def.source ?? null,
55
+ tenant,
56
+ s,
57
+ def.name,
58
+ ]);
59
+ }
60
+ else {
61
+ await d.run("INSERT INTO credits_allowance (tenant_id, scope, bucket, balance, cap, cadence, priority, source) VALUES (?,?,?,0,?,?,?,?)", [
62
+ tenant,
63
+ s,
64
+ def.name,
65
+ def.cap,
66
+ def.cadence,
67
+ def.priority,
68
+ def.source ?? null,
69
+ ]);
70
+ }
71
+ }
72
+ /** Refill every allowance whose epoch has rolled to its cap (reset-to-cap, not
73
+ * additive). Returns the buckets topped and the total µcredits granted. */
74
+ export async function refill(d, now, tenant, scope) {
75
+ const s = scope || GLOBAL;
76
+ const nowMs = now();
77
+ const rows = await d.all("SELECT bucket, cap, cadence, period FROM credits_allowance WHERE tenant_id=? AND scope=?", [tenant, s]);
78
+ const topped = [];
79
+ let granted = 0n;
80
+ for (const r of rows) {
81
+ if (r.cadence === "none")
82
+ continue;
83
+ const key = periodKey(r.cadence, nowMs);
84
+ if (r.period === key)
85
+ continue; // already refilled this epoch
86
+ const cap = toBig(r.cap);
87
+ await d.run("UPDATE credits_allowance SET balance=?, period=?, refilled_at=? WHERE tenant_id=? AND scope=? AND bucket=?", [
88
+ cap,
89
+ key,
90
+ nowMs,
91
+ tenant,
92
+ s,
93
+ r.bucket,
94
+ ]);
95
+ topped.push(r.bucket);
96
+ granted += cap;
97
+ }
98
+ return { topped, granted };
99
+ }
100
+ /** Read the wallet: aggregate, purse, and per-bucket allowance balances. */
101
+ export async function balance(d, tenant, scope) {
102
+ const s = scope || GLOBAL;
103
+ const [purseRows, allowRows] = await Promise.all([
104
+ d.all("SELECT balance FROM credits_ledger WHERE tenant_id=? AND app=?", [tenant, s]),
105
+ d.all("SELECT bucket, balance, cap, cadence, priority, source FROM credits_allowance WHERE tenant_id=? AND scope=? ORDER BY priority ASC", [tenant, s]),
106
+ ]);
107
+ const purse = purseRows.length ? toBig(purseRows[0]?.balance) : 0n;
108
+ const buckets = allowRows.map((r) => ({
109
+ name: r.bucket,
110
+ balance: toBig(r.balance),
111
+ cap: toBig(r.cap),
112
+ cadence: r.cadence,
113
+ priority: Number(toBig(r.priority)),
114
+ source: r.source,
115
+ }));
116
+ const allowSum = buckets.reduce((acc, b) => acc + (b.balance > 0n ? b.balance : 0n), 0n);
117
+ return { aggregate: allowSum + purse, purse, buckets };
118
+ }
119
+ /** Add µcredits to the PURSE (Stripe deposit / admin top-up). The purse is the
120
+ * existing ledger; this nets against any overdraft automatically. */
121
+ export async function deposit(d, tenant, scope, ucredits) {
122
+ if (ucredits <= 0n)
123
+ return;
124
+ const s = scope || GLOBAL;
125
+ await ensurePurse(d, tenant, s);
126
+ await d.run("UPDATE credits_ledger SET balance = balance + ? WHERE tenant_id=? AND app=?", [ucredits, tenant, s]);
127
+ }
128
+ export async function ensurePurse(d, tenant, scope) {
129
+ await d.run("INSERT INTO credits_ledger (tenant_id, app, balance, held) VALUES (?,?,0,0) ON CONFLICT (tenant_id, app) DO NOTHING", [tenant, scope]);
130
+ }
131
+ //# sourceMappingURL=wallet-buckets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet-buckets.js","sourceRoot":"","sources":["../../src/meter/wallet-buckets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { SqlDriver } from "./sql/driver.js";
2
+ export interface ChargeResult {
3
+ /** Total µcredits actually applied (equals the request unless a replay -> 0n). */
4
+ charged: bigint;
5
+ /** Per-bucket breakdown, e.g. {daily: 30n, purse: 70n}. For the audit log. */
6
+ byBucket: Record<string, bigint>;
7
+ /** True when this idemKey was already charged (replay): nothing was applied. */
8
+ replay: boolean;
9
+ }
10
+ /** Charge across buckets in burn order: allowances by priority (floored at 0),
11
+ * then the purse, which OVERDRAFTS for any remainder (goes negative). Idempotent
12
+ * on idemKey across the whole charge. Never throws on funds - recording, not
13
+ * gating (the admission gate is the caller's job, off available()). */
14
+ export declare function charge(d: SqlDriver, now: () => number, tenant: string, scope: string, ucredits: bigint, idemKey?: string): Promise<ChargeResult>;
15
+ //# sourceMappingURL=wallet-charge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet-charge.d.ts","sourceRoot":"","sources":["../../src/meter/wallet-charge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAUjD,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;wEAGwE;AACxE,wBAAsB,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAqDtJ"}
@@ -0,0 +1,64 @@
1
+ import { toBig } from "./sql/driver.js";
2
+ /**
3
+ * wallet-charge - the cross-bucket charge (allowances first, purse overdrafts).
4
+ *
5
+ * Idempotent on `idemKey` across the WHOLE charge (allowance burns AND the purse
6
+ * overdraft) via a `wallet_txn` guard row, so a retried drain charges once.
7
+ */
8
+ import { GLOBAL, ensurePurse } from "./wallet-buckets.js";
9
+ /** Charge across buckets in burn order: allowances by priority (floored at 0),
10
+ * then the purse, which OVERDRAFTS for any remainder (goes negative). Idempotent
11
+ * on idemKey across the whole charge. Never throws on funds - recording, not
12
+ * gating (the admission gate is the caller's job, off available()). */
13
+ export async function charge(d, now, tenant, scope, ucredits, idemKey) {
14
+ const byBucket = {};
15
+ if (ucredits <= 0n)
16
+ return { charged: 0n, byBucket, replay: false };
17
+ const s = scope || GLOBAL;
18
+ // Whole-charge idempotency guard: a retried drain (same idemKey) records once.
19
+ if (idemKey) {
20
+ const nowMs = now();
21
+ const guard = await d.run("INSERT INTO wallet_txn (idem_key, tenant_id, scope, charged, at) VALUES (?,?,?,?,?) ON CONFLICT (idem_key) DO NOTHING", [
22
+ idemKey,
23
+ tenant,
24
+ s,
25
+ ucredits,
26
+ nowMs,
27
+ ]);
28
+ if (guard.rowsAffected === 0)
29
+ return { charged: 0n, byBucket, replay: true };
30
+ }
31
+ let remaining = ucredits;
32
+ // Allowances first, lowest priority number first. Conditional UPDATE floors at
33
+ // zero and the row count says how much it actually took (the row-lock pattern).
34
+ const allows = await d.all("SELECT bucket, balance FROM credits_allowance WHERE tenant_id=? AND scope=? AND balance > 0 ORDER BY priority ASC", [tenant, s]);
35
+ for (const a of allows) {
36
+ if (remaining <= 0n)
37
+ break;
38
+ const have = toBig(a.balance);
39
+ const take = have < remaining ? have : remaining;
40
+ if (take <= 0n)
41
+ continue;
42
+ // Guard on balance>=take so a concurrent burn can't drive it negative.
43
+ const res = await d.run("UPDATE credits_allowance SET balance = balance - ? WHERE tenant_id=? AND scope=? AND bucket=? AND balance >= ?", [
44
+ take,
45
+ tenant,
46
+ s,
47
+ a.bucket,
48
+ take,
49
+ ]);
50
+ if (res.rowsAffected > 0) {
51
+ byBucket[a.bucket] = (byBucket[a.bucket] ?? 0n) + take;
52
+ remaining -= take;
53
+ }
54
+ }
55
+ // Remainder hits the purse, which overdrafts (no floor - goes negative).
56
+ if (remaining > 0n) {
57
+ await ensurePurse(d, tenant, s);
58
+ await d.run("UPDATE credits_ledger SET balance = balance - ? WHERE tenant_id=? AND app=?", [remaining, tenant, s]);
59
+ byBucket.purse = (byBucket.purse ?? 0n) + remaining;
60
+ remaining = 0n;
61
+ }
62
+ return { charged: ucredits, byBucket, replay: false };
63
+ }
64
+ //# sourceMappingURL=wallet-charge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet-charge.js","sourceRoot":"","sources":["../../src/meter/wallet-charge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAW1D;;;wEAGwE;AACxE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAY,EAAE,GAAiB,EAAE,MAAc,EAAE,KAAa,EAAE,QAAgB,EAAE,OAAgB;IAC7H,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,IAAI,QAAQ,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACpE,MAAM,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC;IAE1B,+EAA+E;IAC/E,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,uHAAuH,EAAE;YACjJ,OAAO;YACP,MAAM;YACN,CAAC;YACD,QAAQ;YACR,KAAK;SACN,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC;YAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC/E,CAAC;IAED,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,+EAA+E;IAC/E,gFAAgF;IAChF,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,CACxB,mHAAmH,EACnH,CAAC,MAAM,EAAE,CAAC,CAAC,CACZ,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,SAAS,IAAI,EAAE;YAAE,MAAM;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACjD,IAAI,IAAI,IAAI,EAAE;YAAE,SAAS;QACzB,uEAAuE;QACvE,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,gHAAgH,EAAE;YACxI,IAAI;YACJ,MAAM;YACN,CAAC;YACD,CAAC,CAAC,MAAM;YACR,IAAI;SACL,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;YACvD,SAAS,IAAI,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,IAAI,SAAS,GAAG,EAAE,EAAE,CAAC;QACnB,MAAM,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,CAAC,GAAG,CAAC,6EAA6E,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACnH,QAAQ,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;QACpD,SAAS,GAAG,EAAE,CAAC;IACjB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Wallet - the multi-bucket layer over the single-balance ledger.
3
+ *
4
+ * A wallet is several BUCKETS drawn in a fixed BURN ORDER and presented as one
5
+ * aggregate. Two kinds:
6
+ *
7
+ * - ALLOWANCES (daily / weekly / monthly): grants that refill to a cap each
8
+ * epoch (reset-to-cap, NOT additive), floor at zero (you can't owe back a
9
+ * grant), and are ALWAYS BURNED FIRST. New `credits_allowance` table.
10
+ * - PURSE: the existing `credits_ledger` balance. Persists, burned LAST, and
11
+ * the ONLY bucket that can overdraft (go negative) - the model the queue
12
+ * already ships. Untouched here; the wallet just draws it last.
13
+ *
14
+ * Burn order is `priority` ascending across allowances, then the purse. So the
15
+ * canonical order daily -> monthly -> purse is priorities 10, 20, (purse) with
16
+ * weekly slotting at 15 when activated. Cadences are config: a bucket with
17
+ * `cadence:'none'` exists but never auto-refills, so daily/weekly can sit dormant
18
+ * in the schema until flipped on - no re-plumb.
19
+ *
20
+ * Idempotent on `idemKey` across the WHOLE charge (allowance burns AND the purse
21
+ * overdraft) via a `wallet_txn` guard row, so a retried drain charges once. This
22
+ * is purely additive: existing CreditsClient consumers are unaffected.
23
+ *
24
+ * This file is the barrel: the class stitches together wallet-buckets.ts (config,
25
+ * refill, reads) and wallet-charge.ts (the cross-bucket charge), and re-exports the
26
+ * full public API.
27
+ */
28
+ import type { SqlDriver } from "./sql/driver.js";
29
+ export type { Cadence, BucketDef, BucketBalance, WalletBalance } from "./wallet-buckets.js";
30
+ export type { ChargeResult } from "./wallet-charge.js";
31
+ import type { WalletBalance } from "./wallet-buckets.js";
32
+ import type { BucketDef } from "./wallet-buckets.js";
33
+ import type { ChargeResult } from "./wallet-charge.js";
34
+ export interface WalletOptions {
35
+ /** Injectable clock (epoch ms). Defaults to Date.now. */
36
+ now?: () => number;
37
+ }
38
+ export declare class Wallet {
39
+ private readonly d;
40
+ private readonly now;
41
+ constructor(driver: SqlDriver, opts?: WalletOptions);
42
+ /** Idempotent DDL: the allowance buckets + the charge idempotency guard. The
43
+ * purse (credits_ledger) is created by the base adapter's init - not here. */
44
+ init(): Promise<void>;
45
+ /** Define / update an allowance bucket's config (cap, cadence, priority). Does
46
+ * not change the live balance; the next refill() applies the cap. */
47
+ setBucket(tenant: string, scope: string, def: BucketDef): Promise<void>;
48
+ /** Refill every allowance whose epoch has rolled to its cap (reset-to-cap, not
49
+ * additive). Returns the buckets topped and the total µcredits granted. */
50
+ refill(tenant: string, scope: string): Promise<{
51
+ topped: string[];
52
+ granted: bigint;
53
+ }>;
54
+ /** Read the wallet: aggregate, purse, and per-bucket allowance balances. */
55
+ balance(tenant: string, scope: string): Promise<WalletBalance>;
56
+ /** Spendable now, for the admission gate: allowance balances (floored) + purse.
57
+ * Same number as balance().aggregate, one query path. */
58
+ available(tenant: string, scope: string): Promise<bigint>;
59
+ /** Add µcredits to the PURSE (Stripe deposit / admin top-up). The purse is the
60
+ * existing ledger; this nets against any overdraft automatically. */
61
+ deposit(tenant: string, scope: string, ucredits: bigint): Promise<void>;
62
+ /** Charge across buckets in burn order: allowances by priority (floored at 0),
63
+ * then the purse, which OVERDRAFTS for any remainder (goes negative). Idempotent
64
+ * on idemKey across the whole charge. Never throws on funds - recording, not
65
+ * gating (the admission gate is the caller's job, off available()). */
66
+ charge(tenant: string, scope: string, ucredits: bigint, idemKey?: string): Promise<ChargeResult>;
67
+ }
68
+ //# sourceMappingURL=wallet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../../src/meter/wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC5F,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAY;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,aAAkB;IAKvD;mFAC+E;IACzE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;0EACsE;IAChE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;gFAC4E;IACtE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAI3F,4EAA4E;IACtE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpE;8DAC0D;IACpD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI/D;0EACsE;IAChE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;;4EAGwE;IAClE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAGvG"}
@@ -0,0 +1,47 @@
1
+ import * as buckets from "./wallet-buckets.js";
2
+ import * as chargeMod from "./wallet-charge.js";
3
+ export class Wallet {
4
+ d;
5
+ now;
6
+ constructor(driver, opts = {}) {
7
+ this.d = driver;
8
+ this.now = opts.now ?? (() => Date.now());
9
+ }
10
+ /** Idempotent DDL: the allowance buckets + the charge idempotency guard. The
11
+ * purse (credits_ledger) is created by the base adapter's init - not here. */
12
+ async init() {
13
+ return buckets.init(this.d);
14
+ }
15
+ /** Define / update an allowance bucket's config (cap, cadence, priority). Does
16
+ * not change the live balance; the next refill() applies the cap. */
17
+ async setBucket(tenant, scope, def) {
18
+ return buckets.setBucket(this.d, tenant, scope, def);
19
+ }
20
+ /** Refill every allowance whose epoch has rolled to its cap (reset-to-cap, not
21
+ * additive). Returns the buckets topped and the total µcredits granted. */
22
+ async refill(tenant, scope) {
23
+ return buckets.refill(this.d, this.now, tenant, scope);
24
+ }
25
+ /** Read the wallet: aggregate, purse, and per-bucket allowance balances. */
26
+ async balance(tenant, scope) {
27
+ return buckets.balance(this.d, tenant, scope);
28
+ }
29
+ /** Spendable now, for the admission gate: allowance balances (floored) + purse.
30
+ * Same number as balance().aggregate, one query path. */
31
+ async available(tenant, scope) {
32
+ return (await this.balance(tenant, scope)).aggregate;
33
+ }
34
+ /** Add µcredits to the PURSE (Stripe deposit / admin top-up). The purse is the
35
+ * existing ledger; this nets against any overdraft automatically. */
36
+ async deposit(tenant, scope, ucredits) {
37
+ return buckets.deposit(this.d, tenant, scope, ucredits);
38
+ }
39
+ /** Charge across buckets in burn order: allowances by priority (floored at 0),
40
+ * then the purse, which OVERDRAFTS for any remainder (goes negative). Idempotent
41
+ * on idemKey across the whole charge. Never throws on funds - recording, not
42
+ * gating (the admission gate is the caller's job, off available()). */
43
+ async charge(tenant, scope, ucredits, idemKey) {
44
+ return chargeMod.charge(this.d, this.now, tenant, scope, ucredits, idemKey);
45
+ }
46
+ }
47
+ //# sourceMappingURL=wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.js","sourceRoot":"","sources":["../../src/meter/wallet.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAahD,MAAM,OAAO,MAAM;IACA,CAAC,CAAY;IACb,GAAG,CAAe;IAEnC,YAAY,MAAiB,EAAE,OAAsB,EAAE;QACrD,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC;QAChB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;mFAC+E;IAC/E,KAAK,CAAC,IAAI;QACR,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IAED;0EACsE;IACtE,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,KAAa,EAAE,GAAc;QAC3D,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IAED;gFAC4E;IAC5E,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,KAAa;QACxC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAED,4EAA4E;IAC5E,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,KAAa;QACzC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED;8DAC0D;IAC1D,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,KAAa;QAC3C,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,CAAC;IAED;0EACsE;IACtE,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,KAAa,EAAE,QAAgB;QAC3D,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAED;;;4EAGwE;IACxE,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,KAAa,EAAE,QAAgB,EAAE,OAAgB;QAC5E,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9E,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ import type { Receipt, ViewerConfig } from "./receipts-types.js";
2
+ export declare function openReceiptModal(chain: Receipt[], cfg?: ViewerConfig): Promise<void>;
3
+ export declare function openReceiptById(id: string, cfg?: ViewerConfig): Promise<void>;
4
+ export declare function mountVerifyButton(target: HTMLElement | null, source: Receipt[] | Receipt | string | (() => Receipt[] | Receipt | string | Promise<Receipt[] | Receipt | string>), cfg?: ViewerConfig): void;
5
+ //# sourceMappingURL=receipts-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipts-api.d.ts","sourceRoot":"","sources":["../src/receipts-api.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAajE,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,GAAE,YAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBrG;AAID,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,YAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiC9F;AAID,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,GAAG,IAAI,EAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,OAAO,EAAE,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,EACnH,GAAG,GAAE,YAAiB,GACrB,IAAI,CAcN"}