@voltro/plugin-economy 0.0.0-stage → 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 (177) hide show
  1. package/CHANGELOG.md +24492 -0
  2. package/LICENSE +61 -0
  3. package/README.md +2719 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9841 -0
  6. package/dist/amount.js +20 -0
  7. package/dist/appleIap.server.js +262 -0
  8. package/dist/appleIapNotifications.server.js +162 -0
  9. package/dist/appleIapTrust.server.js +30 -0
  10. package/dist/bundleValue.server.js +112 -0
  11. package/dist/canonicalDataJson.js +10 -0
  12. package/dist/capture.js +53 -0
  13. package/dist/codePort.fixture.js +123 -0
  14. package/dist/codeStore.server.js +439 -0
  15. package/dist/codeTables.entity.js +43 -0
  16. package/dist/codes.d.ts +150 -0
  17. package/dist/codes.js +82 -0
  18. package/dist/components.d.ts +397 -0
  19. package/dist/components.js +283 -0
  20. package/dist/consistency.server.js +115 -0
  21. package/dist/containerStore.server.js +687 -0
  22. package/dist/containerStore.testFixtures.js +75 -0
  23. package/dist/containerTables.entity.js +45 -0
  24. package/dist/containers.d.ts +311 -0
  25. package/dist/containers.fixture.js +128 -0
  26. package/dist/containers.js +214 -0
  27. package/dist/containers.server.js +39 -0
  28. package/dist/containersPort.fixture.js +74 -0
  29. package/dist/currency.js +32 -0
  30. package/dist/declarationFixtures.js +61 -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 +124 -0
  34. package/dist/errors.d.ts +14 -0
  35. package/dist/errors.js +21 -0
  36. package/dist/events.d.ts +46 -0
  37. package/dist/events.js +1 -0
  38. package/dist/googleIap.server.js +391 -0
  39. package/dist/googleIapNotifications.server.js +130 -0
  40. package/dist/googleIapSubscription.server.js +183 -0
  41. package/dist/iap.d.ts +309 -0
  42. package/dist/iap.js +303 -0
  43. package/dist/iap.testFixtures.js +144 -0
  44. package/dist/iapAccountTables.entity.js +39 -0
  45. package/dist/iapAccounts.server.js +200 -0
  46. package/dist/iapBinding.server.js +523 -0
  47. package/dist/iapCheckout.server.js +97 -0
  48. package/dist/iapCompletion.server.js +55 -0
  49. package/dist/iapCurrentState.server.js +107 -0
  50. package/dist/iapCurrentState.testFixtures.js +22 -0
  51. package/dist/iapCurrentStatePort.fixture.js +63 -0
  52. package/dist/iapEntitlements.d.ts +79 -0
  53. package/dist/iapEntitlements.js +57 -0
  54. package/dist/iapEntitlements.server.js +48 -0
  55. package/dist/iapEntitlementsBinding.server.js +105 -0
  56. package/dist/iapEntitlementsPort.fixture.js +80 -0
  57. package/dist/iapInstalledEntitlements.testFixtures.js +75 -0
  58. package/dist/iapInstalledNotifications.testFixtures.js +93 -0
  59. package/dist/iapInventoryRefunds.server.js +261 -0
  60. package/dist/iapNotificationCustodyPort.fixture.js +67 -0
  61. package/dist/iapNotificationPort.fixture.js +73 -0
  62. package/dist/iapNotificationProcessor.server.js +134 -0
  63. package/dist/iapNotificationProcessor.testFixtures.js +63 -0
  64. package/dist/iapNotificationProcessorPort.fixture.js +55 -0
  65. package/dist/iapNotificationSchedule.server.js +69 -0
  66. package/dist/iapNotificationStore.server.js +352 -0
  67. package/dist/iapNotificationStore.testFixtures.js +31 -0
  68. package/dist/iapNotificationTables.entity.js +48 -0
  69. package/dist/iapNotificationTarget.server.js +36 -0
  70. package/dist/iapNotificationTarget.testFixtures.js +29 -0
  71. package/dist/iapNotifications.d.ts +644 -0
  72. package/dist/iapNotifications.js +6 -0
  73. package/dist/iapNotifications.server.js +95 -0
  74. package/dist/iapNotificationsBinding.server.js +232 -0
  75. package/dist/iapNotificationsSystemPort.fixture.js +50 -0
  76. package/dist/iapPlugin.testFixtures.js +59 -0
  77. package/dist/iapPort.fixture.js +74 -0
  78. package/dist/iapProviderCapture.server.js +17 -0
  79. package/dist/iapRecovery.d.ts +146 -0
  80. package/dist/iapRecovery.js +89 -0
  81. package/dist/iapRefundEventDelivery.server.js +82 -0
  82. package/dist/iapRefundEvents.js +44 -0
  83. package/dist/iapRefundEventsPort.fixture.js +59 -0
  84. package/dist/iapRefundPort.fixture.js +54 -0
  85. package/dist/iapRefundTables.entity.js +55 -0
  86. package/dist/iapRefunds.d.ts +755 -0
  87. package/dist/iapRefunds.js +6 -0
  88. package/dist/iapRefunds.server.js +553 -0
  89. package/dist/iapRefunds.testFixtures.js +64 -0
  90. package/dist/iapSpendAdmission.server.js +98 -0
  91. package/dist/iapStore.server.js +740 -0
  92. package/dist/iapStore.testFixtures.js +127 -0
  93. package/dist/iapTables.entity.js +80 -0
  94. package/dist/iapVerification.server.js +120 -0
  95. package/dist/index.d.ts +2984 -0
  96. package/dist/index.js +15 -0
  97. package/dist/intentionRecovery.js +133 -0
  98. package/dist/ledger.js +77 -0
  99. package/dist/loyalty.d.ts +274 -0
  100. package/dist/loyalty.fixture.js +92 -0
  101. package/dist/loyalty.js +311 -0
  102. package/dist/loyaltyBinding.server.js +203 -0
  103. package/dist/loyaltyPort.fixture.js +86 -0
  104. package/dist/loyaltyShopSource.server.js +90 -0
  105. package/dist/loyaltyStore.server.js +552 -0
  106. package/dist/loyaltyStore.testFixtures.js +82 -0
  107. package/dist/loyaltyTables.entity.js +49 -0
  108. package/dist/offerMerchandising.fixture.js +114 -0
  109. package/dist/offerMerchandising.js +282 -0
  110. package/dist/offerPolicies.d.ts +441 -0
  111. package/dist/offerPolicies.fixture.js +53 -0
  112. package/dist/offerPolicies.js +267 -0
  113. package/dist/piggyBanks.d.ts +272 -0
  114. package/dist/piggyBanks.fixture.js +70 -0
  115. package/dist/piggyBanks.js +200 -0
  116. package/dist/piggyBinding.server.js +204 -0
  117. package/dist/piggyPort.fixture.js +72 -0
  118. package/dist/piggyStore.server.js +610 -0
  119. package/dist/piggyStore.testFixtures.js +97 -0
  120. package/dist/piggyTables.entity.js +40 -0
  121. package/dist/plugin.js +1047 -0
  122. package/dist/port.js +12 -0
  123. package/dist/price.d.ts +99 -0
  124. package/dist/price.js +143 -0
  125. package/dist/purchaseRecovery.js +55 -0
  126. package/dist/reports.server.js +114 -0
  127. package/dist/rpc.d.ts +3201 -0
  128. package/dist/rpc.js +1200 -0
  129. package/dist/sanctionsTradeAuthority.server.js +79 -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 +195 -0
  134. package/dist/shop.testFixtures.js +113 -0
  135. package/dist/shopPort.fixture.js +71 -0
  136. package/dist/shopPresentation.js +67 -0
  137. package/dist/shopStore.server.js +623 -0
  138. package/dist/shopTables.entity.js +62 -0
  139. package/dist/shopUiCopy.js +56 -0
  140. package/dist/spendLimits.d.ts +140 -0
  141. package/dist/spendLimits.fixture.js +52 -0
  142. package/dist/spendLimits.js +154 -0
  143. package/dist/spendLimitsPort.fixture.js +119 -0
  144. package/dist/subscriptionBinding.server.js +210 -0
  145. package/dist/subscriptionStore.server.js +642 -0
  146. package/dist/subscriptionStore.testFixtures.js +71 -0
  147. package/dist/subscriptionTables.entity.js +36 -0
  148. package/dist/subscriptions.d.ts +394 -0
  149. package/dist/subscriptions.fixture.js +96 -0
  150. package/dist/subscriptions.js +212 -0
  151. package/dist/subscriptionsPort.fixture.js +75 -0
  152. package/dist/systemFixtures.js +88 -0
  153. package/dist/tables.entity.js +82 -0
  154. package/dist/testing.d.ts +4615 -0
  155. package/dist/testing.js +29 -0
  156. package/dist/tradeAuthority.server.js +171 -0
  157. package/dist/tradeBinding.server.js +200 -0
  158. package/dist/tradeComponents.js +341 -0
  159. package/dist/tradeEscrow.server.js +231 -0
  160. package/dist/tradeEscrow.testFixtures.js +132 -0
  161. package/dist/tradeExpiry.server.js +42 -0
  162. package/dist/tradeRecovery.js +47 -0
  163. package/dist/tradeStore.server.js +749 -0
  164. package/dist/tradeStore.testFixtures.js +72 -0
  165. package/dist/tradeTables.entity.js +65 -0
  166. package/dist/tradeUiCopy.js +78 -0
  167. package/dist/trades.d.ts +246 -0
  168. package/dist/trades.fixture.js +102 -0
  169. package/dist/trades.js +288 -0
  170. package/dist/tradesPort.fixture.js +93 -0
  171. package/dist/walletEscrow.js +105 -0
  172. package/dist/walletEscrow.server.js +375 -0
  173. package/dist/walletKeys.server.js +4 -0
  174. package/dist/walletStore.js +570 -0
  175. package/dist/web.d.ts +2257 -0
  176. package/dist/web.js +8 -0
  177. package/package.json +161 -4
package/dist/server.js ADDED
@@ -0,0 +1,43 @@
1
+ import { iapEntitlementsPort as e } from "./iapEntitlements.server.js";
2
+ import { IapNotificationPort as t, captureIapNotificationService as n, iapNotificationNotice as r, isIapRefundChannel as i, issueVerifiedIapNotification as a } from "./iapNotifications.server.js";
3
+ import { observeAcceptedIapNotificationTarget as o } from "./iapNotificationTarget.server.js";
4
+ import { IapCurrentStatePort as s, captureIapCurrentStateService as c, iapCurrentPurchaseState as l, iapCurrentStatePort as u } from "./iapCurrentState.server.js";
5
+ import { economyCurrenciesTable as d, economyLedgerTable as f, economySubjectsTable as p, economyTables as m, economyTransactionsTable as h, economyWalletsTable as g } from "./tables.entity.js";
6
+ import { dataStoreEconomy as _, resolveEconomyStoreOptions as v } from "./walletStore.js";
7
+ import { iapAccountTables as y, iapAccountTokensTable as b, iapAccountsTable as x } from "./iapAccountTables.entity.js";
8
+ import { IapAccountsPort as S, captureIapAccountBindingService as C, dataStoreIapAccountOwners as w, dataStoreIapAccounts as T } from "./iapAccounts.server.js";
9
+ import { IapVerificationPort as E, iapVerificationPort as D } from "./iapVerification.server.js";
10
+ import { iapNamespacesTable as O, iapOrdersTable as k, iapReceiptsTable as A, iapSubjectsTable as j, iapTables as M } from "./iapTables.entity.js";
11
+ import { IapCompletionPort as N, captureIapCompletionService as P, iapCompletionPort as F } from "./iapCompletion.server.js";
12
+ import { dataStoreIap as I, resolveIapStoreOptions as L } from "./iapStore.server.js";
13
+ import { captureIapCheckoutService as R, captureIapDeliveryService as z, captureIapManagementService as B } from "./iapCheckout.server.js";
14
+ import { IapSystemPort as V, captureIapSystemService as H } from "./iapBinding.server.js";
15
+ import { captureTradeExpiryInput as U, tradeExpirySchedule as W } from "./tradeExpiry.server.js";
16
+ import { tradeEscrowParticipant as G } from "./tradeEscrow.server.js";
17
+ import { tradeOperationsTable as K, tradeSubjectsTable as q, tradeTables as J, tradeTenantsTable as Y, tradesTable as X } from "./tradeTables.entity.js";
18
+ import { dataStoreTrades as Z } from "./tradeStore.server.js";
19
+ import { shopCountersTable as Q, shopOffersTable as $, shopOrdersTable as ee, shopSubjectsTable as te, shopTables as ne, shopTenantsTable as re } from "./shopTables.entity.js";
20
+ import { dataStoreShop as ie, resolveShopStoreOptions as ae, shopOfferDigest as oe } from "./shopStore.server.js";
21
+ import { codeClaimsTable as se, codeSubjectsTable as ce, codeTables as le, codeTenantsTable as ue, codesTable as de } from "./codeTables.entity.js";
22
+ import { dataStoreCodes as fe, resolveCodeStoreOptions as pe } from "./codeStore.server.js";
23
+ import { cryptoContainerRandom as me, cryptoContainerRandomLayer as he, sampleContainer as ge } from "./containers.server.js";
24
+ import { containerDefinitionsTable as _e, containerOpeningsTable as ve, containerSubjectsTable as ye, containerTables as be, containerTenantsTable as xe } from "./containerTables.entity.js";
25
+ import { containerDefinitionDigest as Se, dataStoreContainers as Ce, resolveContainersStoreOptions as we } from "./containerStore.server.js";
26
+ import { subscriptionDefinitionsTable as Te, subscriptionEventsTable as Ee, subscriptionSubjectsTable as De, subscriptionTables as Oe, subscriptionTenantsTable as ke } from "./subscriptionTables.entity.js";
27
+ import { dataStoreSubscriptions as Ae, resolveSubscriptionsStoreOptions as je, subscriptionDefinitionDigest as Me } from "./subscriptionStore.server.js";
28
+ import { loyaltyDefinitionsTable as Ne, loyaltyEntriesTable as Pe, loyaltySeasonsTable as Fe, loyaltySubjectsTable as Ie, loyaltyTables as Le, loyaltyTenantsTable as Re } from "./loyaltyTables.entity.js";
29
+ import { dataStoreLoyalty as ze, resolveLoyaltyStoreOptions as Be } from "./loyaltyStore.server.js";
30
+ import { piggyDefinitionsTable as Ve, piggyEventsTable as He, piggySubjectsTable as Ue, piggyTables as We, piggyTenantsTable as Ge } from "./piggyTables.entity.js";
31
+ import { dataStorePiggyBanks as Ke, piggyBankDigest as qe, resolvePiggyStoreOptions as Je } from "./piggyStore.server.js";
32
+ import { checkSqlEconomyConsistency as Ye, economyConsistencySchedule as Xe } from "./consistency.server.js";
33
+ import { EconomyFlowRowSchema as Ze, economyFlowsAggregate as Qe, readEconomyFlows as $e } from "./reports.server.js";
34
+ import { shopLoyaltyReceiptSource as et } from "./loyaltyShopSource.server.js";
35
+ import { bundleValueFromShopReceipts as tt } from "./bundleValue.server.js";
36
+ import { captureTradeAuthorityAdmission as nt, inventoryTradeAuthoritySource as rt } from "./tradeAuthority.server.js";
37
+ import { sanctionsTradePermissionSource as it } from "./sanctionsTradeAuthority.server.js";
38
+ import { appleIapNotificationPort as at } from "./appleIapNotifications.server.js";
39
+ import { appleIapCurrentStatePort as ot, appleIapProvider as st, appleIapVerificationPort as ct } from "./appleIap.server.js";
40
+ import { googleIapNotificationPort as lt } from "./googleIapNotifications.server.js";
41
+ import { googleIapCompletionPort as ut, googleIapCurrentStatePort as dt, googleIapProvider as ft, googleIapVerificationPort as pt } from "./googleIap.server.js";
42
+ import { iapSpendAdmission as mt } from "./iapSpendAdmission.server.js";
43
+ export { Ze as EconomyFlowRowSchema, S as IapAccountsPort, N as IapCompletionPort, s as IapCurrentStatePort, t as IapNotificationPort, V as IapSystemPort, E as IapVerificationPort, ot as appleIapCurrentStatePort, at as appleIapNotificationPort, st as appleIapProvider, ct as appleIapVerificationPort, tt as bundleValueFromShopReceipts, C as captureIapAccountBindingService, R as captureIapCheckoutService, P as captureIapCompletionService, c as captureIapCurrentStateService, z as captureIapDeliveryService, B as captureIapManagementService, n as captureIapNotificationService, H as captureIapSystemService, nt as captureTradeAuthorityAdmission, U as captureTradeExpiryInput, Ye as checkSqlEconomyConsistency, se as codeClaimsTable, ce as codeSubjectsTable, le as codeTables, ue as codeTenantsTable, de as codesTable, Se as containerDefinitionDigest, _e as containerDefinitionsTable, ve as containerOpeningsTable, ye as containerSubjectsTable, be as containerTables, xe as containerTenantsTable, me as cryptoContainerRandom, he as cryptoContainerRandomLayer, fe as dataStoreCodes, Ce as dataStoreContainers, _ as dataStoreEconomy, I as dataStoreIap, w as dataStoreIapAccountOwners, T as dataStoreIapAccounts, ze as dataStoreLoyalty, Ke as dataStorePiggyBanks, ie as dataStoreShop, Ae as dataStoreSubscriptions, Z as dataStoreTrades, Xe as economyConsistencySchedule, d as economyCurrenciesTable, Qe as economyFlowsAggregate, f as economyLedgerTable, p as economySubjectsTable, m as economyTables, h as economyTransactionsTable, g as economyWalletsTable, ut as googleIapCompletionPort, dt as googleIapCurrentStatePort, lt as googleIapNotificationPort, ft as googleIapProvider, pt as googleIapVerificationPort, y as iapAccountTables, b as iapAccountTokensTable, x as iapAccountsTable, F as iapCompletionPort, l as iapCurrentPurchaseState, u as iapCurrentStatePort, e as iapEntitlementsPort, O as iapNamespacesTable, r as iapNotificationNotice, k as iapOrdersTable, A as iapReceiptsTable, mt as iapSpendAdmission, j as iapSubjectsTable, M as iapTables, D as iapVerificationPort, rt as inventoryTradeAuthoritySource, i as isIapRefundChannel, a as issueVerifiedIapNotification, Ne as loyaltyDefinitionsTable, Pe as loyaltyEntriesTable, Fe as loyaltySeasonsTable, Ie as loyaltySubjectsTable, Le as loyaltyTables, Re as loyaltyTenantsTable, o as observeAcceptedIapNotificationTarget, qe as piggyBankDigest, Ve as piggyDefinitionsTable, He as piggyEventsTable, Ue as piggySubjectsTable, We as piggyTables, Ge as piggyTenantsTable, $e as readEconomyFlows, pe as resolveCodeStoreOptions, we as resolveContainersStoreOptions, v as resolveEconomyStoreOptions, L as resolveIapStoreOptions, Be as resolveLoyaltyStoreOptions, Je as resolvePiggyStoreOptions, ae as resolveShopStoreOptions, je as resolveSubscriptionsStoreOptions, ge as sampleContainer, it as sanctionsTradePermissionSource, Q as shopCountersTable, et as shopLoyaltyReceiptSource, oe as shopOfferDigest, $ as shopOffersTable, ee as shopOrdersTable, te as shopSubjectsTable, ne as shopTables, re as shopTenantsTable, Me as subscriptionDefinitionDigest, Te as subscriptionDefinitionsTable, Ee as subscriptionEventsTable, De as subscriptionSubjectsTable, Oe as subscriptionTables, ke as subscriptionTenantsTable, G as tradeEscrowParticipant, W as tradeExpirySchedule, K as tradeOperationsTable, q as tradeSubjectsTable, J as tradeTables, Y as tradeTenantsTable, X as tradesTable };
package/dist/shop.d.ts ADDED
@@ -0,0 +1,279 @@
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
+ /** The same detached receipt admission serves SQL history and browser recovery. */
9
+ export declare const captureShopOrderReceipt: (input: unknown) => ShopOrderReceipt;
10
+
11
+ /** Capture required methods without invoking accessors; retain the original receiver, including class instances. */
12
+ export declare const captureShopService: (input: ShopService) => ShopService;
13
+
14
+ declare interface CurrencyDefinition {
15
+ readonly id: string;
16
+ readonly kind: 'soft' | 'hard';
17
+ readonly cap?: EconomyAmountInput;
18
+ readonly premium?: boolean;
19
+ readonly display?: {
20
+ readonly nameKey: string;
21
+ } | null;
22
+ }
23
+
24
+ /** `grantless`: the offer may grant nothing, for a store subscription whose value is its paid
25
+ * period (an entitlement such as game time). Every other offer grants items or currencies. */
26
+ export declare const defineShopOffer: (input: ShopOfferDefinition | ShopOffer, declarations: readonly CurrencyDefinition[], realMoney?: boolean, options?: {
27
+ readonly grantless?: boolean;
28
+ }) => ShopOffer;
29
+
30
+ declare type EconomyAmountInput = string | number | bigint;
31
+
32
+ declare type EconomyChannel = 'web' | 'ios' | 'android' | 'steam';
33
+
34
+ /** Redacted codes; never carries owners, amounts or provider payloads. */
35
+ declare class EconomyError extends EconomyError_base {
36
+ }
37
+
38
+ declare const EconomyError_base: Schema.Class<EconomyError, Schema.TaggedStruct<"EconomyError", {
39
+ 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"]>;
40
+ }>, YieldableError>;
41
+
42
+ declare type Price = {
43
+ readonly kind: 'virtual';
44
+ readonly options: readonly ResolvedVirtualPriceOption[];
45
+ } | ({
46
+ readonly kind: 'real';
47
+ } & RealPriceDefinition) | {
48
+ readonly kind: 'mixed';
49
+ readonly real: RealPriceDefinition;
50
+ readonly virtual: readonly ResolvedVirtualPriceOption[];
51
+ } | {
52
+ readonly kind: 'free';
53
+ };
54
+
55
+ declare type PriceDefinition = {
56
+ readonly kind: 'virtual';
57
+ readonly options: readonly VirtualPriceOption[];
58
+ } | ({
59
+ readonly kind: 'real';
60
+ } & RealPriceDefinition) | {
61
+ readonly kind: 'mixed';
62
+ readonly real: RealPriceDefinition;
63
+ readonly virtual: readonly VirtualPriceOption[];
64
+ } | {
65
+ readonly kind: 'free';
66
+ };
67
+
68
+ declare type PriceResolver = (context: PriceResolverContext) => Price;
69
+
70
+ declare interface PriceResolverContext {
71
+ readonly price: Price;
72
+ readonly currencies: readonly ResolvedCurrency[];
73
+ readonly realMoney: boolean;
74
+ readonly adjustments: readonly Readonly<{
75
+ currency: string;
76
+ discountBps: number;
77
+ minimum: string;
78
+ }>[];
79
+ }
80
+
81
+ declare interface RealPriceDefinition {
82
+ /** Integer minor-unit references; verified store receipts remain authoritative. */
83
+ readonly prices: Readonly<Record<string, number>>;
84
+ readonly storeProductIds: Partial<Readonly<Record<EconomyChannel, string>>>;
85
+ }
86
+
87
+ declare interface ResolvedCurrency {
88
+ readonly id: string;
89
+ readonly kind: 'soft' | 'hard';
90
+ readonly cap: string;
91
+ readonly premium: boolean;
92
+ readonly display: {
93
+ readonly nameKey: string;
94
+ } | null;
95
+ }
96
+
97
+ declare interface ResolvedVirtualPriceOption {
98
+ readonly currency: string;
99
+ readonly amount: string;
100
+ }
101
+
102
+ export declare interface ShopBuyInput {
103
+ readonly offerId: string;
104
+ readonly offerVersion: string;
105
+ readonly offerDigest: string;
106
+ readonly requestId: string;
107
+ readonly currency?: string | undefined;
108
+ readonly qty?: number | undefined;
109
+ }
110
+
111
+ export declare const shopBuyIntent: (input: ShopBuyInput) => Readonly<{
112
+ offerId: string;
113
+ offerVersion: string;
114
+ offerDigest: string;
115
+ requestId: string;
116
+ currency: string | null;
117
+ qty: number;
118
+ }>;
119
+
120
+ export declare interface ShopContextInput {
121
+ readonly subject: Subject;
122
+ readonly offer: ShopOffer;
123
+ readonly at: number;
124
+ }
125
+
126
+ export declare interface ShopContextSnapshot {
127
+ readonly eligible: boolean;
128
+ readonly adjustments: readonly VirtualPriceAdjustment[];
129
+ }
130
+
131
+ /** Trusted external profile/segment/flag facts; never inside a replayable SQL callback. */
132
+ export declare interface ShopContextSource {
133
+ readonly read: (input: ShopContextInput) => Effect.Effect<ShopContextSnapshot, EconomyError>;
134
+ }
135
+
136
+ export declare interface ShopCurrencyGrant {
137
+ readonly currency: string;
138
+ readonly amount: EconomyAmountInput;
139
+ }
140
+
141
+ export declare const shopInteger: (input: unknown, minimum: number, maximum: number) => number;
142
+
143
+ export declare interface ShopItemGrant {
144
+ readonly itemId: string;
145
+ readonly qty: number;
146
+ }
147
+
148
+ export declare interface ShopOffer {
149
+ readonly id: string;
150
+ readonly version: string;
151
+ readonly titleKey: string;
152
+ readonly price: Price;
153
+ readonly items: readonly ShopItemGrant[];
154
+ readonly currencies: readonly {
155
+ readonly currency: string;
156
+ readonly amount: string;
157
+ }[];
158
+ readonly from: number | null;
159
+ readonly until: number | null;
160
+ readonly segment: string | null;
161
+ readonly flag: string | null;
162
+ readonly countries: readonly string[];
163
+ readonly limits: {
164
+ readonly perPlayer: number | null;
165
+ readonly perDay: number | null;
166
+ readonly global: number | null;
167
+ };
168
+ readonly oneTime: boolean;
169
+ readonly maxQuantity: number;
170
+ }
171
+
172
+ export declare interface ShopOfferDefinition {
173
+ readonly id: string;
174
+ readonly version?: string | undefined;
175
+ readonly titleKey: string;
176
+ readonly price: PriceDefinition;
177
+ readonly items?: readonly ShopItemGrant[] | undefined;
178
+ readonly currencies?: readonly ShopCurrencyGrant[] | undefined;
179
+ readonly from?: number | null | undefined;
180
+ readonly until?: number | null | undefined;
181
+ readonly segment?: string | null | undefined;
182
+ readonly flag?: string | null | undefined;
183
+ readonly countries?: readonly string[] | undefined;
184
+ readonly limits?: {
185
+ readonly perPlayer?: number | null | undefined;
186
+ readonly perDay?: number | null | undefined;
187
+ readonly global?: number | null | undefined;
188
+ } | undefined;
189
+ readonly oneTime?: boolean | undefined;
190
+ readonly maxQuantity?: number | undefined;
191
+ }
192
+
193
+ export declare interface ShopOfferView {
194
+ readonly offerDigest: string;
195
+ readonly offer: ShopOffer;
196
+ readonly price: Price;
197
+ }
198
+
199
+ export declare interface ShopOrderReceipt {
200
+ readonly orderId: string;
201
+ readonly offerId: string;
202
+ readonly offerVersion: string;
203
+ readonly offerDigest: string;
204
+ readonly quantity: number;
205
+ readonly at: number;
206
+ readonly pricePaid: {
207
+ readonly kind: 'free';
208
+ } | {
209
+ readonly kind: 'virtual';
210
+ readonly currency: string;
211
+ readonly amount: string;
212
+ };
213
+ readonly transfers: readonly {
214
+ readonly txId: string;
215
+ readonly currency: string;
216
+ readonly amount: string;
217
+ readonly at: number;
218
+ }[];
219
+ readonly grants: readonly {
220
+ readonly operationId: string;
221
+ readonly kind: 'grant';
222
+ readonly changes: readonly {
223
+ readonly instanceId: string;
224
+ readonly delta: number;
225
+ }[];
226
+ readonly at: number;
227
+ }[];
228
+ }
229
+
230
+ export declare const shopPayment: (price: Price, currency: string | null, quantity: number) => ShopOrderReceipt["pricePaid"];
231
+
232
+ export declare class ShopPort extends ShopPort_base {
233
+ }
234
+
235
+ declare const ShopPort_base: Context.ServiceClass<ShopPort, "@voltro/ShopPort", ShopService>;
236
+
237
+ /** Nonterminal observations never authorize a new request identity or prove that no purchase can commit. */
238
+ export declare type ShopPurchaseObservation = {
239
+ readonly requestId: string;
240
+ readonly state: 'not-observed' | 'pending';
241
+ } | {
242
+ readonly requestId: string;
243
+ readonly state: 'fulfilled';
244
+ readonly receipt: ShopOrderReceipt;
245
+ };
246
+
247
+ export declare interface ShopRules {
248
+ readonly offers: readonly (ShopOfferDefinition | ShopOffer)[];
249
+ readonly priceResolver?: PriceResolver | undefined;
250
+ readonly contextSource?: ShopContextSource | undefined;
251
+ readonly version?: string | undefined;
252
+ readonly systemAccount?: string | undefined;
253
+ readonly maxOrders?: number | undefined;
254
+ readonly maxAuditRows?: number | undefined;
255
+ readonly now?: (() => number) | undefined;
256
+ }
257
+
258
+ export declare interface ShopService {
259
+ readonly offers: (subject: Subject) => Effect.Effect<readonly ShopOfferView[], EconomyError>;
260
+ readonly buy: (subject: Subject, input: ShopBuyInput) => Effect.Effect<ShopOrderReceipt, EconomyError>;
261
+ readonly receipt: (subject: Subject, input: ShopBuyInput) => Effect.Effect<ShopPurchaseObservation, EconomyError>;
262
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
263
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
264
+ }
265
+
266
+ declare interface VirtualPriceAdjustment {
267
+ readonly currency: string;
268
+ /** Inclusive integer 0..10000. */
269
+ readonly discountBps: number;
270
+ /** A trusted floor, enforced independently of a custom resolver. */
271
+ readonly minimum: EconomyAmountInput;
272
+ }
273
+
274
+ declare interface VirtualPriceOption {
275
+ readonly currency: string;
276
+ readonly amount: EconomyAmountInput;
277
+ }
278
+
279
+ export { }
package/dist/shop.js ADDED
@@ -0,0 +1,195 @@
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 { nonNegativeAmount as a, positiveAmount as o } from "./amount.js";
4
+ import { captureCurrencies as s } from "./currency.js";
5
+ import { definePrice as c } from "./price.js";
6
+ import { Context as l } from "effect";
7
+ // src/shop.ts
8
+ var u = (t, n, r) => {
9
+ if (typeof t != "number" || !Number.isSafeInteger(t) || t < n || t > r) throw e("invalid_input");
10
+ return t;
11
+ }, d = (t, a, l = !1, d = {}) => {
12
+ let f = r(t, [
13
+ "id",
14
+ "version",
15
+ "titleKey",
16
+ "price",
17
+ "items",
18
+ "currencies",
19
+ "from",
20
+ "until",
21
+ "segment",
22
+ "flag",
23
+ "countries",
24
+ "limits",
25
+ "oneTime",
26
+ "maxQuantity"
27
+ ]), p = s(a), m = u(f.maxQuantity ?? 1, 1, 1e3), h = n(f.items ?? [], 16).map((t) => {
28
+ let n = r(t, ["itemId", "qty"]), a = u(n.qty, 1, 1e6);
29
+ if (a * m > 1e6) throw e("limit_exceeded");
30
+ return Object.freeze({
31
+ itemId: i(n.itemId),
32
+ qty: a
33
+ });
34
+ }), g = n(f.currencies ?? [], 16).map((t) => {
35
+ let n = r(t, ["currency", "amount"]), a = i(n.currency), s = p.find((e) => e.id === a), c = o(n.amount);
36
+ if (!s || c * BigInt(m) > BigInt(s.cap)) throw e("invalid_currency");
37
+ return Object.freeze({
38
+ currency: a,
39
+ amount: c.toString()
40
+ });
41
+ });
42
+ if (!h.length && !g.length && d.grantless !== !0 || new Set(h.map((e) => e.itemId)).size !== h.length || new Set(g.map((e) => e.currency)).size !== g.length) throw e("invalid_input");
43
+ let _ = f.from == null ? null : u(f.from, 0, 864e13), v = f.until == null ? null : u(f.until, 1, 864e13);
44
+ if (_ !== null && v !== null && _ >= v) throw e("invalid_input");
45
+ let y = n(f.countries ?? [], 256).map((t) => {
46
+ if (typeof t != "string" || !/^[A-Z]{2}$/.test(t)) throw e("invalid_input");
47
+ return t;
48
+ });
49
+ if (new Set(y).size !== y.length) throw e("invalid_input");
50
+ let b = r(f.limits ?? {}, [
51
+ "perPlayer",
52
+ "perDay",
53
+ "global"
54
+ ]), x = f.oneTime ?? !1;
55
+ if (typeof x != "boolean") throw e("invalid_input");
56
+ let S = (e) => e == null ? null : u(e, 1, 2147483647), C = S(b.perPlayer), w = S(b.perDay), T = S(b.global);
57
+ if (x && (m !== 1 || C !== null && C !== 1)) throw e("invalid_input");
58
+ return Object.freeze({
59
+ id: i(f.id),
60
+ version: i(f.version ?? "1"),
61
+ titleKey: i(f.titleKey),
62
+ price: c(f.price, p, l),
63
+ items: Object.freeze(h),
64
+ currencies: Object.freeze(g),
65
+ from: _,
66
+ until: v,
67
+ segment: f.segment == null ? null : i(f.segment),
68
+ flag: f.flag == null ? null : i(f.flag),
69
+ countries: Object.freeze(y),
70
+ limits: Object.freeze({
71
+ perPlayer: x ? 1 : C,
72
+ perDay: w,
73
+ global: T
74
+ }),
75
+ oneTime: x,
76
+ maxQuantity: m
77
+ });
78
+ }, f = class extends l.Service()("@voltro/ShopPort") {}, p = (t) => {
79
+ let n = r(t, [
80
+ "offerId",
81
+ "offerVersion",
82
+ "offerDigest",
83
+ "requestId",
84
+ "currency",
85
+ "qty"
86
+ ]);
87
+ if (typeof n.offerDigest != "string" || !/^[a-f0-9]{64}$/.test(n.offerDigest)) throw e("invalid_input");
88
+ return Object.freeze({
89
+ offerId: i(n.offerId),
90
+ offerVersion: i(n.offerVersion),
91
+ offerDigest: n.offerDigest,
92
+ requestId: i(n.requestId),
93
+ currency: n.currency === void 0 ? null : i(n.currency),
94
+ qty: u(n.qty ?? 1, 1, 1e3)
95
+ });
96
+ }, m = (t, n, r) => {
97
+ if (u(r, 1, 1e3), t.kind === "free") {
98
+ if (n !== null) throw e("invalid_price");
99
+ return Object.freeze({ kind: "free" });
100
+ }
101
+ let i = (t.kind === "virtual" ? t.options : t.kind === "mixed" ? t.virtual : []).find((e) => e.currency === n);
102
+ if (!i) throw e("invalid_price");
103
+ return Object.freeze({
104
+ kind: "virtual",
105
+ currency: i.currency,
106
+ amount: a(BigInt(i.amount) * BigInt(r)).toString()
107
+ });
108
+ }, h = (e) => t(e, [
109
+ "offers",
110
+ "buy",
111
+ "receipt",
112
+ "exportSubject",
113
+ "eraseSubject"
114
+ ]), g = (t) => {
115
+ try {
116
+ let o = (t) => {
117
+ if (typeof t != "string" || a(t).toString() !== t) throw e("invalid_record");
118
+ return t;
119
+ }, s = r(t, [
120
+ "orderId",
121
+ "offerId",
122
+ "offerVersion",
123
+ "offerDigest",
124
+ "quantity",
125
+ "at",
126
+ "pricePaid",
127
+ "transfers",
128
+ "grants"
129
+ ], "invalid_record");
130
+ if (typeof s.offerDigest != "string" || !/^[a-f0-9]{64}$/.test(s.offerDigest)) throw e("invalid_record");
131
+ let c = r(s.pricePaid, [
132
+ "kind",
133
+ "currency",
134
+ "amount"
135
+ ], "invalid_record"), l;
136
+ if (c.kind === "free") r(s.pricePaid, ["kind"], "invalid_record"), l = Object.freeze({ kind: "free" });
137
+ else if (c.kind === "virtual") l = Object.freeze({
138
+ kind: "virtual",
139
+ currency: i(c.currency, "invalid_record"),
140
+ amount: o(c.amount)
141
+ });
142
+ else throw e("invalid_record");
143
+ let d = Object.freeze(n(s.transfers, 33, "invalid_record").map((e) => {
144
+ let t = r(e, [
145
+ "txId",
146
+ "currency",
147
+ "amount",
148
+ "at"
149
+ ], "invalid_record");
150
+ return Object.freeze({
151
+ txId: i(t.txId, "invalid_record"),
152
+ currency: i(t.currency, "invalid_record"),
153
+ amount: o(t.amount),
154
+ at: u(t.at, 0, 864e13)
155
+ });
156
+ })), f = Object.freeze(n(s.grants, 16, "invalid_record").map((t) => {
157
+ let a = r(t, [
158
+ "operationId",
159
+ "kind",
160
+ "changes",
161
+ "at"
162
+ ], "invalid_record");
163
+ if (a.kind !== "grant") throw e("invalid_record");
164
+ let o = Object.freeze(n(a.changes, 1e4, "invalid_record").map((e) => {
165
+ let t = r(e, ["instanceId", "delta"], "invalid_record");
166
+ return Object.freeze({
167
+ instanceId: i(t.instanceId, "invalid_record"),
168
+ delta: u(t.delta, 1, 1e6)
169
+ });
170
+ }));
171
+ if (new Set(o.map((e) => e.instanceId)).size !== o.length) throw e("invalid_record");
172
+ return Object.freeze({
173
+ operationId: i(a.operationId, "invalid_record"),
174
+ kind: "grant",
175
+ changes: o,
176
+ at: u(a.at, 0, 864e13)
177
+ });
178
+ })), p = u(s.at, 0, 864e13);
179
+ if (new Set(d.map((e) => e.txId)).size !== d.length || new Set(f.map((e) => e.operationId)).size !== f.length || d.some((e) => e.at > p) || f.some((e) => e.at > p)) throw e("invalid_record");
180
+ return Object.freeze({
181
+ orderId: i(s.orderId, "invalid_record"),
182
+ offerId: i(s.offerId, "invalid_record"),
183
+ offerVersion: i(s.offerVersion, "invalid_record"),
184
+ offerDigest: s.offerDigest,
185
+ quantity: u(s.quantity, 1, 1e3),
186
+ at: u(s.at, 0, 864e13),
187
+ pricePaid: l,
188
+ transfers: d,
189
+ grants: f
190
+ });
191
+ } catch {
192
+ throw e("invalid_record");
193
+ }
194
+ };
195
+ export { f as ShopPort, g as captureShopOrderReceipt, h as captureShopService, d as defineShopOffer, p as shopBuyIntent, u as shopInteger, m as shopPayment };
@@ -0,0 +1,113 @@
1
+ import { economyTables as e } from "./tables.entity.js";
2
+ import { dataStoreEconomy as t } from "./walletStore.js";
3
+ import { shopTables as n } from "./shopTables.entity.js";
4
+ import { dataStoreShop as r, resolveShopStoreOptions as i, shopOfferDigest as a } from "./shopStore.server.js";
5
+ import { Effect as o } from "effect";
6
+ import { systemSubject as s } from "@voltro/protocol";
7
+ import { ensureTableRegistered as c } from "@voltro/database";
8
+ import { dataStoreCatalog as l, dataStoreInventory as u, inventoryTables as d } from "@voltro/plugin-inventory/server";
9
+ import { inventoryFixture as f } from "@voltro/plugin-inventory/testing";
10
+ // src/shop.testFixtures.ts
11
+ var p = [
12
+ ...e,
13
+ ...d,
14
+ ...n
15
+ ], m = Date.UTC(2026, 9, 10), h = s("shop.fixture"), g = (e = "a", t = "tenant") => ({
16
+ type: "user",
17
+ id: e,
18
+ tenantId: t,
19
+ kind: "guest"
20
+ }), _ = (e, t = "buy.1") => {
21
+ let n = i(e), r = n.offers[0], o = r.price.kind === "virtual" ? r.price.options : r.price.kind === "mixed" ? r.price.virtual : [];
22
+ return {
23
+ offerId: r.id,
24
+ offerVersion: r.version,
25
+ offerDigest: a(r, n.version),
26
+ requestId: t,
27
+ ...o[0] ? { currency: o[0].currency } : {}
28
+ };
29
+ }, v = async (e, n = "tenant", i = {}, a = 4096) => {
30
+ p.forEach(c);
31
+ let { item: s, content: d } = f(), v = l(e, {
32
+ items: [s],
33
+ now: () => m
34
+ });
35
+ await o.runPromise(v.importSnapshot(h, {
36
+ tenantId: n,
37
+ channel: "live",
38
+ locale: "en",
39
+ revision: "shop.r1",
40
+ items: [d]
41
+ }));
42
+ let y = t(e, {
43
+ currencies: [{
44
+ id: "test.gold",
45
+ kind: "soft"
46
+ }],
47
+ now: () => m
48
+ }), b = u(e, {
49
+ items: [s],
50
+ catalogSource: { read: v.read },
51
+ channel: "live",
52
+ locale: "en",
53
+ maxInstances: a,
54
+ now: () => m
55
+ });
56
+ await o.runPromise(y.transfer(h, {
57
+ tenantId: n,
58
+ requestId: "fund.a",
59
+ currency: "test.gold",
60
+ amount: "100",
61
+ from: {
62
+ kind: "system",
63
+ id: "fixture"
64
+ },
65
+ to: {
66
+ kind: "wallet",
67
+ ownerKind: "player",
68
+ ownerId: "a"
69
+ },
70
+ reason: "fixture.reward",
71
+ ref: {
72
+ kind: "fixture",
73
+ id: "fund.a"
74
+ }
75
+ }));
76
+ let x = {
77
+ currencies: [{
78
+ id: "test.gold",
79
+ kind: "soft"
80
+ }],
81
+ realMoney: !1,
82
+ economy: y,
83
+ inventory: b,
84
+ now: () => m,
85
+ offers: [{
86
+ id: "potion",
87
+ titleKey: "shop.potion",
88
+ price: {
89
+ kind: "virtual",
90
+ options: [{
91
+ currency: "test.gold",
92
+ amount: "7"
93
+ }]
94
+ },
95
+ items: [{
96
+ itemId: s.id,
97
+ qty: 2
98
+ }],
99
+ oneTime: !0
100
+ }],
101
+ ...i
102
+ }, S = _(x);
103
+ return {
104
+ economy: y,
105
+ inventory: b,
106
+ catalog: v,
107
+ options: x,
108
+ shop: r(e, x),
109
+ user: g("a", n),
110
+ buy: S
111
+ };
112
+ };
113
+ export { m as shopAt, v as shopFixture, _ as shopFixtureBuy, p as shopFixtureTables, h as shopMachine, g as shopUser };