@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,108 @@
1
+ import { EconomyError as e, economyError as t } from "./errors.js";
2
+ import { captureBoundMethods as n, dataObject as r, identifier as i } from "./capture.js";
3
+ import { shopInteger as a } from "./shop.js";
4
+ import { iapTimestamp as o } from "./iap.js";
5
+ import { canonicalDataJson as s } from "./canonicalDataJson.js";
6
+ import { captureIapNotificationTargetOptions as c, readAcceptedIapNotificationTarget as l } from "./iapNotificationTarget.server.js";
7
+ import { Context as u, Effect as d } from "effect";
8
+ //#region src/iapCurrentState.server.ts
9
+ var f = class extends u.Service()("@voltro/IapCurrentStatePort") {}, p = /* @__PURE__ */ new WeakMap(), m = (n) => n instanceof e ? n : t("unavailable"), h = (e, n) => {
10
+ let s = r(e, [
11
+ "orderId",
12
+ "channel",
13
+ "state",
14
+ "refundNumerator",
15
+ "refundDenominator",
16
+ "observedAt",
17
+ "revokedAt"
18
+ ], "invalid_record"), c = a(s.refundDenominator, 1, 1e5), l = a(s.refundNumerator, 0, c), u = l === 0 ? "active" : l === c ? "revoked" : "partially-refunded", d = o(s.observedAt), f = s.revokedAt === null ? null : o(s.revokedAt);
19
+ if (s.orderId !== n.orderId || s.channel !== n.facts.channel || s.state !== u || d < n.at || f !== null && (l === 0 || f < n.facts.purchasedAt || f > d)) throw t("invalid_record");
20
+ return Object.freeze({
21
+ orderId: i(s.orderId),
22
+ channel: s.channel,
23
+ state: u,
24
+ refundNumerator: l,
25
+ refundDenominator: c,
26
+ observedAt: d,
27
+ revokedAt: f
28
+ });
29
+ }, g = (e) => {
30
+ let n = typeof e == "object" && e ? p.get(e) : void 0;
31
+ return n ? d.suspend(n.read).pipe(d.map((e) => Object.freeze({
32
+ state: n.state,
33
+ original: e,
34
+ assertFresh: n.assertFresh,
35
+ prepareBinding: n.prepareBinding
36
+ })), d.catchDefect(() => d.fail(t("unavailable")))) : d.fail(t("forbidden"));
37
+ }, _ = (e) => g(e).pipe(d.map((e) => e.state)), v = (e) => n(e, ["initialize", "read"]), y = (e, i) => {
38
+ let u = r(e, [
39
+ "realMoney",
40
+ "channel",
41
+ "applicationId",
42
+ "environment",
43
+ "accounts",
44
+ "maxAgeMs",
45
+ "now"
46
+ ]), f = c({
47
+ realMoney: u.realMoney,
48
+ channel: u.channel,
49
+ applicationId: u.applicationId,
50
+ environment: u.environment
51
+ }), g = n(u.accounts, ["observe", "prepareBinding"]), _ = n(i, ["initialize", "read"]), v = a(u.maxAgeMs ?? 3e4, 1, 3e5), y = u.now ?? Date.now;
52
+ if (typeof y != "function") throw t("invalid_input");
53
+ let b = () => o(y()), x = (e) => l(e).pipe(d.flatMap((e) => e.facts.channel !== f.channel || e.facts.environment !== f.environment || e.facts.applicationId !== f.applicationId ? d.fail(t("forbidden")) : g.observe(e.scope, f.channel, f.environment).pipe(d.map((t) => Object.freeze({
54
+ ...e,
55
+ accountToken: t
56
+ })))));
57
+ return Object.freeze({
58
+ initialize: () => d.suspend(() => _.initialize()).pipe(d.catchDefect(() => d.fail(t("unavailable")))),
59
+ read: (e) => x(e).pipe(d.flatMap((e) => d.try({
60
+ try: () => Object.freeze({
61
+ ...e,
62
+ at: b()
63
+ }),
64
+ catch: m
65
+ })), d.flatMap((n) => d.suspend(() => _.read(n)).pipe(d.flatMap((e) => d.try({
66
+ try: () => h(e, n),
67
+ catch: m
68
+ })), d.flatMap((r) => {
69
+ let i = () => x(e).pipe(d.flatMap((e) => d.try({
70
+ try: () => {
71
+ let i = b();
72
+ if (i < r.observedAt || i - r.observedAt > v) throw t("unavailable");
73
+ if (e.accountToken !== n.accountToken || s([
74
+ e.scope,
75
+ e.receipt,
76
+ e.facts,
77
+ e.product,
78
+ e.currencies,
79
+ e.fingerprint
80
+ ]) !== s([
81
+ n.scope,
82
+ n.receipt,
83
+ n.facts,
84
+ n.product,
85
+ n.currencies,
86
+ n.fingerprint
87
+ ])) throw t("conflict");
88
+ return e;
89
+ },
90
+ catch: m
91
+ })));
92
+ return i().pipe(d.map(() => {
93
+ let e = Object.freeze({ orderId: n.orderId });
94
+ return p.set(e, Object.freeze({
95
+ state: r,
96
+ read: i,
97
+ assertFresh: () => {
98
+ let e = b();
99
+ if (e < r.observedAt || e - r.observedAt > v) throw t("unavailable");
100
+ },
101
+ prepareBinding: () => g.prepareBinding(n.scope, f.channel, f.environment, n.accountToken)
102
+ })), e;
103
+ }));
104
+ }))), d.catchDefect(() => d.fail(t("unavailable"))))
105
+ });
106
+ };
107
+ //#endregion
108
+ export { f as IapCurrentStatePort, v as captureIapCurrentStateService, _ as iapCurrentPurchaseState, y as iapCurrentStatePort, g as readVerifiedIapCurrentState };
@@ -0,0 +1,23 @@
1
+ import { iapStoreFixture as e } from "./iapStore.testFixtures.js";
2
+ import { iapSqlNoticeFixture as t } from "./iapNotificationTarget.testFixtures.js";
3
+ import { Effect as n } from "effect";
4
+ import "node:crypto";
5
+ import { InMemoryDataStore as r } from "@voltro/runtime";
6
+ //#region src/iapCurrentState.testFixtures.ts
7
+ var i = async () => {
8
+ let i = new r(), a = await e(i), o = await n.runPromise(a.iap.redeem(a.user(), a.intent)), s = t(a);
9
+ return {
10
+ store: i,
11
+ f: a,
12
+ accepted: o,
13
+ n: s,
14
+ target: await n.runPromise(a.iap.prepareNotification(s.options, s.evidence)),
15
+ options: {
16
+ ...s.options,
17
+ accounts: a.accounts,
18
+ now: () => a.at
19
+ }
20
+ };
21
+ };
22
+ //#endregion
23
+ export { i as currentStateSqlFixture };
@@ -0,0 +1,64 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { IapCurrentStatePort as t, iapCurrentPurchaseState as n, iapCurrentStatePort as r } from "./iapCurrentState.server.js";
3
+ import { currentStateSqlFixture as i } from "./iapCurrentState.testFixtures.js";
4
+ import { Effect as a, Layer as o, Result as s } from "effect";
5
+ //#region src/iapCurrentStatePort.fixture.ts
6
+ var c = /* @__PURE__ */ new WeakMap(), l = a.tryPromise({
7
+ try: i,
8
+ catch: () => e("unavailable")
9
+ }).pipe(a.map((t) => {
10
+ let n = !0, i = !1, o = { observe: ((...r) => a.suspend(() => n && !i ? t.f.accounts.observe(...r) : a.fail(e("unavailable")))) }, s = r({
11
+ ...t.options,
12
+ accounts: {
13
+ ...o,
14
+ prepareBinding: (...r) => a.suspend(() => n && !i ? t.f.accounts.prepareBinding(...r) : a.fail(e("unavailable")))
15
+ }
16
+ }, {
17
+ initialize: () => a.void,
18
+ read: (e) => a.succeed({
19
+ orderId: e.orderId,
20
+ channel: "ios",
21
+ state: "active",
22
+ refundNumerator: 0,
23
+ refundDenominator: 1,
24
+ observedAt: e.at,
25
+ revokedAt: null
26
+ })
27
+ }), l = Object.freeze({
28
+ initialize: () => a.suspend(() => i ? a.fail(e("unavailable")) : s.initialize()),
29
+ read: (t) => a.suspend(() => n && !i ? s.read(t) : a.fail(e("unavailable")))
30
+ });
31
+ return c.set(l, t), Object.freeze({
32
+ service: l,
33
+ target: t.target,
34
+ original: t.accepted,
35
+ close: a.sync(() => {
36
+ n = !1, i = !0;
37
+ })
38
+ });
39
+ })), u = o.effect(t, a.acquireRelease(l, (e) => e.close).pipe(a.map((e) => e.service))), d = Object.freeze({
40
+ kind: "port",
41
+ tag: t,
42
+ fakeLayer: u,
43
+ probe: {
44
+ run: () => a.scoped(a.gen(function* () {
45
+ let r = yield* t, i = c.get(r);
46
+ if (!i) return yield* a.fail(e("unavailable"));
47
+ let o = yield* r.read(i.target), l = yield* n(o), u = yield* r.read({ ...i.target }).pipe(a.result);
48
+ return {
49
+ state: l.state,
50
+ share: [l.refundNumerator, l.refundDenominator],
51
+ copiedRefused: s.isFailure(u),
52
+ actualOrder: l.orderId === i.accepted.orderId
53
+ };
54
+ }).pipe(a.provide(u))),
55
+ expected: {
56
+ state: "active",
57
+ share: [0, 1],
58
+ copiedRefused: !0,
59
+ actualOrder: !0
60
+ }
61
+ }
62
+ });
63
+ //#endregion
64
+ export { u as iapCurrentStateFakeLayer, l as iapCurrentStatePortFixture, d as iapCurrentStatePortSystemFixture };
@@ -0,0 +1,79 @@
1
+ import { Context } from 'effect';
2
+ import { Effect } from 'effect';
3
+ import { PluginPrivacyTarget } from '@voltro/protocol';
4
+ import { Schema } from 'effect';
5
+ import { Subject } from '@voltro/protocol';
6
+ import { YieldableError } from 'effect/Cause';
7
+
8
+ export declare const captureIapEntitlementObservation: (input: unknown) => IapEntitlementObservation;
9
+
10
+ export declare const captureIapEntitlementsService: (input: IapEntitlementsService) => IapEntitlementsService;
11
+
12
+ /** Half-open accepted period only. No grace, future renewal or provider status is invented. */
13
+ export declare const defaultIapEntitlementPolicy: IapEntitlementPolicy;
14
+
15
+ declare type EconomyChannel = 'web' | 'ios' | 'android' | 'steam';
16
+
17
+ /** Redacted codes; never carries owners, amounts or provider payloads. */
18
+ declare class EconomyError extends EconomyError_base {
19
+ }
20
+
21
+ declare const EconomyError_base: Schema.Class<EconomyError, Schema.TaggedStruct<"EconomyError", {
22
+ readonly code: Schema.Literals<readonly ["invalid_currency", "invalid_amount", "overflow", "invalid_price", "real_money_disabled", "invalid_input", "invalid_record", "currency_cap_reached", "insufficient_funds", "conflict", "forbidden", "unavailable", "not_found", "erased", "limit_exceeded", "already_redeemed"]>;
23
+ }>, YieldableError>;
24
+
25
+ export declare interface IapEntitlementObservation {
26
+ readonly offerId: string;
27
+ readonly entitled: boolean;
28
+ readonly expiresAt: number | null;
29
+ readonly observedAt: number;
30
+ }
31
+
32
+ export declare type IapEntitlementPolicy = (context: IapEntitlementPolicyContext) => boolean;
33
+
34
+ export declare interface IapEntitlementPolicyContext {
35
+ readonly period: IapSubscriptionPeriod;
36
+ readonly at: number;
37
+ }
38
+
39
+ /** Complete replacement owns authoritative history, privacy and expiry admission. */
40
+ export declare class IapEntitlementsPort extends IapEntitlementsPort_base {
41
+ }
42
+
43
+ declare const IapEntitlementsPort_base: Context.ServiceClass<IapEntitlementsPort, "@voltro/IapEntitlementsPort", IapEntitlementsService>;
44
+
45
+ export declare interface IapEntitlementsService {
46
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
47
+ readonly entitled: (subject: Subject, offerId: string) => Effect.Effect<IapEntitlementObservation, EconomyError>;
48
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
49
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
50
+ }
51
+
52
+ /** Accepted SQL refund history only. This projection never authorizes a grant or completion. */
53
+ declare interface IapRefundDisposition {
54
+ readonly sequence: number;
55
+ readonly state: 'active' | 'partially-refunded' | 'revoked';
56
+ readonly refundNumerator: number;
57
+ readonly refundDenominator: number;
58
+ readonly observedAt: number;
59
+ }
60
+
61
+ /** An accepted store period, not a fresh provider observation or financial capability. */
62
+ declare interface IapSubscriptionPeriod {
63
+ readonly orderId: string;
64
+ readonly offerId: string;
65
+ readonly offerVersion: string;
66
+ readonly channel: EconomyChannel;
67
+ readonly startsAt: number;
68
+ readonly expiresAt: number;
69
+ readonly refund: IapRefundDisposition | null;
70
+ }
71
+
72
+ export declare const resolveIapEntitlement: (input: {
73
+ readonly offerId: string;
74
+ readonly periods: readonly IapSubscriptionPeriod[];
75
+ readonly at: number;
76
+ readonly version: string;
77
+ }, policy?: IapEntitlementPolicy) => IapEntitlementObservation;
78
+
79
+ export { }
@@ -0,0 +1,58 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { captureBoundMethods as t, dataList as n, dataObject as r, identifier as i } from "./capture.js";
3
+ import { captureIapSubscriptionPeriod as a, iapTimestamp as o } from "./iap.js";
4
+ import { Context as s } from "effect";
5
+ //#region src/iapEntitlements.ts
6
+ var c = (t) => {
7
+ let n = r(t, [
8
+ "offerId",
9
+ "entitled",
10
+ "expiresAt",
11
+ "observedAt"
12
+ ], "invalid_record"), a = i(n.offerId, "invalid_record"), s = o(n.observedAt);
13
+ if (typeof n.entitled != "boolean") throw e("invalid_record");
14
+ let c = n.expiresAt === null ? null : o(n.expiresAt);
15
+ if (n.entitled ? c === null || c <= s : c !== null) throw e("invalid_record");
16
+ return Object.freeze({
17
+ offerId: a,
18
+ entitled: n.entitled,
19
+ expiresAt: c,
20
+ observedAt: s
21
+ });
22
+ }, l = ({ period: e, at: t }) => t >= e.startsAt && t < e.expiresAt && e.refund?.state !== "revoked", u = (t, s = l) => {
23
+ let c = r(t, [
24
+ "offerId",
25
+ "periods",
26
+ "at",
27
+ "version"
28
+ ]), u = i(c.offerId), d = o(c.at), f = i(c.version);
29
+ if (typeof s != "function" || s !== l && f === "default.v1") throw e("invalid_input");
30
+ let p = Object.freeze(n(c.periods, 1e5, "invalid_record").map(a));
31
+ if (new Set(p.map((e) => e.orderId)).size !== p.length) throw e("invalid_record");
32
+ let m = null;
33
+ for (let t of p) {
34
+ if (t.offerId !== u) continue;
35
+ let n = s(Object.freeze({
36
+ period: t,
37
+ at: d
38
+ }));
39
+ if (typeof n != "boolean") throw e("invalid_record");
40
+ n && l({
41
+ period: t,
42
+ at: d
43
+ }) && (m = m === null ? t.expiresAt : Math.max(m, t.expiresAt));
44
+ }
45
+ return Object.freeze({
46
+ offerId: u,
47
+ entitled: m !== null,
48
+ expiresAt: m,
49
+ observedAt: d
50
+ });
51
+ }, d = class extends s.Service()("@voltro/IapEntitlementsPort") {}, f = (e) => t(e, [
52
+ "initialize",
53
+ "entitled",
54
+ "exportSubject",
55
+ "eraseSubject"
56
+ ]);
57
+ //#endregion
58
+ export { d as IapEntitlementsPort, c as captureIapEntitlementObservation, f as captureIapEntitlementsService, l as defaultIapEntitlementPolicy, u as resolveIapEntitlement };
@@ -0,0 +1,49 @@
1
+ import { EconomyError as e, economyError as t } from "./errors.js";
2
+ import { captureBoundMethods as n, dataObject as r, identifier as i } from "./capture.js";
3
+ import { iapTimestamp as a } from "./iap.js";
4
+ import { defaultIapEntitlementPolicy as o, resolveIapEntitlement as s } from "./iapEntitlements.js";
5
+ import { Effect as c } from "effect";
6
+ //#region src/iapEntitlements.server.ts
7
+ var l = (n) => n instanceof e ? n : t("unavailable"), u = (e) => {
8
+ let u = r(e, [
9
+ "realMoney",
10
+ "version",
11
+ "orders",
12
+ "policy",
13
+ "now"
14
+ ]);
15
+ if (u.realMoney !== !0) throw t(u.realMoney === !1 ? "real_money_disabled" : "invalid_input");
16
+ let d = i(u.version), f = u.policy ?? o, p = u.now ?? Date.now;
17
+ if (typeof f != "function" || typeof p != "function" || f !== o && d === "default.v1") throw t("invalid_input");
18
+ let m = n(u.orders, [
19
+ "subscriptionPeriods",
20
+ "exportSubject",
21
+ "eraseSubject"
22
+ ]), h = (e) => {
23
+ let n = structuredClone(e);
24
+ if (n.type !== "user") throw t("forbidden");
25
+ return i(n.id), i(n.tenantId), n;
26
+ };
27
+ return Object.freeze({
28
+ initialize: () => c.void,
29
+ entitled: (e, n) => c.try({
30
+ try: () => ({
31
+ subject: h(e),
32
+ offerId: i(n)
33
+ }),
34
+ catch: l
35
+ }).pipe(c.flatMap(({ subject: e, offerId: n }) => c.suspend(() => m.subscriptionPeriods(e)).pipe(c.flatMap((e) => c.try({
36
+ try: () => s({
37
+ offerId: n,
38
+ periods: e,
39
+ at: a(p()),
40
+ version: d
41
+ }, f),
42
+ catch: l
43
+ })), c.catchDefect(() => c.fail(t("unavailable")))))),
44
+ exportSubject: (e) => c.suspend(() => m.exportSubject(structuredClone(e))).pipe(c.catchDefect(() => c.fail(t("unavailable")))),
45
+ eraseSubject: (e) => c.suspend(() => m.eraseSubject(structuredClone(e))).pipe(c.catchDefect(() => c.fail(t("unavailable"))))
46
+ });
47
+ };
48
+ //#endregion
49
+ export { u as iapEntitlementsPort };
@@ -0,0 +1,106 @@
1
+ import { EconomyError as e, economyError as t } from "./errors.js";
2
+ import { captureQueryTables as n, dataObject as r, identifier as i } from "./capture.js";
3
+ import { IapEntitlementsPort as a, captureIapEntitlementObservation as o, captureIapEntitlementsService as s, defaultIapEntitlementPolicy as c } from "./iapEntitlements.js";
4
+ import { iapEntitlementsPort as l } from "./iapEntitlements.server.js";
5
+ import { Effect as u, Layer as d } from "effect";
6
+ //#region src/iapEntitlementsBinding.server.ts
7
+ var f = () => u.fail(t("unavailable")), p = Object.freeze({
8
+ initialize: f,
9
+ entitled: f,
10
+ exportSubject: f,
11
+ eraseSubject: f
12
+ }), m = (p, m) => {
13
+ if (p === void 0) return;
14
+ let h = r(p, [
15
+ "version",
16
+ "policy",
17
+ "service",
18
+ "querySources"
19
+ ]), g = i(h.version);
20
+ if (g === "default.v1" || m.subscriptionOffers.length === 0) throw t("invalid_input");
21
+ let _ = h.service === void 0 ? void 0 : s(h.service);
22
+ if (m.replacedIap && !_ || _ && h.policy !== void 0 || !_ && !m.refundDispositionInstalled) throw t("invalid_input");
23
+ let v = h.policy ?? c;
24
+ if (typeof v != "function") throw t("invalid_input");
25
+ let y = h.querySources === void 0 ? [] : n(h.querySources);
26
+ if (_ && y.length === 0) throw t("invalid_input");
27
+ let b = new Set(m.subscriptionOffers), x, S, C = 0, w = () => x !== void 0 && m.isActive(), T = (e) => u.suspend(() => {
28
+ let t = _ ?? S, n = C;
29
+ return t && w() ? u.suspend(() => e(t)).pipe(u.flatMap((e) => w() && C === n && (_ ?? S) === t ? u.succeed(e) : f()), u.catchDefect(() => f())) : f();
30
+ }), E = () => u.suspend(() => {
31
+ let e = _ ?? S, t = C;
32
+ return x && e ? u.suspend(() => e.initialize()).pipe(u.flatMap(() => x && C === t ? u.void : f()), u.catchDefect(() => f())) : f();
33
+ }), D = (e) => {
34
+ let n = r(e, [
35
+ "tenantId",
36
+ "subjectId",
37
+ "kind"
38
+ ]);
39
+ if (n.kind !== "guest" && n.kind !== "user") throw t("invalid_input");
40
+ return Object.freeze({
41
+ tenantId: i(n.tenantId),
42
+ subjectId: i(n.subjectId),
43
+ kind: n.kind
44
+ });
45
+ }, O = Object.freeze({
46
+ initialize: E,
47
+ entitled: (n, r) => u.try({
48
+ try: () => {
49
+ let e = structuredClone(n), a = i(r);
50
+ if (e.type !== "user") throw t("forbidden");
51
+ if (i(e.id), i(e.tenantId), !b.has(a)) throw t("not_found");
52
+ return {
53
+ subject: e,
54
+ offerId: a
55
+ };
56
+ },
57
+ catch: (n) => n instanceof e ? n : t("invalid_input")
58
+ }).pipe(u.flatMap(({ subject: n, offerId: r }) => T((i) => i.entitled(n, r).pipe(u.flatMap((n) => u.try({
59
+ try: () => {
60
+ let e = o(n);
61
+ if (e.offerId !== r) throw t("conflict");
62
+ return e;
63
+ },
64
+ catch: (n) => n instanceof e ? n : t("invalid_record")
65
+ })))))),
66
+ exportSubject: (e) => u.try({
67
+ try: () => D(e),
68
+ catch: () => t("invalid_input")
69
+ }).pipe(u.flatMap((e) => T((t) => t.exportSubject(e)))),
70
+ eraseSubject: (e) => u.try({
71
+ try: () => D(e),
72
+ catch: () => t("invalid_input")
73
+ }).pipe(u.flatMap((e) => T((t) => t.eraseSubject(e))))
74
+ }), k = (e, n) => {
75
+ if (x && x !== e) throw Error("IAP entitlements cannot cross app stores");
76
+ if (x = e, C++, S = void 0, !_) {
77
+ if (!n) throw t("unavailable");
78
+ S = l({
79
+ realMoney: !0,
80
+ version: g,
81
+ orders: n,
82
+ policy: v,
83
+ now: m.now
84
+ });
85
+ }
86
+ }, A = Object.freeze({
87
+ backend: _ ? "custom" : "sql",
88
+ version: g,
89
+ policy: _ ? "app-owned" : v === c ? "default" : "custom",
90
+ budgets: _ ? null : { historyRows: m.historyRows }
91
+ });
92
+ return Object.freeze({
93
+ service: O,
94
+ initialize: E,
95
+ bind: k,
96
+ querySources: y,
97
+ status: A,
98
+ custom: _ !== void 0,
99
+ layer: d.succeed(a, O),
100
+ deactivate: () => {
101
+ C++, x = void 0, S = void 0;
102
+ }
103
+ });
104
+ };
105
+ //#endregion
106
+ export { m as bindIapEntitlementsSystem, p as unavailableIapEntitlements };
@@ -0,0 +1,81 @@
1
+ import { economyError as e } from "./errors.js";
2
+ import { captureIapSubscriptionPeriod as t } from "./iap.js";
3
+ import { IapEntitlementsPort as n, defaultIapEntitlementPolicy as r, resolveIapEntitlement as i } from "./iapEntitlements.js";
4
+ import { iapInstalledEntitlementsFixture as a } from "./iapInstalledEntitlements.testFixtures.js";
5
+ import { Effect as o, Layer as s } from "effect";
6
+ import { InMemoryDataStore as c } from "@voltro/runtime";
7
+ //#region src/iapEntitlementsPort.fixture.ts
8
+ var l = /* @__PURE__ */ new WeakMap(), u = o.tryPromise({
9
+ try: () => a(new c(), "fixture.iap.entitlements"),
10
+ catch: () => e("unavailable")
11
+ }).pipe(o.flatMap((e) => e.activate().pipe(o.flatMap(() => e.plugin.economy.iap().redeem(e.base.user(), e.base.intent)), o.map(() => {
12
+ let t = e.plugin.economy.iapEntitlements();
13
+ return l.set(t, e), Object.freeze({
14
+ service: t,
15
+ fixture: e,
16
+ close: e.close().pipe(o.orDie)
17
+ });
18
+ }), o.onError(() => e.close().pipe(o.orDie))))), d = s.effect(n, o.acquireRelease(u, (e) => e.close).pipe(o.map((e) => e.service))), f = Object.freeze({
19
+ kind: "port",
20
+ tag: n,
21
+ fakeLayer: d,
22
+ probe: {
23
+ run: () => o.scoped(o.gen(function* () {
24
+ let t = yield* n, r = l.get(t);
25
+ if (!r) return yield* o.fail(e("unavailable"));
26
+ let i = yield* t.entitled(r.base.user(), r.base.intent.offerId), a = yield* t.entitled(r.base.user("other"), r.base.intent.offerId), s = r.verifications();
27
+ r.setTime(r.base.at + 1e3);
28
+ let c = yield* t.entitled(r.base.user(), r.base.intent.offerId);
29
+ return {
30
+ accepted: i.entitled,
31
+ ownsExpiry: i.expiresAt === r.base.at + 1e3,
32
+ foreign: a.entitled,
33
+ expired: c.entitled,
34
+ expiryIsReadOnly: r.verifications() === s
35
+ };
36
+ }).pipe(o.provide(d))),
37
+ expected: {
38
+ accepted: !0,
39
+ ownsExpiry: !0,
40
+ foreign: !1,
41
+ expired: !1,
42
+ expiryIsReadOnly: !0
43
+ }
44
+ }
45
+ }), p = () => Object.freeze({
46
+ offerId: "fixture.subscription",
47
+ at: 150,
48
+ version: "fixture.entitlement.policy.v1",
49
+ periods: Object.freeze([t({
50
+ orderId: "fixture.original",
51
+ offerId: "fixture.subscription",
52
+ offerVersion: "fixture.v1",
53
+ channel: "ios",
54
+ startsAt: 100,
55
+ expiresAt: 200,
56
+ refund: null
57
+ })])
58
+ }), m = Object.freeze({
59
+ kind: "slot",
60
+ defaultImplementation: r,
61
+ defaultProbe: {
62
+ run: () => o.sync(() => i(p())),
63
+ expected: {
64
+ offerId: "fixture.subscription",
65
+ entitled: !0,
66
+ expiresAt: 200,
67
+ observedAt: 150
68
+ }
69
+ },
70
+ overrideProbe: {
71
+ run: () => o.sync(() => i(p(), () => !1)),
72
+ expected: {
73
+ offerId: "fixture.subscription",
74
+ entitled: !1,
75
+ expiresAt: null,
76
+ observedAt: 150
77
+ }
78
+ }
79
+ });
80
+ //#endregion
81
+ export { p as iapEntitlementPolicyFixture, m as iapEntitlementPolicySystemFixture, d as iapEntitlementsFakeLayer, u as iapEntitlementsPortFixture, f as iapEntitlementsPortSystemFixture };
@@ -0,0 +1,76 @@
1
+ import { iapVerificationPort as e } from "./iapVerification.server.js";
2
+ import { economyPlugin as t } from "./plugin.js";
3
+ import { iapFixtureLifecycle as n } from "./iapPlugin.testFixtures.js";
4
+ import { inventoryIapRefundAssets as r } from "./iapInventoryRefunds.server.js";
5
+ import { iapInstalledNotificationsFixture as i } from "./iapInstalledNotifications.testFixtures.js";
6
+ import { Effect as a } from "effect";
7
+ import { dataStoreInventory as o } from "@voltro/plugin-inventory/server";
8
+ //#region src/iapInstalledEntitlements.testFixtures.ts
9
+ var s = async (s, c = "test.installed.entitlements") => {
10
+ let l = await i(s, c), u = l.base;
11
+ await a.runPromise(l.close());
12
+ let d = u.at, f = 0, p = o(s, {
13
+ items: [u.item],
14
+ catalogSource: u.catalog,
15
+ channel: "live",
16
+ locale: "en",
17
+ now: () => d
18
+ }), m = {
19
+ ...l.options,
20
+ inventory: p,
21
+ now: () => d,
22
+ products: [{
23
+ ...u.definition,
24
+ kind: "subscription",
25
+ offer: {
26
+ ...u.definition.offer,
27
+ maxQuantity: 1
28
+ }
29
+ }],
30
+ providers: [{
31
+ ...l.options.providers[0],
32
+ create: (t) => e(t, {
33
+ initialize: () => a.void,
34
+ verify: () => (f++, a.succeed({
35
+ ...u.facts,
36
+ transactionId: u.transactionId,
37
+ kind: "subscription",
38
+ quantity: 1,
39
+ purchasedAt: u.at - 1e3,
40
+ expiresAt: u.at + 1e3,
41
+ acknowledgement: "none",
42
+ consumption: "none"
43
+ }))
44
+ })
45
+ }],
46
+ notifications: {
47
+ ...l.options.notifications,
48
+ assets: r(s, {
49
+ inventory: p,
50
+ disposition: "requireAll"
51
+ })
52
+ },
53
+ entitlements: { version: "periods.fixture.v1" }
54
+ }, h = t({
55
+ currencies: u.currencies,
56
+ realMoney: !0,
57
+ now: () => d,
58
+ iap: m
59
+ });
60
+ await h.bindDataStore?.(s);
61
+ let g = () => h.onActivate(n), _ = () => h.onDeactivate(n);
62
+ return {
63
+ ...l,
64
+ plugin: h,
65
+ options: m,
66
+ inventory: p,
67
+ activate: g,
68
+ close: _,
69
+ setTime: (e) => {
70
+ d = e;
71
+ },
72
+ verifications: () => f
73
+ };
74
+ };
75
+ //#endregion
76
+ export { s as iapInstalledEntitlementsFixture };