@voltro/plugin-economy 0.0.0-stage → 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.
Files changed (177) hide show
  1. package/CHANGELOG.md +24329 -0
  2. package/LICENSE +61 -0
  3. package/README.md +2719 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9189 -0
  6. package/dist/amount.js +21 -0
  7. package/dist/appleIap.server.js +263 -0
  8. package/dist/appleIapNotifications.server.js +163 -0
  9. package/dist/appleIapTrust.server.js +31 -0
  10. package/dist/bundleValue.server.js +113 -0
  11. package/dist/canonicalDataJson.js +11 -0
  12. package/dist/capture.js +54 -0
  13. package/dist/codePort.fixture.js +124 -0
  14. package/dist/codeStore.server.js +440 -0
  15. package/dist/codeTables.entity.js +44 -0
  16. package/dist/codes.d.ts +150 -0
  17. package/dist/codes.js +83 -0
  18. package/dist/components.d.ts +397 -0
  19. package/dist/components.js +284 -0
  20. package/dist/consistency.server.js +116 -0
  21. package/dist/containerStore.server.js +688 -0
  22. package/dist/containerStore.testFixtures.js +76 -0
  23. package/dist/containerTables.entity.js +46 -0
  24. package/dist/containers.d.ts +311 -0
  25. package/dist/containers.fixture.js +129 -0
  26. package/dist/containers.js +215 -0
  27. package/dist/containers.server.js +40 -0
  28. package/dist/containersPort.fixture.js +75 -0
  29. package/dist/currency.js +33 -0
  30. package/dist/declarationFixtures.js +62 -0
  31. package/dist/economy-app/events/economy.refundReversed.event.ts +1 -0
  32. package/dist/economy-app/events/economy.refunded.event.ts +1 -0
  33. package/dist/economyPort.fixture.js +125 -0
  34. package/dist/errors.d.ts +14 -0
  35. package/dist/errors.js +22 -0
  36. package/dist/events.d.ts +46 -0
  37. package/dist/events.js +1 -0
  38. package/dist/googleIap.server.js +392 -0
  39. package/dist/googleIapNotifications.server.js +131 -0
  40. package/dist/googleIapSubscription.server.js +184 -0
  41. package/dist/iap.d.ts +309 -0
  42. package/dist/iap.js +304 -0
  43. package/dist/iap.testFixtures.js +145 -0
  44. package/dist/iapAccountTables.entity.js +40 -0
  45. package/dist/iapAccounts.server.js +201 -0
  46. package/dist/iapBinding.server.js +524 -0
  47. package/dist/iapCheckout.server.js +98 -0
  48. package/dist/iapCompletion.server.js +56 -0
  49. package/dist/iapCurrentState.server.js +108 -0
  50. package/dist/iapCurrentState.testFixtures.js +23 -0
  51. package/dist/iapCurrentStatePort.fixture.js +64 -0
  52. package/dist/iapEntitlements.d.ts +79 -0
  53. package/dist/iapEntitlements.js +58 -0
  54. package/dist/iapEntitlements.server.js +49 -0
  55. package/dist/iapEntitlementsBinding.server.js +106 -0
  56. package/dist/iapEntitlementsPort.fixture.js +81 -0
  57. package/dist/iapInstalledEntitlements.testFixtures.js +76 -0
  58. package/dist/iapInstalledNotifications.testFixtures.js +94 -0
  59. package/dist/iapInventoryRefunds.server.js +262 -0
  60. package/dist/iapNotificationCustodyPort.fixture.js +68 -0
  61. package/dist/iapNotificationPort.fixture.js +74 -0
  62. package/dist/iapNotificationProcessor.server.js +135 -0
  63. package/dist/iapNotificationProcessor.testFixtures.js +64 -0
  64. package/dist/iapNotificationProcessorPort.fixture.js +56 -0
  65. package/dist/iapNotificationSchedule.server.js +70 -0
  66. package/dist/iapNotificationStore.server.js +353 -0
  67. package/dist/iapNotificationStore.testFixtures.js +32 -0
  68. package/dist/iapNotificationTables.entity.js +49 -0
  69. package/dist/iapNotificationTarget.server.js +37 -0
  70. package/dist/iapNotificationTarget.testFixtures.js +30 -0
  71. package/dist/iapNotifications.d.ts +644 -0
  72. package/dist/iapNotifications.js +6 -0
  73. package/dist/iapNotifications.server.js +96 -0
  74. package/dist/iapNotificationsBinding.server.js +233 -0
  75. package/dist/iapNotificationsSystemPort.fixture.js +51 -0
  76. package/dist/iapPlugin.testFixtures.js +60 -0
  77. package/dist/iapPort.fixture.js +75 -0
  78. package/dist/iapProviderCapture.server.js +18 -0
  79. package/dist/iapRecovery.d.ts +146 -0
  80. package/dist/iapRecovery.js +90 -0
  81. package/dist/iapRefundEventDelivery.server.js +83 -0
  82. package/dist/iapRefundEvents.js +45 -0
  83. package/dist/iapRefundEventsPort.fixture.js +60 -0
  84. package/dist/iapRefundPort.fixture.js +55 -0
  85. package/dist/iapRefundTables.entity.js +56 -0
  86. package/dist/iapRefunds.d.ts +755 -0
  87. package/dist/iapRefunds.js +6 -0
  88. package/dist/iapRefunds.server.js +554 -0
  89. package/dist/iapRefunds.testFixtures.js +65 -0
  90. package/dist/iapSpendAdmission.server.js +99 -0
  91. package/dist/iapStore.server.js +741 -0
  92. package/dist/iapStore.testFixtures.js +128 -0
  93. package/dist/iapTables.entity.js +81 -0
  94. package/dist/iapVerification.server.js +121 -0
  95. package/dist/index.d.ts +2984 -0
  96. package/dist/index.js +15 -0
  97. package/dist/intentionRecovery.js +134 -0
  98. package/dist/ledger.js +78 -0
  99. package/dist/loyalty.d.ts +274 -0
  100. package/dist/loyalty.fixture.js +93 -0
  101. package/dist/loyalty.js +312 -0
  102. package/dist/loyaltyBinding.server.js +204 -0
  103. package/dist/loyaltyPort.fixture.js +87 -0
  104. package/dist/loyaltyShopSource.server.js +91 -0
  105. package/dist/loyaltyStore.server.js +553 -0
  106. package/dist/loyaltyStore.testFixtures.js +83 -0
  107. package/dist/loyaltyTables.entity.js +50 -0
  108. package/dist/offerMerchandising.fixture.js +115 -0
  109. package/dist/offerMerchandising.js +283 -0
  110. package/dist/offerPolicies.d.ts +441 -0
  111. package/dist/offerPolicies.fixture.js +54 -0
  112. package/dist/offerPolicies.js +268 -0
  113. package/dist/piggyBanks.d.ts +272 -0
  114. package/dist/piggyBanks.fixture.js +71 -0
  115. package/dist/piggyBanks.js +201 -0
  116. package/dist/piggyBinding.server.js +205 -0
  117. package/dist/piggyPort.fixture.js +73 -0
  118. package/dist/piggyStore.server.js +611 -0
  119. package/dist/piggyStore.testFixtures.js +98 -0
  120. package/dist/piggyTables.entity.js +41 -0
  121. package/dist/plugin.js +1048 -0
  122. package/dist/port.js +13 -0
  123. package/dist/price.d.ts +99 -0
  124. package/dist/price.js +144 -0
  125. package/dist/purchaseRecovery.js +56 -0
  126. package/dist/reports.server.js +115 -0
  127. package/dist/rpc.d.ts +3201 -0
  128. package/dist/rpc.js +1201 -0
  129. package/dist/sanctionsTradeAuthority.server.js +80 -0
  130. package/dist/server.d.ts +3856 -0
  131. package/dist/server.js +43 -0
  132. package/dist/shop.d.ts +279 -0
  133. package/dist/shop.js +196 -0
  134. package/dist/shop.testFixtures.js +114 -0
  135. package/dist/shopPort.fixture.js +72 -0
  136. package/dist/shopPresentation.js +68 -0
  137. package/dist/shopStore.server.js +624 -0
  138. package/dist/shopTables.entity.js +63 -0
  139. package/dist/shopUiCopy.js +57 -0
  140. package/dist/spendLimits.d.ts +140 -0
  141. package/dist/spendLimits.fixture.js +53 -0
  142. package/dist/spendLimits.js +155 -0
  143. package/dist/spendLimitsPort.fixture.js +120 -0
  144. package/dist/subscriptionBinding.server.js +211 -0
  145. package/dist/subscriptionStore.server.js +643 -0
  146. package/dist/subscriptionStore.testFixtures.js +72 -0
  147. package/dist/subscriptionTables.entity.js +37 -0
  148. package/dist/subscriptions.d.ts +394 -0
  149. package/dist/subscriptions.fixture.js +97 -0
  150. package/dist/subscriptions.js +213 -0
  151. package/dist/subscriptionsPort.fixture.js +76 -0
  152. package/dist/systemFixtures.js +89 -0
  153. package/dist/tables.entity.js +83 -0
  154. package/dist/testing.d.ts +4615 -0
  155. package/dist/testing.js +29 -0
  156. package/dist/tradeAuthority.server.js +172 -0
  157. package/dist/tradeBinding.server.js +201 -0
  158. package/dist/tradeComponents.js +342 -0
  159. package/dist/tradeEscrow.server.js +232 -0
  160. package/dist/tradeEscrow.testFixtures.js +133 -0
  161. package/dist/tradeExpiry.server.js +43 -0
  162. package/dist/tradeRecovery.js +48 -0
  163. package/dist/tradeStore.server.js +750 -0
  164. package/dist/tradeStore.testFixtures.js +73 -0
  165. package/dist/tradeTables.entity.js +66 -0
  166. package/dist/tradeUiCopy.js +79 -0
  167. package/dist/trades.d.ts +246 -0
  168. package/dist/trades.fixture.js +103 -0
  169. package/dist/trades.js +289 -0
  170. package/dist/tradesPort.fixture.js +94 -0
  171. package/dist/walletEscrow.js +106 -0
  172. package/dist/walletEscrow.server.js +376 -0
  173. package/dist/walletKeys.server.js +5 -0
  174. package/dist/walletStore.js +571 -0
  175. package/dist/web.d.ts +2257 -0
  176. package/dist/web.js +9 -0
  177. package/package.json +161 -4
@@ -0,0 +1,96 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { captureBoundMethods as t, dataObject as n, identifier as r } from "./capture.js";
3
+ import { iapEnvironment as i, iapTimestamp as a } from "./iap.js";
4
+ import { canonicalDataJson as o } from "./canonicalDataJson.js";
5
+ import { Context as s, Effect as c } from "effect";
6
+ import { Buffer as l } from "node:buffer";
7
+ import { createHash as u } from "node:crypto";
8
+ //#region src/iapNotifications.server.ts
9
+ var d = (e) => e === "ios" || e === "android" || e === "web", f = class extends s.Service()("@voltro/IapNotificationPort") {}, p = (e) => t(e, ["initialize", "verify"]), m = /* @__PURE__ */ new WeakMap(), h = (t) => {
10
+ let o = n(t, [
11
+ "channel",
12
+ "applicationId",
13
+ "environment",
14
+ "noticeId",
15
+ "sourceType",
16
+ "kind",
17
+ "occurredAt",
18
+ "transactionId",
19
+ "originalTransactionId",
20
+ "storeProductId"
21
+ ], "invalid_record");
22
+ if (!d(o.channel) || ![
23
+ "one-time",
24
+ "subscription",
25
+ "voided",
26
+ "review",
27
+ "test"
28
+ ].includes(String(o.kind))) throw e("invalid_record");
29
+ let s = (t) => {
30
+ if (typeof t != "string" || t.length === 0 || t.length > 128 || /[\u0000-\u0020\u007f]/.test(t)) throw e("invalid_record");
31
+ return t;
32
+ };
33
+ return Object.freeze({
34
+ channel: o.channel,
35
+ applicationId: r(o.applicationId),
36
+ environment: o.environment === null ? null : i(o.environment),
37
+ noticeId: s(o.noticeId),
38
+ sourceType: s(o.sourceType),
39
+ kind: o.kind,
40
+ occurredAt: a(o.occurredAt),
41
+ transactionId: o.transactionId === null ? null : s(o.transactionId),
42
+ originalTransactionId: o.originalTransactionId === null ? null : s(o.originalTransactionId),
43
+ storeProductId: o.storeProductId === null ? null : r(o.storeProductId)
44
+ });
45
+ }, g = (e, t, n, r) => {
46
+ let i = h(e), a = structuredClone(t), s = Object.freeze({
47
+ noticeId: i.noticeId,
48
+ channel: i.channel
49
+ });
50
+ return m.set(s, Object.freeze({
51
+ notice: i,
52
+ fingerprint: u("sha256").update(o([i, a])).digest("hex"),
53
+ material: a,
54
+ ...n === void 0 ? {} : { validate: n },
55
+ ...r === void 0 ? {} : { prepare: r }
56
+ })), s;
57
+ }, _ = (t) => {
58
+ let n = typeof t == "object" && t ? m.get(t) : void 0;
59
+ if (!n) throw e("forbidden");
60
+ return Object.freeze({
61
+ ...n,
62
+ material: structuredClone(n.material)
63
+ });
64
+ }, v = (t) => {
65
+ let n = typeof t == "object" && t ? m.get(t) : void 0;
66
+ return n ? n.validate === void 0 ? c.void : c.suspend(n.validate).pipe(c.catchDefect(() => c.fail(e("unavailable")))) : c.fail(e("forbidden"));
67
+ }, y = (t) => {
68
+ let n = typeof t == "object" && t ? m.get(t) : void 0;
69
+ return n ? n.validate === void 0 ? c.succeed([]) : n.prepare === void 0 ? c.fail(e("unavailable")) : c.suspend(n.prepare).pipe(c.map((e) => Object.freeze([e])), c.catchDefect(() => c.fail(e("unavailable")))) : c.fail(e("forbidden"));
70
+ }, b = (e) => _(e).notice, x = (t, r) => {
71
+ let i = n(t, [
72
+ "body",
73
+ "authorization",
74
+ "headers"
75
+ ]);
76
+ if (typeof i.body != "string" || i.body.length === 0 || i.body.length > r || l.byteLength(i.body, "utf8") > r) throw e("limit_exceeded");
77
+ let a = i.authorization;
78
+ if (a !== void 0 && (typeof a != "string" || a.length === 0 || a.length > 16384 || /[\u0000-\u001f\u007f]/.test(a))) throw e("invalid_input");
79
+ let o;
80
+ if (i.headers !== void 0) {
81
+ let e = Object.entries(n(i.headers, Object.keys(i.headers)));
82
+ o = {};
83
+ for (let [t, n] of e.slice(0, 64)) typeof n != "string" || !/^[a-z0-9-]{1,64}$/.test(t) || n.length > 8192 || /[\u0000-\u001f\u007f]/.test(n) || (o[t] = n);
84
+ }
85
+ return Object.freeze({
86
+ body: i.body,
87
+ ...a === void 0 ? {} : { authorization: a },
88
+ ...o === void 0 ? {} : { headers: Object.freeze(o) }
89
+ });
90
+ }, S = (t, n) => {
91
+ let r = a(t);
92
+ if (r > n + 6e4) throw e("invalid_record");
93
+ return r;
94
+ };
95
+ //#endregion
96
+ export { f as IapNotificationPort, h as captureIapNotificationNotice, p as captureIapNotificationService, x as captureNotificationInput, b as iapNotificationNotice, d as isIapRefundChannel, g as issueVerifiedIapNotification, S as notificationTime, y as prepareVerifiedIapNotification, _ as readVerifiedIapNotification, v as validateVerifiedIapNotification };
@@ -0,0 +1,233 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { captureBoundMethods as t, dataObject as n, identifier as r } from "./capture.js";
3
+ import { shopInteger as i } from "./shop.js";
4
+ import { iapChannel as a } from "./iap.js";
5
+ import { IapRefundEventsPort as o } from "./iapRefundEvents.js";
6
+ import { isIapRefundChannel as s } from "./iapNotifications.server.js";
7
+ import { iapNotificationTables as c } from "./iapNotificationTables.entity.js";
8
+ import { iapRefundTables as l } from "./iapRefundTables.entity.js";
9
+ import { dataStoreIapRefunds as u } from "./iapRefunds.server.js";
10
+ import { dataStoreIapNotificationCustody as d } from "./iapNotificationStore.server.js";
11
+ import { captureIapNotificationProcessorService as f, iapNotificationHttpRoute as p, iapNotificationProcessor as m } from "./iapNotificationProcessor.server.js";
12
+ import { Context as h, Effect as g, Layer as _ } from "effect";
13
+ //#region src/iapNotificationsBinding.server.ts
14
+ var v = class extends h.Service()("@voltro/IapNotificationsSystemPort") {}, y = (e) => t(e, [
15
+ "initialize",
16
+ "processor",
17
+ "exportSubject",
18
+ "eraseSubject"
19
+ ]), b = () => g.fail(e("unavailable")), x = Object.freeze({
20
+ initialize: b,
21
+ processor: b,
22
+ exportSubject: b,
23
+ eraseSubject: b
24
+ }), S = (h, x) => {
25
+ if (h === void 0) return;
26
+ let S = n(h, [
27
+ "version",
28
+ "pathPrefix",
29
+ "maxBodyBytes",
30
+ "bodyRetentionMs",
31
+ "debtPolicy",
32
+ "assets",
33
+ "assetsVersion",
34
+ "maxNoticeRows",
35
+ "maxRefundRows",
36
+ "cipher",
37
+ "service",
38
+ "refundEvents",
39
+ "refundDisposition",
40
+ "onChange"
41
+ ]), C = r(S.version);
42
+ if (C === "default.v1") throw e("invalid_input");
43
+ let w = S.service === void 0 ? void 0 : y(S.service);
44
+ if (x.replacedIap && !w || w && [
45
+ "bodyRetentionMs",
46
+ "debtPolicy",
47
+ "assets",
48
+ "assetsVersion",
49
+ "maxNoticeRows",
50
+ "maxRefundRows",
51
+ "cipher",
52
+ "onChange"
53
+ ].some((e) => S[e] !== void 0) || S.onChange !== void 0 && typeof S.onChange != "function") throw e("invalid_input");
54
+ let T = S.onChange;
55
+ if (!w && (S.refundEvents !== void 0 || S.refundDisposition !== void 0) || x.replacedIap && S.refundDisposition !== void 0) throw e("invalid_input");
56
+ let E = S.refundEvents === void 0 ? void 0 : t(S.refundEvents, [
57
+ "pending",
58
+ "read",
59
+ "acknowledge"
60
+ ]), D = S.refundDisposition === void 0 ? void 0 : t(S.refundDisposition, ["read"]), O = S.pathPrefix ?? "/_voltro/economy/iap";
61
+ if (typeof O != "string" || !/^\/(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_-]+$/.test(O) || O.length > 210) throw e("invalid_input");
62
+ let k = i(S.maxBodyBytes ?? 262144, 1024, 1048576), A = Object.freeze([...new Set(x.channels.map(a))]);
63
+ if (!A.length || !w && (A.some((e) => !s(e)) || x.declarations.some((e) => !e.createNotifications || !e.createCurrentState))) throw e("invalid_input");
64
+ let j = w ? null : i(S.bodyRetentionMs, 1, 31536e6), M = w ? null : i(S.maxNoticeRows ?? 1e4, 1, 1e5), N = w ? null : i(S.maxRefundRows ?? 1e4, 1, 1e5), P = w ? null : S.debtPolicy;
65
+ if (!w && P !== "allowNegative" && P !== "blockSpend") throw e("invalid_input");
66
+ let F = S.assets === void 0 ? void 0 : t(S.assets, [
67
+ "prepare",
68
+ "validate",
69
+ "audit"
70
+ ]), I = F ? r(S.assetsVersion) : null;
71
+ if (I === "default.v1" || !w && x.itemOrders && !F || !F && S.assetsVersion !== void 0) throw e("invalid_input");
72
+ let L = S.cipher === void 0 ? void 0 : t(S.cipher, ["read"]);
73
+ for (let e of A) r(x.namespace + ".notice." + e);
74
+ let R, z = 0, B = /* @__PURE__ */ new Map(), V, H = () => R !== void 0 && x.isActive(), U = (t) => g.try({
75
+ try: () => a(t),
76
+ catch: () => e("invalid_input")
77
+ }).pipe(g.flatMap((e) => g.suspend(() => {
78
+ if (!H() || !A.includes(e)) return b();
79
+ let t = z;
80
+ return (w ? w.processor(e) : B.has(e) ? g.succeed(B.get(e)) : b()).pipe(g.map((e) => {
81
+ let n = f(e), r = (e) => g.suspend(() => H() && z === t ? g.suspend(e).pipe(g.flatMap((e) => H() && z === t ? g.succeed(e) : b())) : b());
82
+ return Object.freeze({
83
+ initialize: () => r(n.initialize),
84
+ ingest: (e) => r(() => n.ingest(e)),
85
+ process: (e) => r(() => n.process(e)),
86
+ pending: () => r(n.pending),
87
+ prune: () => r(n.prune),
88
+ exportSubject: (e) => r(() => n.exportSubject(e)),
89
+ eraseSubject: (e) => r(() => n.eraseSubject(e))
90
+ });
91
+ }), g.catchDefect(() => b()));
92
+ }))), W = (e) => g.suspend(() => {
93
+ let t = z;
94
+ return H() ? g.suspend(e).pipe(g.flatMap((e) => H() && z === t ? g.succeed(e) : b()), g.catchDefect(() => b())) : b();
95
+ }), G = () => g.suspend(() => {
96
+ let e = z;
97
+ return R ? (w ? w.initialize() : g.gen(function* () {
98
+ for (let e of B.values()) yield* e.initialize();
99
+ })).pipe(g.flatMap(() => z === e && R ? g.void : b()), g.catchDefect(() => b())) : b();
100
+ }), K = Object.freeze({
101
+ initialize: G,
102
+ processor: U,
103
+ exportSubject: (e) => W(() => w ? w.exportSubject(structuredClone(e)) : g.gen(function* () {
104
+ let t = [];
105
+ for (let [n, r] of B) t.push({
106
+ channel: n,
107
+ data: yield* r.exportSubject(e)
108
+ });
109
+ return { deployments: t };
110
+ })),
111
+ eraseSubject: (e) => W(() => w ? w.eraseSubject(structuredClone(e)) : g.gen(function* () {
112
+ for (let t of B.values()) yield* t.eraseSubject(e);
113
+ }))
114
+ }), q = A.map((e) => {
115
+ let t = Object.freeze({
116
+ initialize: () => U(e).pipe(g.flatMap((e) => e.initialize())),
117
+ ingest: (t) => U(e).pipe(g.flatMap((e) => e.ingest(t))),
118
+ process: (t) => U(e).pipe(g.flatMap((e) => e.process(t))),
119
+ pending: () => U(e).pipe(g.flatMap((e) => e.pending())),
120
+ prune: () => U(e).pipe(g.flatMap((e) => e.prune())),
121
+ exportSubject: (t) => U(e).pipe(g.flatMap((e) => e.exportSubject(t))),
122
+ eraseSubject: (t) => U(e).pipe(g.flatMap((e) => e.eraseSubject(t)))
123
+ }), n = x.deliver;
124
+ return p({
125
+ path: O + "/" + e,
126
+ maxBodyBytes: k,
127
+ processor: t,
128
+ ...n ? { deliver: (t) => n(e, t) } : {}
129
+ });
130
+ }), J = (t, n, r) => {
131
+ if (R && R !== t) throw Error("IAP notification system cannot cross app stores");
132
+ if (R = t, z++, B = /* @__PURE__ */ new Map(), V = void 0, !w) {
133
+ if (!n) throw e("unavailable");
134
+ V = u(t, {
135
+ realMoney: !0,
136
+ namespace: x.namespace,
137
+ debtPolicy: P,
138
+ orders: n,
139
+ economy: x.economy,
140
+ assets: F,
141
+ assetsVersion: I ?? void 0,
142
+ maxHistoryRows: N
143
+ });
144
+ for (let { declaration: i, context: a } of r) {
145
+ if (!s(i.channel) || !i.createNotifications || !i.createCurrentState) throw e("invalid_input");
146
+ let r = {
147
+ realMoney: !0,
148
+ channel: i.channel,
149
+ environment: i.environment,
150
+ applicationId: i.applicationId
151
+ }, o = d(t, {
152
+ namespace: x.namespace + ".notice." + i.channel,
153
+ deployment: r,
154
+ orders: n,
155
+ bodyRetentionMs: j,
156
+ maxRows: M,
157
+ maxBodyBytes: k,
158
+ now: a.now,
159
+ cipher: L
160
+ });
161
+ B.set(i.channel, m({
162
+ deployment: r,
163
+ orders: n,
164
+ custody: o,
165
+ refunds: V,
166
+ notifications: i.createNotifications(a),
167
+ current: i.createCurrentState(a),
168
+ ...T ? { changed: T } : {}
169
+ }));
170
+ }
171
+ }
172
+ }, Y = Object.freeze({
173
+ pending: (e) => W(() => {
174
+ let t = E ?? V?.events;
175
+ return t ? t.pending(structuredClone(e)) : b();
176
+ }),
177
+ read: (e, t) => W(() => {
178
+ let n = E ?? V?.events;
179
+ return n ? n.read(structuredClone(e), t) : b();
180
+ }),
181
+ acknowledge: (e, t, n) => W(() => {
182
+ let r = E ?? V?.events;
183
+ return r ? r.acknowledge(structuredClone(e), t, n) : b();
184
+ })
185
+ }), X = w ? E ? "custom" : null : "sql", Z = w ? D ? "custom" : null : "sql", Q = Z === null ? void 0 : Object.freeze({ read: async (t, n) => {
186
+ let r = z, i = D?.read ?? V?.readDisposition;
187
+ if (!H() || !i) throw e("unavailable");
188
+ let a = await i(t, n);
189
+ if (!H() || z !== r) throw e("unavailable");
190
+ return a;
191
+ } }), $ = X === null ? _.succeed(v, K) : _.merge(_.succeed(v, K), _.succeed(o, Y)), ee = Object.freeze({
192
+ backend: w ? "custom" : "sql",
193
+ version: C,
194
+ channels: A,
195
+ httpPaths: q.map((e) => e.path),
196
+ eventQueue: X,
197
+ orderDisposition: Z,
198
+ debtPolicy: P,
199
+ itemParticipant: w ? "app-owned" : F ? "prepared-sql" : "none",
200
+ assetsVersion: I,
201
+ budgets: {
202
+ maxBodyBytes: k,
203
+ bodyRetentionMs: j,
204
+ noticeRows: M,
205
+ refundRows: N
206
+ }
207
+ });
208
+ return Object.freeze({
209
+ service: K,
210
+ events: X === null ? void 0 : Y,
211
+ dispositionSource: Q,
212
+ bind: J,
213
+ initialize: G,
214
+ layer: $,
215
+ httpRoutes: q,
216
+ status: ee,
217
+ tables: w ? [] : [...c, ...l],
218
+ roles: w ? [] : [
219
+ "iapNotificationNamespaces",
220
+ "iapNotificationSubjects",
221
+ "iapNotifications",
222
+ "iapRefundNamespaces",
223
+ "iapRefundSubjects",
224
+ "iapRefundOrders",
225
+ "iapRefundAdjustments"
226
+ ],
227
+ deactivate: () => {
228
+ z++, R = void 0, B = /* @__PURE__ */ new Map(), V = void 0;
229
+ }
230
+ });
231
+ };
232
+ //#endregion
233
+ export { v as IapNotificationsSystemPort, S as bindIapNotificationsSystem, y as captureIapNotificationsSystemService, x as unavailableIapNotifications };
@@ -0,0 +1,51 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { IapNotificationsSystemPort as t } from "./iapNotificationsBinding.server.js";
3
+ import { iapInstalledNotificationsFixture as n } from "./iapInstalledNotifications.testFixtures.js";
4
+ import { Effect as r, Layer as i } from "effect";
5
+ import { InMemoryDataStore as a } from "@voltro/runtime";
6
+ //#region src/iapNotificationsSystemPort.fixture.ts
7
+ var o = /* @__PURE__ */ new WeakMap(), s = r.tryPromise({
8
+ try: () => n(new a(), "fixture.notifications.system"),
9
+ catch: () => e("unavailable")
10
+ }).pipe(r.flatMap((e) => e.activate().pipe(r.map(() => {
11
+ let t = e.plugin.economy.iapNotifications();
12
+ return o.set(t, e), Object.freeze({
13
+ service: t,
14
+ plugin: e.plugin,
15
+ user: e.base.user(),
16
+ intent: e.base.intent,
17
+ body: e.body,
18
+ privacy: {
19
+ tenantId: e.base.scope.tenantId,
20
+ subjectId: e.base.scope.ownerId,
21
+ kind: "guest"
22
+ },
23
+ close: e.close().pipe(r.orDie)
24
+ });
25
+ })))), c = i.effectContext(r.acquireRelease(s, (e) => e.close).pipe(r.flatMap((e) => i.build(e.plugin.services)))), l = Object.freeze({
26
+ kind: "port",
27
+ tag: t,
28
+ fakeLayer: c,
29
+ probe: {
30
+ run: () => r.scoped(r.gen(function* () {
31
+ let n = yield* t, i = o.get(n);
32
+ if (!i) return yield* r.fail(e("unavailable"));
33
+ yield* i.plugin.economy.iap().redeem(i.base.user(), i.base.intent);
34
+ let a = yield* n.processor("ios"), s = yield* a.ingest({ body: i.body }), c = yield* a.ingest({ body: i.body });
35
+ return {
36
+ processed: s?.state === "processed",
37
+ replay: c !== null && s?.id === c.id,
38
+ providerCalls: i.reads(),
39
+ pending: (yield* a.pending()).length
40
+ };
41
+ }).pipe(r.provide(c))),
42
+ expected: {
43
+ processed: !0,
44
+ replay: !0,
45
+ providerCalls: 1,
46
+ pending: 0
47
+ }
48
+ }
49
+ });
50
+ //#endregion
51
+ export { c as iapNotificationsSystemFakeLayer, s as iapNotificationsSystemPortFixture, l as iapNotificationsSystemPortSystemFixture };
@@ -0,0 +1,60 @@
1
+ import { iapVerificationPort as e } from "./iapVerification.server.js";
2
+ import { iapCompletionPort as t } from "./iapCompletion.server.js";
3
+ import { economyPlugin as n } from "./plugin.js";
4
+ import { iapStoreFixture as r } from "./iapStore.testFixtures.js";
5
+ import { Effect as i } from "effect";
6
+ //#region src/iapPlugin.testFixtures.ts
7
+ var a = {
8
+ app: {
9
+ name: "iap-fixture",
10
+ type: "api",
11
+ voltroVersion: "0.83.1"
12
+ },
13
+ config: {},
14
+ env: {},
15
+ logger: {
16
+ info() {},
17
+ warn() {},
18
+ error() {},
19
+ debug() {}
20
+ }
21
+ }, o = async (o, s, c = "test.tenant", l) => {
22
+ let u = await r(o, c), d = 0, f = 0, p = {
23
+ version: "iap.fixture.v1",
24
+ products: [u.definition],
25
+ namespace: u.options.namespace,
26
+ systemAccount: u.options.systemAccount,
27
+ admission: u.options.admission,
28
+ inventory: u.inventory,
29
+ providers: [{
30
+ channel: "ios",
31
+ environment: "sandbox",
32
+ applicationId: u.verificationOptions.applicationId,
33
+ create: (t) => e(t, {
34
+ initialize: () => (d++, s ? s() : i.void),
35
+ verify: () => (f++, i.succeed({
36
+ ...u.facts,
37
+ transactionId: u.transactionId
38
+ }))
39
+ }),
40
+ ...l ? { createCompletion: (e) => t(e, l) } : {}
41
+ }]
42
+ }, m = n({
43
+ currencies: u.currencies,
44
+ realMoney: !0,
45
+ now: () => u.at,
46
+ iap: p
47
+ });
48
+ return await m.bindDataStore?.(o), {
49
+ base: u,
50
+ plugin: m,
51
+ options: p,
52
+ store: o,
53
+ activate: () => m.onActivate(a),
54
+ close: i.suspend(() => m.onDeactivate(a)),
55
+ initializations: () => d,
56
+ verifications: () => f
57
+ };
58
+ };
59
+ //#endregion
60
+ export { a as iapFixtureLifecycle, o as iapPluginFixture };
@@ -0,0 +1,75 @@
1
+ import { IapPort as e, createIapReceiptReference as t } from "./iap.js";
2
+ import { IapSystemPort as n } from "./iapBinding.server.js";
3
+ import { EconomyPort as r } from "./port.js";
4
+ import { iapTestFixture as i } from "./iap.testFixtures.js";
5
+ import { iapPluginFixture as a } from "./iapPlugin.testFixtures.js";
6
+ import { Effect as o, Layer as s, Result as c } from "effect";
7
+ import { InMemoryDataStore as l } from "@voltro/runtime";
8
+ //#region src/iapPort.fixture.ts
9
+ var u = o.promise(() => a(new l(), void 0, "fixture", {
10
+ initialize: () => o.void,
11
+ complete: (e) => o.succeed({
12
+ orderId: e.orderId,
13
+ channel: "ios",
14
+ operation: "finish",
15
+ observedAt: e.at
16
+ })
17
+ })).pipe(o.flatMap((e) => e.activate().pipe(o.as(e)))), d = s.effectContext(o.acquireRelease(u, (e) => e.close).pipe(o.flatMap((e) => s.build(e.plugin.services)))), f = Object.freeze({
18
+ kind: "port",
19
+ tag: e,
20
+ fakeLayer: d,
21
+ probe: {
22
+ run: () => o.scoped(o.gen(function* () {
23
+ let a = yield* e, s = yield* n, c = yield* r, l = i().intent, u = {
24
+ type: "user",
25
+ kind: "guest",
26
+ id: "test.player",
27
+ tenantId: "fixture"
28
+ }, d = yield* a.redeem(u, l), f = yield* a.redeem(u, l), p = yield* o.promise(() => t(l)), m = yield* a.receipt(u, p), h = yield* s.complete(u, l), g = yield* c.mine(u);
29
+ return {
30
+ original: d.orderId === f.orderId,
31
+ state: m.state,
32
+ completion: h.orderId === d.orderId,
33
+ balance: g[0]?.balance
34
+ };
35
+ }).pipe(o.provide(d))),
36
+ expected: {
37
+ original: !0,
38
+ state: "fulfilled",
39
+ completion: !0,
40
+ balance: "20"
41
+ }
42
+ }
43
+ }), p = Object.freeze({
44
+ kind: "port",
45
+ tag: n,
46
+ fakeLayer: d,
47
+ probe: {
48
+ run: () => o.scoped(o.gen(function* () {
49
+ let e = yield* n, t = {
50
+ type: "user",
51
+ kind: "guest",
52
+ id: "fixture.account",
53
+ tenantId: "fixture"
54
+ }, r = yield* e.account(t, "ios"), i = yield* e.account(t, "ios");
55
+ yield* e.eraseSubject({
56
+ tenantId: t.tenantId,
57
+ subjectId: t.id,
58
+ kind: "guest"
59
+ });
60
+ let a = yield* e.account(t, "ios").pipe(o.result);
61
+ return {
62
+ stable: r === i,
63
+ publicUuid: /^[a-f0-9-]{36}$/.test(r),
64
+ erased: c.isFailure(a) && a.failure.code === "erased"
65
+ };
66
+ }).pipe(o.provide(d))),
67
+ expected: {
68
+ stable: !0,
69
+ publicUuid: !0,
70
+ erased: !0
71
+ }
72
+ }
73
+ });
74
+ //#endregion
75
+ export { d as iapFakeLayer, u as iapPortFixture, f as iapPortSystemFixture, p as iapSystemPortSystemFixture };
@@ -0,0 +1,18 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { dataObject as t } from "./capture.js";
3
+ import { iapTimestamp as n } from "./iap.js";
4
+ //#region src/iapProviderCapture.server.ts
5
+ var r = (n) => {
6
+ if (typeof n != "object" || !n || Array.isArray(n)) throw e("invalid_record");
7
+ let r = Object.keys(n);
8
+ if (r.length > 128) throw e("limit_exceeded");
9
+ return t(n, r, "invalid_record");
10
+ }, i = (t, n = 512) => {
11
+ if (typeof t != "string" || t.length === 0 || t.length > n || /[\u0000-\u0020\u007f]/.test(t)) throw e("invalid_record");
12
+ return t;
13
+ }, a = (t) => {
14
+ if (typeof t != "string" || t.length > 48 || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/.test(t)) throw e("invalid_record");
15
+ return n(Date.parse(t));
16
+ };
17
+ //#endregion
18
+ export { r as iapProviderRecord, i as iapProviderText, a as iapProviderTimestamp };
@@ -0,0 +1,146 @@
1
+ /** Caller supplies real browser storage/Web Locks. Missing capabilities refuse; no memory fallback. */
2
+ export declare const browserIapPurchaseJournal: (storage: Pick<Storage, "getItem" | "setItem" | "removeItem">, locks: Pick<LockManager, "request">) => IapPurchaseJournal;
3
+
4
+ /** One shared acknowledged-intention/lock/scope algorithm; no automatic restore, redemption or finish. */
5
+ export declare const createIapPurchaseRecovery: (journal: IapPurchaseJournal, sourceInput: IapReceiptRecoverySource, transportInput: IapPurchaseRecoveryTransport) => IapPurchaseRecovery;
6
+
7
+ declare type EconomyChannel = 'web' | 'ios' | 'android' | 'steam';
8
+
9
+ declare interface IapCompletionReceipt {
10
+ readonly orderId: string;
11
+ readonly channel: EconomyChannel;
12
+ readonly operation: 'acknowledge' | 'consume' | 'finalize' | 'finish';
13
+ readonly observedAt: number;
14
+ }
15
+
16
+ declare type IapOrderObservation = {
17
+ readonly requestId: string;
18
+ readonly state: 'not-observed' | 'pending';
19
+ } | {
20
+ readonly requestId: string;
21
+ readonly state: 'fulfilled';
22
+ readonly receipt: IapOrderReceipt;
23
+ readonly refund?: IapRefundDisposition;
24
+ } | {
25
+ readonly requestId: string;
26
+ readonly state: 'partially-refunded' | 'refunded';
27
+ readonly receipt: IapOrderReceipt;
28
+ readonly refund: IapRefundDisposition;
29
+ };
30
+
31
+ declare interface IapOrderReceipt {
32
+ readonly orderId: string;
33
+ readonly offerId: string;
34
+ readonly offerVersion: string;
35
+ readonly quantity: number;
36
+ readonly at: number;
37
+ readonly pricePaid: IapPaidAmount | null;
38
+ readonly transfers: readonly {
39
+ readonly txId: string;
40
+ readonly currency: string;
41
+ readonly amount: string;
42
+ readonly at: number;
43
+ }[];
44
+ readonly grants: readonly {
45
+ readonly operationId: string;
46
+ readonly kind: 'grant';
47
+ readonly changes: readonly {
48
+ readonly instanceId: string;
49
+ readonly delta: number;
50
+ }[];
51
+ readonly at: number;
52
+ }[];
53
+ }
54
+
55
+ /** Provider units retain their own decimal scale. Reference prices are never substituted. */
56
+ declare interface IapPaidAmount {
57
+ readonly currency: string;
58
+ readonly units: string;
59
+ readonly scale: number;
60
+ }
61
+
62
+ /** Native implementations must provide actual durable acknowledgement and a cross-session/cross-client lock. */
63
+ export declare type IapPurchaseJournal = IntentionJournal<IapReceiptReference>;
64
+
65
+ export declare interface IapPurchaseRecovery {
66
+ readonly selectScope: (scope: string | undefined) => void;
67
+ readonly pending: () => Promise<IapPurchaseReference | undefined>;
68
+ /** Hash captured evidence and acknowledge its reference before any server mutation. */
69
+ readonly begin: (originalIntent: IapReceiptIntent) => Promise<IapPurchaseReference>;
70
+ /** Explicit retry of the stored reference; restore and match the actual evidence before any mutation. */
71
+ readonly retry: (originalReference: IapReceiptReference) => Promise<IapPurchaseSettlement>;
72
+ /** Fulfilled/refunded/partially-refunded alone is nonterminal for provider completion recovery. */
73
+ readonly observe: (observation: IapRecoveryObservation) => Promise<IapPurchaseReference | undefined>;
74
+ }
75
+
76
+ export declare interface IapPurchaseRecoveryTransport {
77
+ readonly read: (reference: IapReceiptReference) => Promise<IapOrderObservation>;
78
+ readonly redeem: (intent: IapReceiptIntent) => Promise<IapOrderReceipt>;
79
+ /** Explicit actual provider observation. For StoreKit, native transaction.finish after SQL acceptance. */
80
+ readonly complete: (intent: IapReceiptIntent, accepted: IapOrderReceipt) => Promise<IapCompletionReceipt>;
81
+ }
82
+
83
+ export declare type IapPurchaseReference = IntentionReference<IapReceiptReference>;
84
+
85
+ export declare interface IapPurchaseSettlement {
86
+ readonly receipt: IapOrderReceipt;
87
+ readonly completion: IapCompletionReceipt;
88
+ }
89
+
90
+ declare interface IapReceiptIntent {
91
+ readonly channel: EconomyChannel;
92
+ readonly offerId: string;
93
+ readonly offerVersion: string;
94
+ readonly requestId: string;
95
+ /** Opaque store evidence. Never log or persist this in player receipts. */
96
+ readonly receipt: string;
97
+ }
98
+
99
+ export declare interface IapReceiptRecoverySource {
100
+ /** Restore the exact original from the actual store/secure authority. Never browser-journal plaintext. */
101
+ readonly restore: (reference: IapReceiptReference) => Promise<IapReceiptIntent>;
102
+ }
103
+
104
+ /** Read-only original-intention reference. Never send raw receipts in query parameters. */
105
+ declare interface IapReceiptReference {
106
+ readonly channel: EconomyChannel;
107
+ readonly offerId: string;
108
+ readonly offerVersion: string;
109
+ readonly requestId: string;
110
+ readonly receiptDigest: string;
111
+ }
112
+
113
+ export declare type IapRecoveryObservation = IapOrderObservation | {
114
+ readonly requestId: string;
115
+ readonly state: 'completed';
116
+ readonly receipt: IapOrderReceipt;
117
+ readonly completion: IapCompletionReceipt;
118
+ };
119
+
120
+ /** Accepted SQL refund history only. This projection never authorizes a grant or completion. */
121
+ declare interface IapRefundDisposition {
122
+ readonly sequence: number;
123
+ readonly state: 'active' | 'partially-refunded' | 'revoked';
124
+ readonly refundNumerator: number;
125
+ readonly refundDenominator: number;
126
+ readonly observedAt: number;
127
+ }
128
+
129
+ declare interface IntentionJournal<I> {
130
+ readonly withLocked: <A>(scope: string, work: (slot: IntentionSlot<I>) => Promise<A>) => Promise<A>;
131
+ }
132
+
133
+ /** Internal shared acknowledged-intention algorithm. Domain adapters own receipt authority. */
134
+ declare interface IntentionReference<I> {
135
+ readonly version: 1;
136
+ readonly scope: string;
137
+ readonly request: I;
138
+ }
139
+
140
+ declare interface IntentionSlot<I> {
141
+ readonly read: () => IntentionReference<I> | undefined;
142
+ readonly write: (value: IntentionReference<I>) => void;
143
+ readonly clear: (value: IntentionReference<I>) => void;
144
+ }
145
+
146
+ export { }