@voltro/plugin-billing 0.86.0 → 0.87.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.
@@ -0,0 +1,165 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { Effect as t } from "effect";
3
+ import { and as n, eq as r, id as i, integer as a, table as o, text as s, timestamp as c } from "@voltro/database";
4
+ //#region src/checkoutStore.ts
5
+ var l = "_voltro_billing_checkout_operations", u = o(l, {
6
+ id: i({ prefix: "bcheckout" }),
7
+ tenantId: s().maxLength(191),
8
+ provider: s().maxLength(64),
9
+ requestDigest: s().maxLength(64),
10
+ plan: s(),
11
+ quantity: a(),
12
+ providerCustomerId: s().nullable(),
13
+ providerSessionId: s().nullable(),
14
+ phase: s(),
15
+ revision: a().default(0),
16
+ active: a().nullable(),
17
+ slot: s().maxLength(191),
18
+ createdAt: c(),
19
+ expiresAt: c(),
20
+ updatedAt: c()
21
+ }).unique("byBillingCheckoutActiveTenant", ["tenantId", "slot"]).index("byBillingCheckoutActiveState", ["tenantId", "active"]).index("byBillingCheckoutHistory", ["tenantId", "createdAt"]), d = () => {
22
+ let e = /* @__PURE__ */ new Map(), n = (t) => [...e.values()].find((e) => e.tenantId === t && e.active === 1);
23
+ return {
24
+ getActive: (e) => t.try({
25
+ try: () => {
26
+ let t = n(e);
27
+ return t ? g({ ...t }) : null;
28
+ },
29
+ catch: p
30
+ }),
31
+ reserve: (r) => t.try({
32
+ try: () => {
33
+ let t = n(r.tenantId);
34
+ if (t) return g({ ...t });
35
+ let i = _(r);
36
+ if (e.has(i.id)) throw p();
37
+ return e.set(i.id, i), g({ ...i });
38
+ },
39
+ catch: p
40
+ }),
41
+ patch: (n, r) => t.try({
42
+ try: () => {
43
+ let t = e.get(n);
44
+ if (!t) throw p();
45
+ let i = v(t, r);
46
+ return e.set(t.id, i), g({ ...i });
47
+ },
48
+ catch: p
49
+ })
50
+ };
51
+ }, f = (e) => {
52
+ let t = async (t) => {
53
+ let i = await e.query(b(n(r("tenantId", t), r("active", 1))));
54
+ if (i.length > 1) throw p();
55
+ return i[0] ? g(i[0]) : null;
56
+ };
57
+ return {
58
+ getActive: (e) => y(() => t(e)),
59
+ reserve: (n) => y(async () => {
60
+ for (let r = 0; r < 8; r++) {
61
+ let r = await t(n.tenantId);
62
+ if (r) return r;
63
+ let i = _(n);
64
+ await e.insertIgnore(l, { ...i }, { conflictColumns: ["tenantId", "slot"] });
65
+ let a = await t(n.tenantId);
66
+ if (a) return a;
67
+ }
68
+ throw p();
69
+ }),
70
+ patch: (t, i) => y(async () => {
71
+ for (let a = 0; a < 32; a++) {
72
+ let a = await e.query(b(r("id", t)));
73
+ if (a.length !== 1 || !a[0]) throw p();
74
+ let o = g(a[0]), s = v(o, i);
75
+ if (s === o) return o;
76
+ if (await e.updateMany("_voltro_billing_checkout_operations", {
77
+ phase: s.phase,
78
+ active: s.active,
79
+ slot: s.slot,
80
+ revision: s.revision,
81
+ providerCustomerId: s.providerCustomerId,
82
+ providerSessionId: s.providerSessionId,
83
+ updatedAt: s.updatedAt
84
+ }, { where: n(r("id", t), r("revision", o.revision), r("active", 1)) }) === 1) return s;
85
+ }
86
+ throw p();
87
+ })
88
+ };
89
+ }, p = () => new e({
90
+ source: "checkoutStore",
91
+ message: "Checkout coordination could not be confirmed. Retry its current status before starting another request.",
92
+ transient: !0
93
+ }), m = [
94
+ "preparing",
95
+ "creating",
96
+ "ready",
97
+ "completed",
98
+ "expired",
99
+ "retired"
100
+ ], h = (e) => {
101
+ let t = e instanceof Date ? new Date(e) : new Date(typeof e == "string" || typeof e == "number" ? e : NaN);
102
+ if (!Number.isFinite(t.getTime())) throw p();
103
+ return t;
104
+ }, g = (e) => {
105
+ if ([
106
+ "id",
107
+ "tenantId",
108
+ "provider",
109
+ "requestDigest",
110
+ "plan"
111
+ ].some((t) => typeof e[t] != "string" || e[t] === "") || String(e.tenantId).length > 191 || String(e.id).length > 191 || String(e.provider).length > 64 || !/^[a-f0-9]{64}$/.test(String(e.requestDigest)) || !m.includes(String(e.phase)) || !Number.isSafeInteger(e.quantity) || Number(e.quantity) < 1 || !Number.isSafeInteger(e.revision) || Number(e.revision) < 0 || e.active !== null && e.active !== 1 || (e.active === 1 ? e.slot !== "active" : e.slot !== e.id) || (e.phase === "expired" || e.phase === "retired" ? e.active !== null : e.active !== 1) || e.providerCustomerId !== null && (typeof e.providerCustomerId != "string" || e.providerCustomerId === "") || e.providerSessionId !== null && (typeof e.providerSessionId != "string" || e.providerSessionId === "")) throw p();
112
+ return {
113
+ id: String(e.id),
114
+ tenantId: String(e.tenantId),
115
+ provider: String(e.provider),
116
+ requestDigest: String(e.requestDigest),
117
+ plan: String(e.plan),
118
+ quantity: Number(e.quantity),
119
+ providerCustomerId: e.providerCustomerId,
120
+ providerSessionId: e.providerSessionId,
121
+ phase: e.phase,
122
+ revision: Number(e.revision),
123
+ active: e.active,
124
+ slot: String(e.slot),
125
+ createdAt: h(e.createdAt),
126
+ expiresAt: h(e.expiresAt),
127
+ updatedAt: h(e.updatedAt)
128
+ };
129
+ }, _ = (e) => g({
130
+ ...e,
131
+ id: "bcheckout_" + globalThis.crypto.randomUUID(),
132
+ providerCustomerId: null,
133
+ providerSessionId: null,
134
+ phase: "preparing",
135
+ revision: 0,
136
+ active: 1,
137
+ slot: "active",
138
+ updatedAt: e.createdAt
139
+ }), v = (e, t) => {
140
+ if (t.phase !== void 0 && !m.includes(t.phase) || t.providerCustomerId !== void 0 && (typeof t.providerCustomerId != "string" || t.providerCustomerId.length === 0) || t.providerSessionId !== void 0 && (typeof t.providerSessionId != "string" || t.providerSessionId.length === 0) || t.providerCustomerId !== void 0 && e.providerCustomerId !== null && e.providerCustomerId !== t.providerCustomerId || t.providerSessionId !== void 0 && e.providerSessionId !== null && e.providerSessionId !== t.providerSessionId) throw p();
141
+ let n = (e) => e === "preparing" ? 0 : e === "creating" ? 1 : e === "ready" ? 2 : e === "completed" ? 3 : 4;
142
+ if (e.active === null || t.phase !== void 0 && n(t.phase) < n(e.phase)) return e;
143
+ let r = t.phase ?? e.phase, i = r === "retired" || r === "expired";
144
+ return g({
145
+ ...e,
146
+ ...t,
147
+ phase: r,
148
+ active: i ? null : 1,
149
+ slot: i ? e.id : "active",
150
+ revision: e.revision + 1,
151
+ updatedAt: /* @__PURE__ */ new Date()
152
+ });
153
+ }, y = (e) => t.tryPromise({
154
+ try: e,
155
+ catch: p
156
+ }), b = (e) => ({
157
+ table: l,
158
+ predicate: e,
159
+ order: [],
160
+ take: 2,
161
+ skip: void 0,
162
+ projection: void 0
163
+ });
164
+ //#endregion
165
+ export { l as BILLING_CHECKOUT_OPERATIONS_TABLE, u as billingCheckoutOperationsTable, f as dataStoreCheckoutOperationStore, d as memoryCheckoutOperationStore };
@@ -0,0 +1,125 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { Effect as t } from "effect";
3
+ import { and as n, eq as r, id as i, integer as a, table as o, text as s, timestamp as c } from "@voltro/database";
4
+ //#region src/closeStore.ts
5
+ var l = "_voltro_billing_close_operations", u = o(l, {
6
+ id: i({ prefix: "bclose" }),
7
+ tenantId: s().maxLength(191),
8
+ provider: s().maxLength(64),
9
+ scope: s().maxLength(64),
10
+ providerSubscriptionId: s().nullable(),
11
+ cancelAt: c().nullable(),
12
+ createdAt: c(),
13
+ usageSealed: a().default(0)
14
+ }).unique("byBillingCloseTenantScope", ["tenantId", "scope"]), d = () => new e({
15
+ source: "closeStore",
16
+ message: "Billing closure coordination requires reconciliation.",
17
+ transient: !0
18
+ }), f = (e) => {
19
+ let t = e instanceof Date ? new Date(e) : new Date(typeof e == "string" || typeof e == "number" ? e : NaN);
20
+ if (!Number.isFinite(t.getTime())) throw d();
21
+ return t;
22
+ }, p = (e) => {
23
+ for (let t of [
24
+ "id",
25
+ "tenantId",
26
+ "provider",
27
+ "scope"
28
+ ]) if (typeof e[t] != "string" || e[t] === "") throw d();
29
+ if (String(e.tenantId).length > 191 || String(e.provider).length > 64 || e.scope !== "account" && !/^[a-f0-9]{64}$/.test(String(e.scope))) throw d();
30
+ let t = e.scope === "account";
31
+ if (e.usageSealed !== 0 && e.usageSealed !== 1 || (t ? e.providerSubscriptionId !== null || e.cancelAt !== null : typeof e.providerSubscriptionId != "string" || e.providerSubscriptionId === "" || e.cancelAt === null)) throw d();
32
+ return {
33
+ id: String(e.id),
34
+ tenantId: String(e.tenantId),
35
+ provider: String(e.provider),
36
+ scope: String(e.scope),
37
+ providerSubscriptionId: t ? null : String(e.providerSubscriptionId),
38
+ cancelAt: t ? null : f(e.cancelAt),
39
+ createdAt: f(e.createdAt),
40
+ usageSealed: Number(e.usageSealed)
41
+ };
42
+ }, m = async (e) => {
43
+ if (e.length === 0) throw d();
44
+ let t = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
45
+ return [...new Uint8Array(t)].map((e) => e.toString(16).padStart(2, "0")).join("");
46
+ }, h = (e, n, r) => {
47
+ let i = async (t) => {
48
+ let r = p({
49
+ ...t,
50
+ id: "bclose_" + globalThis.crypto.randomUUID()
51
+ });
52
+ for (let i = 0; i < 8; i++) {
53
+ let i = await e(t.tenantId, t.scope);
54
+ if (i !== null) {
55
+ if (i.provider !== t.provider || i.providerSubscriptionId !== t.providerSubscriptionId) throw d();
56
+ return i;
57
+ }
58
+ await n(r);
59
+ }
60
+ throw d();
61
+ }, a = (e) => t.tryPromise({
62
+ try: e,
63
+ catch: d
64
+ });
65
+ return {
66
+ getAccount: (t) => a(() => e(t, "account")),
67
+ sealUsage: (t) => a(async () => {
68
+ let n = await e(t, "account");
69
+ if (n === null) throw d();
70
+ if (n.usageSealed === 1) return n;
71
+ await r(n);
72
+ let i = await e(t, "account");
73
+ if (i?.usageSealed !== 1) throw d();
74
+ return i;
75
+ }),
76
+ reserveAccount: (e, t, n) => a(() => i({
77
+ tenantId: e,
78
+ provider: t,
79
+ scope: "account",
80
+ providerSubscriptionId: null,
81
+ cancelAt: null,
82
+ createdAt: n,
83
+ usageSealed: 0
84
+ })),
85
+ getSubscription: (t, n) => a(async () => e(t, await m(n))),
86
+ reserveSubscription: (e) => a(async () => i({
87
+ ...e,
88
+ scope: await m(e.providerSubscriptionId),
89
+ usageSealed: 0
90
+ }))
91
+ };
92
+ }, g = () => {
93
+ let e = /* @__PURE__ */ new Map(), t = (e, t) => JSON.stringify([e, t]);
94
+ return h(async (n, r) => {
95
+ let i = e.get(t(n, r));
96
+ return i ? p({ ...i }) : null;
97
+ }, async (n) => {
98
+ let r = t(n.tenantId, n.scope);
99
+ e.has(r) || e.set(r, p({ ...n }));
100
+ }, async (n) => {
101
+ let r = t(n.tenantId, "account"), i = e.get(r);
102
+ if (!i || i.id !== n.id) throw d();
103
+ e.set(r, p({
104
+ ...i,
105
+ usageSealed: 1
106
+ }));
107
+ });
108
+ }, _ = (e) => h(async (t, i) => {
109
+ let a = {
110
+ table: l,
111
+ predicate: n(r("tenantId", t), r("scope", i)),
112
+ order: [],
113
+ take: 2,
114
+ skip: void 0,
115
+ projection: void 0
116
+ }, o = await e.query(a);
117
+ if (o.length > 1) throw d();
118
+ return o[0] ? p(o[0]) : null;
119
+ }, async (t) => {
120
+ await e.insertIgnore(l, { ...t }, { conflictColumns: ["tenantId", "scope"] });
121
+ }, async (t) => {
122
+ await e.updateMany(l, { usageSealed: 1 }, { where: n(r("id", t.id), r("tenantId", t.tenantId), r("scope", "account"), r("usageSealed", 0)) });
123
+ });
124
+ //#endregion
125
+ export { l as BILLING_CLOSE_OPERATIONS_TABLE, u as billingCloseOperationsTable, _ as dataStoreBillingCloseStore, g as memoryBillingCloseStore };
@@ -0,0 +1,155 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { exactProviderIdentifier as t } from "./providerIdentity.js";
3
+ import { Effect as n } from "effect";
4
+ import { canonicalJson as r } from "@voltro/protocol";
5
+ import { and as i, eq as a, gt as o } from "@voltro/database";
6
+ import { createHash as s } from "node:crypto";
7
+ //#region src/custodyInventory.ts
8
+ var c = Object.freeze([
9
+ "_voltro_billing_customer_accounts",
10
+ "_voltro_billing_customer_custody",
11
+ "_voltro_billing_account_invoices",
12
+ "_voltro_billing_invoice_history_walks",
13
+ "_voltro_billing_invoice_history_pages",
14
+ "_voltro_billing_invoice_history_references",
15
+ "_voltro_billing_invoice_history_work",
16
+ "_voltro_billing_invoice_history_dispatch",
17
+ "_voltro_billing_customers",
18
+ "_voltro_billing_subscriptions",
19
+ "_voltro_billing_invoices",
20
+ "_voltro_billing_invoice_observations",
21
+ "_voltro_billing_usage",
22
+ "_voltro_billing_usage_events",
23
+ "_voltro_billing_checkout_operations",
24
+ "_voltro_billing_close_operations",
25
+ "_voltro_billing_dunning_notices"
26
+ ]), l = (r, i) => n.tryPromise({
27
+ try: async () => {
28
+ let e = i.tenantId, n = i.pageSize ?? 200, a = i.maxRowsPerTable ?? 5e3;
29
+ if (!t(e) || e.length > 191 || !e.isWellFormed() || !Number.isSafeInteger(n) || n < 1 || n > 500 || !Number.isSafeInteger(a) || a < 1 || a > 5e4) throw v("Inventory tenant and bounds must be valid.", !1);
30
+ if (r.integrityQuery === void 0) throw v("Complete native integrity reads are required.", !1);
31
+ let o = {
32
+ format: "voltro.billing-custody-inventory.v1",
33
+ tenantId: e,
34
+ tables: await r.transactional(async (t) => {
35
+ if (t.integrityQuery === void 0) throw v("Transactional native integrity reads are required.", !1);
36
+ let r = await u(t, e, n, a), i = await u(t, e, n, a);
37
+ if (g(r) !== g(i)) throw v("Billing state changed during inventory. Quiesce writers and retry.", !0);
38
+ return i;
39
+ })
40
+ };
41
+ return Object.freeze({
42
+ ...o,
43
+ digest: g(o),
44
+ observedAt: /* @__PURE__ */ new Date(),
45
+ purpose: "review-only"
46
+ });
47
+ },
48
+ catch: (t) => t instanceof e ? t : v("The native billing inventory could not be completed.", !0)
49
+ }), u = async (e, n, s, l) => {
50
+ let u = e.integrityQuery;
51
+ if (u === void 0) throw v("Complete native integrity reads are required.", !1);
52
+ let y = [], b = 0;
53
+ for (let x of c) {
54
+ let c = [], S = /* @__PURE__ */ new Set(), C;
55
+ for (;;) {
56
+ let g = Math.min(s, l - c.length + 1), y = {
57
+ table: x,
58
+ predicate: C === void 0 ? a("tenantId", n) : i(a("tenantId", n), o("id", C)),
59
+ crossTenant: !0,
60
+ includeDeleted: !0,
61
+ includeExpired: !0,
62
+ order: [{
63
+ column: "id",
64
+ direction: "asc"
65
+ }],
66
+ take: g,
67
+ skip: void 0,
68
+ projection: void 0
69
+ }, w = await u.call(e, y);
70
+ if (!Array.isArray(w) || w.length > g) throw v(`Invalid inventory page for ${x}.`, !1);
71
+ for (let e of w) {
72
+ let i = h(e);
73
+ if (!m(i)) throw v(`Invalid inventory row for ${x}.`, !1);
74
+ let a = i.id;
75
+ if (!t(a) || i.tenantId !== n || S.has(a)) throw v(`Invalid inventory identity for ${x}.`, !1);
76
+ if (c.length === l) throw v(`Inventory bound exceeded for ${x}; no partial inventory is returned.`, !1);
77
+ S.add(a);
78
+ let o = {};
79
+ for (let e of d) {
80
+ let n = i[e];
81
+ if (n != null) {
82
+ if (!t(n)) throw v(`Invalid inventory reference for ${x}.`, !1);
83
+ o[e] = n;
84
+ }
85
+ }
86
+ let s = r(i), u = Buffer.byteLength(s, "utf8");
87
+ if (b += u, u > f || b > p) throw v("Native billing inventory byte bound exceeded; no partial inventory is returned.", !1);
88
+ c.push(Object.freeze({
89
+ id: a,
90
+ digest: _(s),
91
+ references: Object.freeze(o)
92
+ })), C = a;
93
+ }
94
+ if (w.length < g) break;
95
+ }
96
+ c.sort((e, t) => e.id < t.id ? -1 : +(e.id > t.id)), y.push(Object.freeze({
97
+ tableName: x,
98
+ rowCount: c.length,
99
+ digest: g(c),
100
+ rows: Object.freeze(c)
101
+ }));
102
+ }
103
+ return Object.freeze(y);
104
+ }, d = [
105
+ "provider",
106
+ "providerAccountId",
107
+ "providerCustomerId",
108
+ "providerSubscriptionId",
109
+ "providerInvoiceId"
110
+ ], f = 4194304, p = 67108864, m = (e) => typeof e == "object" && !!e && !Array.isArray(e), h = (e, t = /* @__PURE__ */ new Set(), n = 0) => {
111
+ if (n > 64) throw v("Native inventory value exceeds supported depth.", !1);
112
+ if (e === null || typeof e == "boolean") return e;
113
+ if (typeof e == "string") {
114
+ if (!e.isWellFormed()) throw v("Native inventory contains malformed text.", !1);
115
+ return e;
116
+ }
117
+ if (typeof e == "number") {
118
+ if (!Number.isFinite(e) || !Number.isSafeInteger(e) && Math.abs(e) > 2 ** 53 - 1 || Object.is(e, -0)) throw v("Native inventory numeric evidence cannot be encoded exactly.", !1);
119
+ return e;
120
+ }
121
+ if (e instanceof Date) {
122
+ if (!Number.isFinite(e.getTime())) throw v("Native inventory contains an invalid date.", !1);
123
+ return e.toISOString();
124
+ }
125
+ if (typeof e != "object" || t.has(e)) throw v("Native inventory contains a non-JSON value.", !1);
126
+ let r = Object.getPrototypeOf(e);
127
+ if (!Array.isArray(e) && r !== Object.prototype && r !== null) throw v("Native inventory value has an unsupported prototype.", !1);
128
+ if (Object.getOwnPropertySymbols(e).length !== 0) throw v("Native inventory contains unsupported symbol fields.", !1);
129
+ t.add(e);
130
+ try {
131
+ if (Array.isArray(e)) {
132
+ if (Object.keys(e).length !== e.length || Object.getOwnPropertyNames(e).length !== e.length + 1) throw v("Native inventory contains a sparse or extended array.", !1);
133
+ return Array.from({ length: e.length }, (r, i) => {
134
+ let a = Object.getOwnPropertyDescriptor(e, String(i));
135
+ if (a === void 0 || !("value" in a)) throw v("Native inventory contains a sparse or accessor array.", !1);
136
+ return h(a.value, t, n + 1);
137
+ });
138
+ }
139
+ if (Object.getOwnPropertyNames(e).length !== Object.keys(e).length) throw v("Native inventory contains non-enumerable fields.", !1);
140
+ return Object.fromEntries(Object.keys(e).map((r) => {
141
+ if (!r.isWellFormed()) throw v("Native inventory contains a malformed field.", !1);
142
+ let i = Object.getOwnPropertyDescriptor(e, r);
143
+ if (i === void 0 || !("value" in i)) throw v("Native inventory contains an accessor field.", !1);
144
+ return [r, h(i.value, t, n + 1)];
145
+ }));
146
+ } finally {
147
+ t.delete(e);
148
+ }
149
+ }, g = (e) => _(r(e)), _ = (e) => s("sha256").update(e, "utf8").digest("hex"), v = (t, n) => new e({
150
+ source: "billingCustodyInventory",
151
+ message: t,
152
+ transient: n
153
+ });
154
+ //#endregion
155
+ export { c as BILLING_CUSTODY_INVENTORY_TABLES, l as inspectBillingCustody };