@voltro/plugin-billing 0.86.0 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/README.md +428 -0
  3. package/THIRD-PARTY-NOTICES.md +1511 -276
  4. package/dist/catalog.js +1 -2
  5. package/dist/checkoutStore.js +164 -0
  6. package/dist/closeStore.js +124 -0
  7. package/dist/custodyInventory.js +154 -0
  8. package/dist/customerStore.js +258 -0
  9. package/dist/dunning.js +1 -2
  10. package/dist/economy.d.ts +30 -0
  11. package/dist/economy.js +265 -0
  12. package/dist/entitlement.js +1 -2
  13. package/dist/eventDelivery.js +71 -0
  14. package/dist/flushClaim.js +1 -2
  15. package/dist/index.d.ts +669 -65
  16. package/dist/index.js +230 -173
  17. package/dist/invoiceHistory.js +90 -0
  18. package/dist/invoiceHistoryJournal.js +475 -0
  19. package/dist/invoiceHistoryProgress.js +234 -0
  20. package/dist/invoiceHistoryWork.js +362 -0
  21. package/dist/invoiceSnapshot.js +65 -0
  22. package/dist/invoiceStore.js +132 -0
  23. package/dist/metering.js +1 -2
  24. package/dist/plans.js +1 -2
  25. package/dist/providerIdentity.js +23 -0
  26. package/dist/providers.js +271 -67
  27. package/dist/provision.js +1 -2
  28. package/dist/rpc.js +1 -2
  29. package/dist/service.js +892 -392
  30. package/dist/stores.js +125 -291
  31. package/dist/stripeClient.js +1 -2
  32. package/dist/stripeIdentity.js +55 -0
  33. package/dist/stripeInvoiceHistory.js +66 -0
  34. package/dist/stripeInvoiceSnapshot.js +85 -0
  35. package/dist/stripeProvider.js +305 -179
  36. package/dist/stripeRest.js +1 -2
  37. package/dist/stripeVerifiedEvent.js +28 -0
  38. package/dist/table.js +70 -48
  39. package/dist/types.d.ts +298 -27
  40. package/dist/types.js +1 -2
  41. package/dist/usageEvents.js +64 -0
  42. package/dist/web/index.js +1 -2
  43. package/dist/webhook.js +32 -40
  44. package/package.json +18 -8
@@ -0,0 +1,234 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { captureProviderAccount as t, exactProviderIdentifier as n, sameProviderAccount as r } from "./providerIdentity.js";
3
+ import { BILLING_CUSTOMER_CUSTODY_TABLE as i, dataStoreCustomerStore as a } from "./customerStore.js";
4
+ import { invoiceHistoryJournalInTransaction as o } from "./invoiceHistoryJournal.js";
5
+ import { BILLING_INVOICE_HISTORY_WORK_TABLE as s } from "./invoiceHistoryWork.js";
6
+ import { Effect as c } from "effect";
7
+ import { and as l, eq as u, generateId as d, id as f, integer as p, table as m, text as h, timestamp as g } from "@voltro/database";
8
+ // src/invoiceHistoryProgress.ts
9
+ var _ = "_voltro_billing_invoice_history_scan_accounts", v = "_voltro_billing_invoice_history_dispatch", y = () => ({
10
+ provider: h().maxLength(64),
11
+ providerAccountId: h().maxLength(191),
12
+ livemode: p(),
13
+ identityKey: h().maxLength(64),
14
+ revision: p(),
15
+ updatedAt: g()
16
+ }), b = m(_, {
17
+ id: f({ prefix: "bihscan" }),
18
+ ...y(),
19
+ afterCustodyId: h().maxLength(191).nullable()
20
+ }).unique("byBillingHistoryScanAccount", ["identityKey"]), x = m(v, {
21
+ id: f({ prefix: "bihdispatch" }),
22
+ ...y(),
23
+ tenantId: h().maxLength(191),
24
+ providerCustomerId: h().maxLength(191),
25
+ custodyId: h().maxLength(191),
26
+ walkId: h().maxLength(191).nullable(),
27
+ afterUnpreparedId: h().maxLength(191).nullable(),
28
+ afterReadyId: h().maxLength(191).nullable(),
29
+ afterWorkingId: h().maxLength(191).nullable()
30
+ }).unique("byBillingHistoryDispatchCustody", ["identityKey"]).index("byBillingHistoryDispatchTenant", ["tenantId"]), S = 2147483647, C = () => new e({
31
+ source: "invoiceHistoryProgress",
32
+ message: "Invoice-history traversal requires fresh owned native state.",
33
+ transient: !0
34
+ }), w = (e, t = 191) => {
35
+ if (!n(e) || e.length > t || !e.isWellFormed()) throw C();
36
+ return e;
37
+ }, T = (e, t) => w(d({
38
+ kind: "typeid",
39
+ prefix: t
40
+ }, e)), E = (e) => e === null ? null : w(e), D = (e) => {
41
+ let t = e instanceof Date ? new Date(e) : new Date(typeof e == "string" ? e : NaN);
42
+ if (!Number.isFinite(t.getTime())) throw C();
43
+ return t;
44
+ }, O = (e) => {
45
+ if (typeof e != "number" || !Number.isInteger(e) || e < 1 || e > S) throw C();
46
+ return e;
47
+ }, k = (e) => {
48
+ let n = t(e.account);
49
+ return w(n.providerAccountId), {
50
+ provider: w(e.provider, 64),
51
+ account: n
52
+ };
53
+ }, A = (e) => ({
54
+ provider: e.provider,
55
+ providerAccountId: e.account.providerAccountId,
56
+ livemode: +!!e.account.livemode
57
+ }), j = async (e, t) => [...new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify([
58
+ "voltro.billing-history-progress.v1",
59
+ e.provider,
60
+ e.account.providerAccountId,
61
+ e.account.livemode,
62
+ t
63
+ ]))))].map((e) => e.toString(16).padStart(2, "0")).join(""), M = (e, t, n = !1) => ({
64
+ table: e,
65
+ predicate: u("identityKey", t),
66
+ order: [],
67
+ take: 2,
68
+ skip: void 0,
69
+ projection: void 0,
70
+ ...n ? { lock: "update" } : {}
71
+ }), N = (t) => c.tryPromise({
72
+ try: t,
73
+ catch: (t) => t instanceof e ? t : C()
74
+ }), P = [
75
+ "unprepared",
76
+ "ready",
77
+ "working"
78
+ ], F = (e) => {
79
+ let t = async (e, t) => {
80
+ let n = k(t), i = w(t.id), o = w(t.tenantId), s = w(t.providerCustomerId), l = await c.runPromise(a(e).getByProviderCustomerId(s, n));
81
+ if (l === null || l.id !== i || l.tenantId !== o || l.provider !== n.provider || !r(l.account, n.account)) throw C();
82
+ return l;
83
+ }, n = async (e, t, n) => {
84
+ let r = await e.query({
85
+ ...M(i, n),
86
+ predicate: l(u("id", n), u("provider", t.provider), u("providerAccountId", t.account.providerAccountId), u("livemode", +!!t.account.livemode))
87
+ });
88
+ if (r.length !== 1 || r[0]?.id !== n) throw C();
89
+ let o = await c.runPromise(a(e).getByProviderCustomerId(w(r[0].providerCustomerId), t));
90
+ if (o === null || o.id !== n || o.tenantId !== w(r[0].tenantId)) throw C();
91
+ }, d = async (e, t, n, r, i = !0) => {
92
+ let a = await e.query(M(t, r, i));
93
+ if (a.length > 1) throw C();
94
+ let o = a[0];
95
+ if (!o) return null;
96
+ if (o.identityKey !== r || o.provider !== n.provider || o.providerAccountId !== n.account.providerAccountId || o.livemode !== +!!n.account.livemode) throw C();
97
+ return w(o.id), O(o.revision), D(o.updatedAt), o;
98
+ }, f = async (e, t, r) => {
99
+ let i = E(r.afterCustodyId);
100
+ return i !== null && await n(e, t, i), {
101
+ ...t,
102
+ id: w(r.id),
103
+ revision: O(r.revision),
104
+ afterCustodyId: i,
105
+ updatedAt: D(r.updatedAt)
106
+ };
107
+ }, p = async (e, t, n) => {
108
+ if (n.tenantId !== t.tenantId || n.providerCustomerId !== t.providerCustomerId || n.custodyId !== t.id) throw C();
109
+ let r = E(n.walkId), i = {
110
+ unprepared: E(n.afterUnpreparedId),
111
+ ready: E(n.afterReadyId),
112
+ working: E(n.afterWorkingId)
113
+ };
114
+ if (r === null && P.some((e) => i[e] !== null)) throw C();
115
+ if (r !== null) {
116
+ let n = o(e), a = await c.runPromise(n.read(t));
117
+ if (a === null || a.id !== r) throw C();
118
+ for (let a of P) {
119
+ let o = i[a];
120
+ if (o === null) continue;
121
+ let l = o;
122
+ if (a !== "unprepared") {
123
+ let n = await e.query({
124
+ ...M(s, o),
125
+ predicate: u("id", o)
126
+ });
127
+ if (n.length !== 1 || n[0]?.id !== o) throw C();
128
+ let i = n[0];
129
+ if (i.walkId !== r || i.custodyId !== t.id || i.tenantId !== t.tenantId || i.providerCustomerId !== t.providerCustomerId || i.provider !== t.provider || i.providerAccountId !== t.account.providerAccountId || i.livemode !== +!!t.account.livemode) throw C();
130
+ l = w(i.referenceId);
131
+ }
132
+ let d = await c.runPromise(n.reference(t, l));
133
+ if (d === null || d.walkId !== r) throw C();
134
+ }
135
+ }
136
+ return {
137
+ id: w(n.id),
138
+ revision: O(n.revision),
139
+ owner: t,
140
+ walkId: r,
141
+ cursors: i,
142
+ updatedAt: D(n.updatedAt)
143
+ };
144
+ }, m = async (e, t, n, r, i, a, o) => {
145
+ let s = await d(e, t, n, r);
146
+ if (s === null || s.id !== w(i.id) || s.revision !== O(i.revision)) return null;
147
+ if (Object.entries(a).every(([e, t]) => t instanceof Date ? D(s[e]).getTime() === t.getTime() : s[e] === t)) return s;
148
+ if (i.revision >= S) throw C();
149
+ let c = {
150
+ ...a,
151
+ revision: i.revision + 1,
152
+ updatedAt: o
153
+ }, f = await e.updateMany(t, c, { where: l(u("id", i.id), u("identityKey", r), u("revision", i.revision)) });
154
+ if (f === 0) return null;
155
+ if (f !== 1) throw C();
156
+ let p = await d(e, t, n, r);
157
+ if (p === null || p.id !== i.id) throw C();
158
+ for (let [e, t] of Object.entries(c)) if (t instanceof Date ? D(p[e]).getTime() !== t.getTime() : p[e] !== t) throw C();
159
+ return p;
160
+ };
161
+ return {
162
+ account: (t, n) => N(async () => {
163
+ let r = k(t), i = D(n), a = await j(r, null);
164
+ return e.transactional(async (e) => {
165
+ await e.insertIgnore(_, {
166
+ id: T(_, "bihscan"),
167
+ ...A(r),
168
+ identityKey: a,
169
+ revision: 1,
170
+ afterCustodyId: null,
171
+ updatedAt: i
172
+ }, { conflictColumns: ["identityKey"] });
173
+ let t = await d(e, _, r, a);
174
+ if (t === null) throw C();
175
+ return f(e, r, t);
176
+ });
177
+ }),
178
+ advanceAccount: (t, r, i) => N(async () => {
179
+ let a = structuredClone(t), o = k(a), s = E(r), c = D(i), l = await j(o, null);
180
+ return e.transactional(async (e) => {
181
+ s !== null && await n(e, o, s);
182
+ let t = await m(e, _, o, l, a, { afterCustodyId: s }, c);
183
+ return t === null ? null : f(e, o, t);
184
+ });
185
+ }),
186
+ customer: (n, r) => N(async () => {
187
+ let i = structuredClone(n), a = D(r);
188
+ return e.transactional(async (e) => {
189
+ let n = await t(e, i), r = await j(n, n.id);
190
+ await e.insertIgnore(v, {
191
+ id: T(v, "bihdispatch"),
192
+ ...A(n),
193
+ tenantId: n.tenantId,
194
+ providerCustomerId: n.providerCustomerId,
195
+ custodyId: n.id,
196
+ identityKey: r,
197
+ revision: 1,
198
+ walkId: null,
199
+ afterUnpreparedId: null,
200
+ afterReadyId: null,
201
+ afterWorkingId: null,
202
+ updatedAt: a
203
+ }, { conflictColumns: ["identityKey"] });
204
+ let o = await d(e, v, n, r);
205
+ if (o === null) throw C();
206
+ return p(e, n, o);
207
+ });
208
+ }),
209
+ advanceCustomer: (n, r, i) => N(async () => {
210
+ let a = structuredClone(n), o = structuredClone(r), s = D(i);
211
+ return e.transactional(async (e) => {
212
+ let n = await t(e, a.owner), r = await j(n, n.id), i = {
213
+ walkId: E(o.walkId),
214
+ afterUnpreparedId: E(o.cursors.unprepared),
215
+ afterReadyId: E(o.cursors.ready),
216
+ afterWorkingId: E(o.cursors.working)
217
+ };
218
+ await p(e, n, {
219
+ id: a.id,
220
+ revision: a.revision,
221
+ ...A(n),
222
+ tenantId: n.tenantId,
223
+ providerCustomerId: n.providerCustomerId,
224
+ custodyId: n.id,
225
+ ...i,
226
+ updatedAt: s
227
+ });
228
+ let c = await m(e, v, n, r, a, i, s);
229
+ return c === null ? null : p(e, n, c);
230
+ });
231
+ })
232
+ };
233
+ };
234
+ export { v as BILLING_INVOICE_HISTORY_DISPATCH_TABLE, _ as BILLING_INVOICE_HISTORY_SCAN_ACCOUNTS_TABLE, x as billingInvoiceHistoryDispatchTable, b as billingInvoiceHistoryScanAccountsTable, F as dataStoreInvoiceHistoryProgressStore };
@@ -0,0 +1,362 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { captureProviderAccount as t, exactProviderIdentifier as n, sameProviderAccount as r } from "./providerIdentity.js";
3
+ import { BILLING_INVOICE_HISTORY_REFERENCES_TABLE as i, invoiceHistoryJournalInTransaction as a } from "./invoiceHistoryJournal.js";
4
+ import { dataStoreInvoiceStore as o } from "./invoiceStore.js";
5
+ import { Effect as s, Schema as c } from "effect";
6
+ import { DEFAULT_IN_FLIGHT_LEASE_MS as l, beginIdempotent as u, finishIdempotent as d, holdIdempotentClaim as f, prepareIdempotentInput as p, systemSubject as m } from "@voltro/protocol";
7
+ import { and as h, eq as g, gt as _, id as v, inSet as y, integer as b, lte as x, notInSubquery as S, table as C, text as w, timestamp as T } from "@voltro/database";
8
+ import { dataStoreIdempotencyStore as E } from "@voltro/runtime";
9
+ // src/invoiceHistoryWork.ts
10
+ var D = "_voltro_billing_invoice_history_work", O = C(D, {
11
+ id: v({ prefix: "bihwork" }),
12
+ tenantId: w().maxLength(191),
13
+ provider: w().maxLength(64),
14
+ providerAccountId: w().maxLength(191),
15
+ livemode: b(),
16
+ providerCustomerId: w().maxLength(191),
17
+ custodyId: w().maxLength(191),
18
+ walkId: w().maxLength(191),
19
+ referenceId: w().maxLength(191),
20
+ providerInvoiceId: w().maxLength(191),
21
+ revision: b(),
22
+ attempt: b(),
23
+ executions: b(),
24
+ state: w().maxLength(32),
25
+ activeClaimId: w().maxLength(191).nullable(),
26
+ nextAttemptAt: T(),
27
+ lastOutcome: w().maxLength(32),
28
+ observationId: w().maxLength(191).nullable(),
29
+ observationRevision: b().nullable(),
30
+ updatedAt: T()
31
+ }).unique("byBillingInvoiceHistoryWorkReference", ["referenceId"]).index("byBillingInvoiceHistoryWorkDue", [
32
+ "walkId",
33
+ "state",
34
+ "nextAttemptAt"
35
+ ]).index("byBillingInvoiceHistoryWorkTenant", ["tenantId"]), k = 2147483647, A = 6e4, j = 864e5, M = m("billing.invoice-history-work", []), N = (t = "Invoice reference work remains unconfirmed and requires reconciliation.", n = !0) => new e({
36
+ source: "invoiceHistoryWork",
37
+ message: t,
38
+ transient: n
39
+ }), P = (e, t = 191) => {
40
+ if (!n(e) || e.length > t || !e.isWellFormed()) throw N();
41
+ return e;
42
+ }, F = (e, t = 0) => {
43
+ if (typeof e != "number" || !Number.isInteger(e) || e < t || e > k) throw N();
44
+ return e;
45
+ }, I = (e) => {
46
+ let t = e instanceof Date ? new Date(e) : new Date(typeof e == "string" ? e : NaN);
47
+ if (!Number.isFinite(t.getTime())) throw N();
48
+ return t;
49
+ }, L = (e, t) => I(new Date(e.getTime() + t)), R = (t) => s.tryPromise({
50
+ try: t,
51
+ catch: (t) => t instanceof e ? t : N()
52
+ }), z = (e, t = !1) => ({
53
+ table: D,
54
+ predicate: g("referenceId", e),
55
+ order: [],
56
+ take: 2,
57
+ skip: void 0,
58
+ projection: void 0,
59
+ ...t ? { lock: "update" } : {}
60
+ }), B = (e) => ({
61
+ tenantId: e.tenantId,
62
+ provider: e.provider,
63
+ providerAccountId: e.account.providerAccountId,
64
+ livemode: +!!e.account.livemode,
65
+ providerCustomerId: e.providerCustomerId,
66
+ custodyId: e.custodyId,
67
+ walkId: e.walkId,
68
+ referenceId: e.id,
69
+ providerInvoiceId: e.providerInvoiceId
70
+ }), V = (e) => ({
71
+ tenantId: e.tenantId,
72
+ provider: e.provider,
73
+ ...e.account,
74
+ providerCustomerId: e.providerCustomerId,
75
+ providerInvoiceId: e.providerInvoiceId
76
+ }), H = (e, n) => {
77
+ if (e.livemode !== 0 && e.livemode !== 1) throw N();
78
+ let i = t({
79
+ providerAccountId: P(e.providerAccountId),
80
+ livemode: e.livemode === 1
81
+ });
82
+ if (e.tenantId !== n.tenantId || e.provider !== n.provider || e.providerCustomerId !== n.providerCustomerId || e.custodyId !== n.custodyId || e.walkId !== n.walkId || e.referenceId !== n.id || e.providerInvoiceId !== n.providerInvoiceId || !r(i, n.account)) throw N();
83
+ let a = e.state, o = e.lastOutcome, s = e.activeClaimId;
84
+ if (a !== "queued" && a !== "working" && a !== "retry" && a !== "deferred" && a !== "observed" || o !== "unconfirmed" && o !== "open" && o !== "observed") throw N();
85
+ let c = e.observationId === null ? null : P(e.observationId), l = e.observationRevision === null ? null : F(e.observationRevision, 1);
86
+ if (c === null != (l === null) || a === "working" != (s !== null) || (a === "observed" || a === "deferred") && c === null || a === "observed" && o !== "observed" || a === "deferred" && o !== "open") throw N();
87
+ let u = {
88
+ id: P(e.id),
89
+ tenantId: n.tenantId,
90
+ provider: n.provider,
91
+ account: i,
92
+ providerCustomerId: n.providerCustomerId,
93
+ custodyId: n.custodyId,
94
+ walkId: n.walkId,
95
+ referenceId: n.id,
96
+ providerInvoiceId: n.providerInvoiceId,
97
+ revision: F(e.revision, 1),
98
+ attempt: F(e.attempt),
99
+ executions: F(e.executions),
100
+ state: a,
101
+ activeClaimId: s === null ? null : P(s),
102
+ nextAttemptAt: I(e.nextAttemptAt),
103
+ lastOutcome: o,
104
+ observationId: c,
105
+ observationRevision: l,
106
+ updatedAt: I(e.updatedAt)
107
+ };
108
+ if (a === "working" && (u.attempt === 0 || u.executions === 0)) throw N();
109
+ return u;
110
+ }, U = (e) => "billing.invoice-history-work.v1:" + e.walkId, W = (e, t) => e.id + ":" + t, G = async (e, t) => (await p(c.Unknown, [
111
+ e.id,
112
+ e.tenantId,
113
+ e.provider,
114
+ e.account.providerAccountId,
115
+ e.account.livemode,
116
+ e.providerCustomerId,
117
+ e.custodyId,
118
+ e.walkId,
119
+ e.referenceId,
120
+ e.providerInvoiceId,
121
+ t
122
+ ], M)).fingerprint, K = (e) => {
123
+ let t = structuredClone(e.work), n = Object.freeze({
124
+ scope: P(e.claim.scope, 1024),
125
+ key: P(e.claim.key),
126
+ id: P(e.claim.id),
127
+ inputHash: P(e.claim.inputHash, 64)
128
+ });
129
+ if (!/^[a-f0-9]{64}$/.test(n.inputHash) || n.scope !== U(t) || n.key !== W(t, F(t.attempt, 1)) || t.state !== "working" || t.activeClaimId !== n.id) throw N();
130
+ return Object.freeze({
131
+ work: Object.freeze(t),
132
+ claim: n
133
+ });
134
+ }, q = (e) => {
135
+ let t = E(e), n = async (e, t, n) => {
136
+ let r = await s.runPromise(a(e).reference(t, n));
137
+ if (r === null) throw N("The original owned invoice history reference is missing.", !1);
138
+ return r;
139
+ }, r = async (e, t, n = !1) => {
140
+ let r = await e.query(z(t.id, n));
141
+ if (r.length > 1) throw N();
142
+ return r[0] ? H(r[0], t) : null;
143
+ }, c = async (e, t, n, i, a) => {
144
+ if (n.revision >= k) throw N("The invoice reference work revision is exhausted.", !1);
145
+ if (await e.updateMany("_voltro_billing_invoice_history_work", {
146
+ ...i,
147
+ revision: n.revision + 1,
148
+ updatedAt: a
149
+ }, { where: h(g("id", n.id), g("referenceId", t.id), g("revision", n.revision), g("custodyId", t.custodyId), g("walkId", t.walkId)) }) !== 1) throw N();
150
+ let o = await r(e, t, !0);
151
+ if (o === null || o.id !== n.id || o.revision !== n.revision + 1 || o.updatedAt.getTime() !== a.getTime()) throw N();
152
+ for (let [e, t] of Object.entries(i)) {
153
+ let n = Object.getOwnPropertyDescriptor(o, e)?.value;
154
+ if (t instanceof Date ? !(n instanceof Date) || n.getTime() !== t.getTime() : n !== t) throw N("The invoice work acknowledgement was not read back exactly.");
155
+ }
156
+ return o;
157
+ }, p = async (e, t, n) => {
158
+ let i = await r(e, t, !0), a = n.work;
159
+ return i === null || i.id !== a.id || i.referenceId !== a.referenceId || i.state !== "working" || i.attempt !== a.attempt || i.activeClaimId !== n.claim.id || n.claim.inputHash !== await G(i, i.attempt) ? null : i;
160
+ };
161
+ return {
162
+ pending: (t, r, o, c) => R(async () => {
163
+ let l = structuredClone(t), u = I(o), d = c?.limit ?? 25;
164
+ if (![
165
+ "unprepared",
166
+ "ready",
167
+ "working"
168
+ ].includes(r) || !Number.isInteger(d) || d < 1 || d > 100) throw N();
169
+ let f = c?.cursor === void 0 ? null : {
170
+ lane: c.cursor.lane,
171
+ walkId: P(c.cursor.walkId),
172
+ afterId: P(c.cursor.afterId)
173
+ };
174
+ if (f !== null && f.lane !== r) throw N("Invoice work cursors belong to one selection lane.", !1);
175
+ return e.transactional(async (e) => {
176
+ let t = a(e), o = await s.runPromise(t.read(l));
177
+ if (o === null) throw N("No retained owned invoice history walk exists.");
178
+ let c = await s.runPromise(t.begin(l));
179
+ if (c.id !== o.id || f !== null && f.walkId !== c.id) throw N("Invoice work cursor differs from the retained walk.", !1);
180
+ let p = h(g("tenantId", c.tenantId), g("provider", c.provider), g("providerAccountId", c.account.providerAccountId), g("livemode", +!!c.account.livemode), g("providerCustomerId", c.providerCustomerId), g("custodyId", c.custodyId), g("walkId", c.id)), m = r === "unprepared", v = m ? i : D;
181
+ if (f !== null) {
182
+ let t = await e.query({
183
+ ...z(f.afterId),
184
+ table: v,
185
+ predicate: h(p, g("id", f.afterId))
186
+ });
187
+ if (t.length !== 1 || t[0]?.id !== f.afterId) throw N("Invoice work cursor is no longer retained.", !0);
188
+ let r = await n(e, l, m ? f.afterId : P(t[0]?.referenceId));
189
+ m || H(t[0], r);
190
+ }
191
+ let b = [p];
192
+ f !== null && b.push(_("id", f.afterId)), m ? b.push(x("pageOrdinal", c.pageOrdinal), S("id", {
193
+ ...z(c.id),
194
+ predicate: p,
195
+ projection: ["referenceId"],
196
+ take: void 0
197
+ })) : b.push(x("nextAttemptAt", u), r === "working" ? g("state", "working") : y("state", [
198
+ "queued",
199
+ "retry",
200
+ "deferred"
201
+ ]));
202
+ let C = await e.query({
203
+ ...z(c.id),
204
+ table: v,
205
+ predicate: h(...b),
206
+ order: [{
207
+ column: "id",
208
+ direction: "asc"
209
+ }],
210
+ take: d + 1
211
+ });
212
+ if (C.length > d + 1) throw N();
213
+ let w = await s.runPromise(t.referencesByIds(l, C.map((e) => P(m ? e.id : e.referenceId))));
214
+ if (m && C.length > 0 && (await e.query({
215
+ ...z(c.id),
216
+ predicate: y("referenceId", w.map((e) => e.id)),
217
+ take: 1
218
+ })).length > 0) throw N("An unprepared invoice reference already has native work.", !0);
219
+ let T = f?.afterId ?? null, E = [];
220
+ for (let [e, t] of C.entries()) {
221
+ let n = P(t.id);
222
+ if (T !== null && n <= T) throw N("Invoice work selection is not strictly ordered.");
223
+ T = n;
224
+ let i = w[e];
225
+ if (i === void 0 || i.walkId !== c.id || i.pageOrdinal > c.pageOrdinal) throw N();
226
+ let a = m ? null : H(t, i);
227
+ if (a !== null && (a.id !== n || a.nextAttemptAt.getTime() > u.getTime() || (r === "working" ? a.state !== "working" : ![
228
+ "queued",
229
+ "retry",
230
+ "deferred"
231
+ ].includes(a.state)))) throw N("Invoice work changed while selecting its lane.", !0);
232
+ E.push({
233
+ reference: i,
234
+ work: a
235
+ });
236
+ }
237
+ return {
238
+ entries: E.slice(0, d),
239
+ nextCursor: C.length > d ? {
240
+ lane: r,
241
+ walkId: c.id,
242
+ afterId: P(C[d - 1]?.id)
243
+ } : null,
244
+ walkRevision: c.revision
245
+ };
246
+ });
247
+ }),
248
+ claim: (t, i, a) => R(async () => {
249
+ let d = structuredClone(t), f = P(i), p = I(a);
250
+ return e.transactional(async (e) => {
251
+ let t = await n(e, d, f);
252
+ await e.insertIgnore(D, {
253
+ id: "bihwork_" + globalThis.crypto.randomUUID(),
254
+ ...B(t),
255
+ revision: 1,
256
+ attempt: 0,
257
+ executions: 0,
258
+ state: "queued",
259
+ activeClaimId: null,
260
+ nextAttemptAt: p,
261
+ lastOutcome: "unconfirmed",
262
+ observationId: null,
263
+ observationRevision: null,
264
+ updatedAt: p
265
+ }, { conflictColumns: ["referenceId"] });
266
+ let i = await r(e, t, !0);
267
+ if (i === null) throw N();
268
+ if (i.state === "observed") {
269
+ let n = await s.runPromise(o(e).get(V(t)));
270
+ if (n !== null && n.id === i.observationId && n.revision >= i.observationRevision && n.snapshot.status !== "open") return {
271
+ kind: "observed",
272
+ work: i
273
+ };
274
+ i = await c(e, t, i, {
275
+ state: "queued",
276
+ activeClaimId: null,
277
+ lastOutcome: "unconfirmed",
278
+ nextAttemptAt: p
279
+ }, p);
280
+ }
281
+ if (i.state !== "working" && i.nextAttemptAt.getTime() > p.getTime()) return {
282
+ kind: "busy",
283
+ work: i
284
+ };
285
+ let a = i.state === "working" ? i.attempt : i.attempt + 1;
286
+ if (a > k || i.executions >= k) throw N("The invoice reference attempt counter is exhausted.", !1);
287
+ let m = await G(i, a), h = await u(E(e), U(i), W(i, a), m, j, p.getTime());
288
+ if (h.kind === "replay") return i = await c(e, t, i, {
289
+ attempt: a,
290
+ state: "queued",
291
+ activeClaimId: null,
292
+ nextAttemptAt: p,
293
+ lastOutcome: "unconfirmed"
294
+ }, p), {
295
+ kind: "busy",
296
+ work: i
297
+ };
298
+ if (h.kind === "conflict") return {
299
+ kind: "busy",
300
+ work: i
301
+ };
302
+ if (h.kind !== "fresh") throw N("The retained invoice attempt has a different input binding.", !1);
303
+ return i = await c(e, t, i, {
304
+ attempt: a,
305
+ executions: i.executions + 1,
306
+ state: "working",
307
+ activeClaimId: h.claim.id,
308
+ nextAttemptAt: L(p, l),
309
+ lastOutcome: "unconfirmed"
310
+ }, p), {
311
+ kind: "claimed",
312
+ work: i,
313
+ attempt: Object.freeze({
314
+ work: i,
315
+ claim: h.claim
316
+ })
317
+ };
318
+ });
319
+ }),
320
+ observed: (t, r, i, a) => R(async () => {
321
+ let l = structuredClone(t), u = K(r), f = I(a), m = P(i.id), h = F(i.revision, 1);
322
+ return e.transactional(async (e) => {
323
+ let t = await n(e, l, u.work.referenceId), r = await p(e, t, u);
324
+ if (r === null) return null;
325
+ let i = await s.runPromise(o(e).get(V(t)));
326
+ if (i === null || i.id !== m || i.revision < h) throw N("The qualified native invoice observation is missing or older than the offered acknowledgement.");
327
+ let a = i.snapshot.status === "open";
328
+ return await d(E(e), u.claim, {
329
+ status: a ? 202 : 204,
330
+ body: null
331
+ }, f.getTime()) ? c(e, t, r, {
332
+ state: a ? "deferred" : "observed",
333
+ activeClaimId: null,
334
+ observationId: i.id,
335
+ observationRevision: i.revision,
336
+ lastOutcome: a ? "open" : "observed",
337
+ nextAttemptAt: a ? L(f, A) : f
338
+ }, f) : null;
339
+ });
340
+ }),
341
+ failed: (t, r, i) => R(async () => {
342
+ let a = structuredClone(t), o = K(r), s = I(i);
343
+ return e.transactional(async (e) => {
344
+ let t = await n(e, a, o.work.referenceId), r = await p(e, t, o);
345
+ return r === null || !await d(E(e), o.claim, {
346
+ status: 503,
347
+ body: null
348
+ }, s.getTime()) ? null : c(e, t, r, {
349
+ state: "retry",
350
+ activeClaimId: null,
351
+ lastOutcome: "unconfirmed",
352
+ nextAttemptAt: L(s, A)
353
+ }, s);
354
+ });
355
+ }),
356
+ hold: (e, n) => {
357
+ let r = K(e);
358
+ return f(t, r.claim, l, n);
359
+ }
360
+ };
361
+ };
362
+ export { D as BILLING_INVOICE_HISTORY_WORK_TABLE, O as billingInvoiceHistoryWorkTable, q as dataStoreInvoiceHistoryWorkStore };
@@ -0,0 +1,65 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { Schema as t } from "effect";
3
+ // src/invoiceSnapshot.ts
4
+ var n = (t, n = !1) => new e({
5
+ source: "invoiceSnapshot",
6
+ message: t,
7
+ transient: n
8
+ }), r = (e) => typeof e == "string" && e.length > 0 && e.trim() === e, i = (e) => e === null || r(e), a = (e) => e instanceof Date && Number.isSafeInteger(e.getTime()), o = (e) => Number.isSafeInteger(e) && e >= 0, s = t.Struct({
9
+ providerInvoiceId: t.String,
10
+ providerCustomerId: t.String,
11
+ providerSubscriptionId: t.NullOr(t.String),
12
+ providerRevisionId: t.NullOr(t.String),
13
+ status: t.Literals([
14
+ "open",
15
+ "paid",
16
+ "uncollectible",
17
+ "void"
18
+ ]),
19
+ livemode: t.Boolean,
20
+ currency: t.String,
21
+ amountDueMinor: t.Number,
22
+ amountPaidMinor: t.Number,
23
+ amountRemainingMinor: t.Number,
24
+ finalizedAt: t.DateFromString,
25
+ paidAt: t.NullOr(t.DateFromString),
26
+ observedAt: t.DateFromString,
27
+ lines: t.Array(t.Struct({
28
+ providerLineId: t.String,
29
+ providerSubscriptionId: t.NullOr(t.String),
30
+ providerSubscriptionItemId: t.NullOr(t.String),
31
+ providerPriceId: t.NullOr(t.String),
32
+ amountMinor: t.Number,
33
+ currency: t.String,
34
+ quantityDecimal: t.NullOr(t.String),
35
+ periodStart: t.DateFromString,
36
+ periodEnd: t.DateFromString,
37
+ proration: t.NullOr(t.Boolean)
38
+ }))
39
+ }), c = t.Struct({
40
+ protocol: t.Literal("voltro.invoice-observation.v1"),
41
+ snapshot: s
42
+ }), l = (e) => (d(e), t.encodeUnknownSync(c)({
43
+ protocol: "voltro.invoice-observation.v1",
44
+ snapshot: e
45
+ })), u = (e) => {
46
+ try {
47
+ let n = t.decodeUnknownSync(c)(e).snapshot;
48
+ return d(n), n;
49
+ } catch {
50
+ throw n("The stored complete invoice observation could not be confirmed.");
51
+ }
52
+ }, d = (e) => {
53
+ if (!r(e.providerInvoiceId) || !r(e.providerCustomerId) || !i(e.providerSubscriptionId) || !i(e.providerRevisionId) || ![
54
+ "open",
55
+ "paid",
56
+ "uncollectible",
57
+ "void"
58
+ ].includes(e.status) || typeof e.livemode != "boolean" || !/^[a-z]{3}$/.test(e.currency) || !o(e.amountDueMinor) || !o(e.amountPaidMinor) || !o(e.amountRemainingMinor) || !a(e.finalizedAt) || !a(e.observedAt) || e.paidAt !== null && (!a(e.paidAt) || e.paidAt < e.finalizedAt) || e.status === "paid" && e.paidAt === null || !Array.isArray(e.lines) || e.lines.length > 1e4) throw n("A complete finalized invoice observation is required.");
59
+ let t = /* @__PURE__ */ new Set();
60
+ for (let o of e.lines) {
61
+ if (!r(o.providerLineId) || t.has(o.providerLineId) || !i(o.providerSubscriptionId) || !i(o.providerSubscriptionItemId) || !i(o.providerPriceId) || !Number.isSafeInteger(o.amountMinor) || o.currency !== e.currency || o.quantityDecimal !== null && (typeof o.quantityDecimal != "string" || !/^\d+(?:\.\d{1,12})?$/.test(o.quantityDecimal)) || !a(o.periodStart) || !a(o.periodEnd) || o.periodEnd < o.periodStart || o.proration !== null && typeof o.proration != "boolean") throw n("Invoice lines must retain exact identities, money and periods.");
62
+ t.add(o.providerLineId);
63
+ }
64
+ };
65
+ export { d as assertInvoiceSnapshot, u as decodeInvoiceSnapshot, l as encodeInvoiceSnapshot, n as invoiceSnapshotError };