@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.
- package/CHANGELOG.md +24329 -0
- package/LICENSE +61 -0
- package/README.md +2719 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +9189 -0
- package/dist/amount.js +21 -0
- package/dist/appleIap.server.js +263 -0
- package/dist/appleIapNotifications.server.js +163 -0
- package/dist/appleIapTrust.server.js +31 -0
- package/dist/bundleValue.server.js +113 -0
- package/dist/canonicalDataJson.js +11 -0
- package/dist/capture.js +54 -0
- package/dist/codePort.fixture.js +124 -0
- package/dist/codeStore.server.js +440 -0
- package/dist/codeTables.entity.js +44 -0
- package/dist/codes.d.ts +150 -0
- package/dist/codes.js +83 -0
- package/dist/components.d.ts +397 -0
- package/dist/components.js +284 -0
- package/dist/consistency.server.js +116 -0
- package/dist/containerStore.server.js +688 -0
- package/dist/containerStore.testFixtures.js +76 -0
- package/dist/containerTables.entity.js +46 -0
- package/dist/containers.d.ts +311 -0
- package/dist/containers.fixture.js +129 -0
- package/dist/containers.js +215 -0
- package/dist/containers.server.js +40 -0
- package/dist/containersPort.fixture.js +75 -0
- package/dist/currency.js +33 -0
- package/dist/declarationFixtures.js +62 -0
- package/dist/economy-app/events/economy.refundReversed.event.ts +1 -0
- package/dist/economy-app/events/economy.refunded.event.ts +1 -0
- package/dist/economyPort.fixture.js +125 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +22 -0
- package/dist/events.d.ts +46 -0
- package/dist/events.js +1 -0
- package/dist/googleIap.server.js +392 -0
- package/dist/googleIapNotifications.server.js +131 -0
- package/dist/googleIapSubscription.server.js +184 -0
- package/dist/iap.d.ts +309 -0
- package/dist/iap.js +304 -0
- package/dist/iap.testFixtures.js +145 -0
- package/dist/iapAccountTables.entity.js +40 -0
- package/dist/iapAccounts.server.js +201 -0
- package/dist/iapBinding.server.js +524 -0
- package/dist/iapCheckout.server.js +98 -0
- package/dist/iapCompletion.server.js +56 -0
- package/dist/iapCurrentState.server.js +108 -0
- package/dist/iapCurrentState.testFixtures.js +23 -0
- package/dist/iapCurrentStatePort.fixture.js +64 -0
- package/dist/iapEntitlements.d.ts +79 -0
- package/dist/iapEntitlements.js +58 -0
- package/dist/iapEntitlements.server.js +49 -0
- package/dist/iapEntitlementsBinding.server.js +106 -0
- package/dist/iapEntitlementsPort.fixture.js +81 -0
- package/dist/iapInstalledEntitlements.testFixtures.js +76 -0
- package/dist/iapInstalledNotifications.testFixtures.js +94 -0
- package/dist/iapInventoryRefunds.server.js +262 -0
- package/dist/iapNotificationCustodyPort.fixture.js +68 -0
- package/dist/iapNotificationPort.fixture.js +74 -0
- package/dist/iapNotificationProcessor.server.js +135 -0
- package/dist/iapNotificationProcessor.testFixtures.js +64 -0
- package/dist/iapNotificationProcessorPort.fixture.js +56 -0
- package/dist/iapNotificationSchedule.server.js +70 -0
- package/dist/iapNotificationStore.server.js +353 -0
- package/dist/iapNotificationStore.testFixtures.js +32 -0
- package/dist/iapNotificationTables.entity.js +49 -0
- package/dist/iapNotificationTarget.server.js +37 -0
- package/dist/iapNotificationTarget.testFixtures.js +30 -0
- package/dist/iapNotifications.d.ts +644 -0
- package/dist/iapNotifications.js +6 -0
- package/dist/iapNotifications.server.js +96 -0
- package/dist/iapNotificationsBinding.server.js +233 -0
- package/dist/iapNotificationsSystemPort.fixture.js +51 -0
- package/dist/iapPlugin.testFixtures.js +60 -0
- package/dist/iapPort.fixture.js +75 -0
- package/dist/iapProviderCapture.server.js +18 -0
- package/dist/iapRecovery.d.ts +146 -0
- package/dist/iapRecovery.js +90 -0
- package/dist/iapRefundEventDelivery.server.js +83 -0
- package/dist/iapRefundEvents.js +45 -0
- package/dist/iapRefundEventsPort.fixture.js +60 -0
- package/dist/iapRefundPort.fixture.js +55 -0
- package/dist/iapRefundTables.entity.js +56 -0
- package/dist/iapRefunds.d.ts +755 -0
- package/dist/iapRefunds.js +6 -0
- package/dist/iapRefunds.server.js +554 -0
- package/dist/iapRefunds.testFixtures.js +65 -0
- package/dist/iapSpendAdmission.server.js +99 -0
- package/dist/iapStore.server.js +741 -0
- package/dist/iapStore.testFixtures.js +128 -0
- package/dist/iapTables.entity.js +81 -0
- package/dist/iapVerification.server.js +121 -0
- package/dist/index.d.ts +2984 -0
- package/dist/index.js +15 -0
- package/dist/intentionRecovery.js +134 -0
- package/dist/ledger.js +78 -0
- package/dist/loyalty.d.ts +274 -0
- package/dist/loyalty.fixture.js +93 -0
- package/dist/loyalty.js +312 -0
- package/dist/loyaltyBinding.server.js +204 -0
- package/dist/loyaltyPort.fixture.js +87 -0
- package/dist/loyaltyShopSource.server.js +91 -0
- package/dist/loyaltyStore.server.js +553 -0
- package/dist/loyaltyStore.testFixtures.js +83 -0
- package/dist/loyaltyTables.entity.js +50 -0
- package/dist/offerMerchandising.fixture.js +115 -0
- package/dist/offerMerchandising.js +283 -0
- package/dist/offerPolicies.d.ts +441 -0
- package/dist/offerPolicies.fixture.js +54 -0
- package/dist/offerPolicies.js +268 -0
- package/dist/piggyBanks.d.ts +272 -0
- package/dist/piggyBanks.fixture.js +71 -0
- package/dist/piggyBanks.js +201 -0
- package/dist/piggyBinding.server.js +205 -0
- package/dist/piggyPort.fixture.js +73 -0
- package/dist/piggyStore.server.js +611 -0
- package/dist/piggyStore.testFixtures.js +98 -0
- package/dist/piggyTables.entity.js +41 -0
- package/dist/plugin.js +1048 -0
- package/dist/port.js +13 -0
- package/dist/price.d.ts +99 -0
- package/dist/price.js +144 -0
- package/dist/purchaseRecovery.js +56 -0
- package/dist/reports.server.js +115 -0
- package/dist/rpc.d.ts +3201 -0
- package/dist/rpc.js +1201 -0
- package/dist/sanctionsTradeAuthority.server.js +80 -0
- package/dist/server.d.ts +3856 -0
- package/dist/server.js +43 -0
- package/dist/shop.d.ts +279 -0
- package/dist/shop.js +196 -0
- package/dist/shop.testFixtures.js +114 -0
- package/dist/shopPort.fixture.js +72 -0
- package/dist/shopPresentation.js +68 -0
- package/dist/shopStore.server.js +624 -0
- package/dist/shopTables.entity.js +63 -0
- package/dist/shopUiCopy.js +57 -0
- package/dist/spendLimits.d.ts +140 -0
- package/dist/spendLimits.fixture.js +53 -0
- package/dist/spendLimits.js +155 -0
- package/dist/spendLimitsPort.fixture.js +120 -0
- package/dist/subscriptionBinding.server.js +211 -0
- package/dist/subscriptionStore.server.js +643 -0
- package/dist/subscriptionStore.testFixtures.js +72 -0
- package/dist/subscriptionTables.entity.js +37 -0
- package/dist/subscriptions.d.ts +394 -0
- package/dist/subscriptions.fixture.js +97 -0
- package/dist/subscriptions.js +213 -0
- package/dist/subscriptionsPort.fixture.js +76 -0
- package/dist/systemFixtures.js +89 -0
- package/dist/tables.entity.js +83 -0
- package/dist/testing.d.ts +4615 -0
- package/dist/testing.js +29 -0
- package/dist/tradeAuthority.server.js +172 -0
- package/dist/tradeBinding.server.js +201 -0
- package/dist/tradeComponents.js +342 -0
- package/dist/tradeEscrow.server.js +232 -0
- package/dist/tradeEscrow.testFixtures.js +133 -0
- package/dist/tradeExpiry.server.js +43 -0
- package/dist/tradeRecovery.js +48 -0
- package/dist/tradeStore.server.js +750 -0
- package/dist/tradeStore.testFixtures.js +73 -0
- package/dist/tradeTables.entity.js +66 -0
- package/dist/tradeUiCopy.js +79 -0
- package/dist/trades.d.ts +246 -0
- package/dist/trades.fixture.js +103 -0
- package/dist/trades.js +289 -0
- package/dist/tradesPort.fixture.js +94 -0
- package/dist/walletEscrow.js +106 -0
- package/dist/walletEscrow.server.js +376 -0
- package/dist/walletKeys.server.js +5 -0
- package/dist/walletStore.js +571 -0
- package/dist/web.d.ts +2257 -0
- package/dist/web.js +9 -0
- package/package.json +161 -4
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { economyTables as e } from "./tables.entity.js";
|
|
2
|
+
import { CodePort as t, defaultCodeEligibility as n, defineCode as r, resolveCodeEligibility as i } from "./codes.js";
|
|
3
|
+
import { codeTables as a } from "./codeTables.entity.js";
|
|
4
|
+
import { EconomyPort as o } from "./port.js";
|
|
5
|
+
import { economyPlugin as s } from "./plugin.js";
|
|
6
|
+
import { Effect as c, Layer as l } from "effect";
|
|
7
|
+
import { systemSubject as u } from "@voltro/protocol";
|
|
8
|
+
import { randomBytes as d } from "node:crypto";
|
|
9
|
+
import { ensureTableRegistered as f } from "@voltro/database";
|
|
10
|
+
import { InMemoryDataStore as p } from "@voltro/runtime";
|
|
11
|
+
//#region src/codePort.fixture.ts
|
|
12
|
+
var m = () => {
|
|
13
|
+
let e = [{
|
|
14
|
+
id: "test.gold",
|
|
15
|
+
kind: "soft",
|
|
16
|
+
cap: "1000"
|
|
17
|
+
}];
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
currencies: e,
|
|
20
|
+
context: Object.freeze({
|
|
21
|
+
definition: r({
|
|
22
|
+
id: "fixture.gift",
|
|
23
|
+
campaign: "fixture.campaign",
|
|
24
|
+
currencies: [{
|
|
25
|
+
currency: "test.gold",
|
|
26
|
+
amount: "5"
|
|
27
|
+
}],
|
|
28
|
+
limits: {
|
|
29
|
+
global: 2,
|
|
30
|
+
perPlayer: 1
|
|
31
|
+
},
|
|
32
|
+
from: 0,
|
|
33
|
+
until: 864e5
|
|
34
|
+
}, e),
|
|
35
|
+
at: 1,
|
|
36
|
+
claims: 0,
|
|
37
|
+
playerClaims: 0
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
}, h = m(), g = Object.freeze({
|
|
41
|
+
kind: "slot",
|
|
42
|
+
defaultImplementation: n,
|
|
43
|
+
defaultProbe: {
|
|
44
|
+
run: () => c.sync(() => i(h.context, h.currencies)),
|
|
45
|
+
expected: !0
|
|
46
|
+
},
|
|
47
|
+
overrideProbe: {
|
|
48
|
+
run: () => c.sync(() => i(h.context, h.currencies, (e) => n(e) && e.claims > 0)),
|
|
49
|
+
expected: !1
|
|
50
|
+
}
|
|
51
|
+
}), _ = {
|
|
52
|
+
app: {
|
|
53
|
+
name: "codes-fixture",
|
|
54
|
+
type: "api",
|
|
55
|
+
voltroVersion: "0.83.1"
|
|
56
|
+
},
|
|
57
|
+
config: {},
|
|
58
|
+
env: {},
|
|
59
|
+
logger: {
|
|
60
|
+
info() {},
|
|
61
|
+
warn() {},
|
|
62
|
+
error() {},
|
|
63
|
+
debug() {}
|
|
64
|
+
}
|
|
65
|
+
}, v = c.gen(function* () {
|
|
66
|
+
yield* c.sync(() => [...e, ...a].forEach(f));
|
|
67
|
+
let t = new p(), n = s({
|
|
68
|
+
currencies: h.currencies,
|
|
69
|
+
now: () => 1,
|
|
70
|
+
codes: {}
|
|
71
|
+
});
|
|
72
|
+
yield* c.promise(async () => {
|
|
73
|
+
await n.bindDataStore?.(t);
|
|
74
|
+
});
|
|
75
|
+
let r = d(32).toString("hex"), i = Object.freeze({
|
|
76
|
+
code: r,
|
|
77
|
+
requestId: "fixture.redeem"
|
|
78
|
+
});
|
|
79
|
+
return yield* n.economy.codes().create(u("codes.fixture"), {
|
|
80
|
+
tenantId: "fixture",
|
|
81
|
+
code: r,
|
|
82
|
+
definition: h.context.definition
|
|
83
|
+
}), Object.freeze({
|
|
84
|
+
plugin: n,
|
|
85
|
+
store: t,
|
|
86
|
+
input: i,
|
|
87
|
+
close: c.suspend(() => n.onDeactivate?.(_))
|
|
88
|
+
});
|
|
89
|
+
}), y = l.effectContext(c.acquireRelease(v, (e) => e.close).pipe(c.flatMap((e) => l.build(e.plugin.services)))), b = Object.freeze({
|
|
90
|
+
kind: "port",
|
|
91
|
+
tag: t,
|
|
92
|
+
fakeLayer: y,
|
|
93
|
+
probe: {
|
|
94
|
+
run: () => c.scoped(c.gen(function* () {
|
|
95
|
+
let e = yield* t, n = yield* o, r = d(32).toString("hex"), i = {
|
|
96
|
+
code: r,
|
|
97
|
+
requestId: "fixture.probe"
|
|
98
|
+
}, a = {
|
|
99
|
+
type: "user",
|
|
100
|
+
id: "a",
|
|
101
|
+
tenantId: "fixture",
|
|
102
|
+
kind: "guest"
|
|
103
|
+
};
|
|
104
|
+
yield* e.create(u("codes.fixture"), {
|
|
105
|
+
tenantId: "fixture",
|
|
106
|
+
code: r,
|
|
107
|
+
definition: h.context.definition
|
|
108
|
+
});
|
|
109
|
+
let s = yield* e.redeem(a, i), c = yield* e.redeem(a, i), l = yield* e.receipt(a, i), f = yield* n.mine(a);
|
|
110
|
+
return {
|
|
111
|
+
same: s.operationId === c.operationId,
|
|
112
|
+
state: l.state,
|
|
113
|
+
balance: f[0]?.balance
|
|
114
|
+
};
|
|
115
|
+
}).pipe(c.provide(y))),
|
|
116
|
+
expected: {
|
|
117
|
+
same: !0,
|
|
118
|
+
state: "fulfilled",
|
|
119
|
+
balance: "5"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
//#endregion
|
|
124
|
+
export { m as codeEligibilityFixture, g as codeEligibilitySystemFixture, v as codePortFixture, b as codePortSystemFixture, y as codesFakeLayer };
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { EconomyError as e, economyError as t } from "./errors.js";
|
|
2
|
+
import { captureBoundMethods as n, dataList as r, dataObject as i, identifier as a } from "./capture.js";
|
|
3
|
+
import { positiveAmount as o } from "./amount.js";
|
|
4
|
+
import { captureCurrencies as s } from "./currency.js";
|
|
5
|
+
import { shopInteger as c } from "./shop.js";
|
|
6
|
+
import { captureCodeIntent as l, captureCodeValue as u, defaultCodeEligibility as d, defineCode as f, resolveCodeEligibility as p } from "./codes.js";
|
|
7
|
+
import { codeClaimsTable as m, codeSubjectsTable as h, codeTenantsTable as g, codesTable as _ } from "./codeTables.entity.js";
|
|
8
|
+
import { Effect as v, Result as y } from "effect";
|
|
9
|
+
import { systemSubject as b } from "@voltro/protocol";
|
|
10
|
+
import { createHash as x } from "node:crypto";
|
|
11
|
+
import { and as S, eq as C, queryFor as w } from "@voltro/database";
|
|
12
|
+
import { executeSqlMutations as ee, prepareSqlMutation as te, prepareSqlMutationGroup as ne } from "@voltro/database/sql";
|
|
13
|
+
import { InventoryError as T } from "@voltro/plugin-inventory/errors";
|
|
14
|
+
//#region src/codeStore.server.ts
|
|
15
|
+
var E = (e) => x("sha256").update(JSON.stringify(e)).digest("hex"), D = (e, ...t) => e + E(t).slice(0, 56), O = (n) => n instanceof e ? n : n instanceof T && [
|
|
16
|
+
"invalid_input",
|
|
17
|
+
"invalid_record",
|
|
18
|
+
"not_found",
|
|
19
|
+
"erased",
|
|
20
|
+
"forbidden",
|
|
21
|
+
"conflict",
|
|
22
|
+
"limit_exceeded"
|
|
23
|
+
].includes(n.code) ? t(n.code) : t("unavailable"), k = (e) => v.tryPromise({
|
|
24
|
+
try: e,
|
|
25
|
+
catch: O
|
|
26
|
+
}), A = (e) => {
|
|
27
|
+
try {
|
|
28
|
+
return e();
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return v.fail(O(e));
|
|
31
|
+
}
|
|
32
|
+
}, j = async (e) => {
|
|
33
|
+
let t = await v.runPromise(v.result(e));
|
|
34
|
+
if (y.isFailure(t)) throw t.failure;
|
|
35
|
+
return t.success;
|
|
36
|
+
}, M = (e) => {
|
|
37
|
+
let t = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
|
|
38
|
+
return c(t, 0, 864e13);
|
|
39
|
+
}, N = class extends Error {
|
|
40
|
+
constructor() {
|
|
41
|
+
super("Accepted code request replay"), this.name = "CodeReplay";
|
|
42
|
+
}
|
|
43
|
+
}, P = (e) => {
|
|
44
|
+
let o = i(e, [
|
|
45
|
+
"currencies",
|
|
46
|
+
"economy",
|
|
47
|
+
"inventory",
|
|
48
|
+
"systemAccount",
|
|
49
|
+
"allowedServices",
|
|
50
|
+
"maxHistoryRows",
|
|
51
|
+
"now",
|
|
52
|
+
"eligibility",
|
|
53
|
+
"version"
|
|
54
|
+
]), l = s(o.currencies), u = n(o.economy, ["prepareTransfer"]), f = o.inventory === void 0 ? void 0 : n(o.inventory, ["prepareGrant"]), p = a(o.systemAccount ?? "codes"), m = r(o.allowedServices ?? [], 256).map((e) => a(e));
|
|
55
|
+
if (new Set(m).size !== m.length) throw t("invalid_input");
|
|
56
|
+
let h = c(o.maxHistoryRows ?? 1e4, 1, 1e5), g = o.now ?? Date.now, _ = o.eligibility ?? d;
|
|
57
|
+
if (typeof _ != "function") throw t("invalid_input");
|
|
58
|
+
let v = a(o.version ?? (_ === d ? "default.v1" : void 0));
|
|
59
|
+
if (_ !== d && v === "default.v1" || typeof g != "function") throw t("invalid_input");
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
currencies: l,
|
|
62
|
+
economy: u,
|
|
63
|
+
inventory: f,
|
|
64
|
+
systemAccount: p,
|
|
65
|
+
allowedServices: Object.freeze(m),
|
|
66
|
+
maxHistoryRows: h,
|
|
67
|
+
now: g,
|
|
68
|
+
eligibility: _,
|
|
69
|
+
version: v
|
|
70
|
+
});
|
|
71
|
+
}, F = (e, n) => {
|
|
72
|
+
let v = P(n), { currencies: y, economy: x, inventory: T, systemAccount: O, eligibility: F, version: I } = v, L = v.maxHistoryRows, re = v.now, ie = new Set(v.allowedServices), R = () => c(re(), 0, 864e13), z = b("economy.codes"), B = (e) => {
|
|
73
|
+
if (e.type !== "user") throw t("forbidden");
|
|
74
|
+
return {
|
|
75
|
+
tenant: a(e.tenantId),
|
|
76
|
+
owner: a(e.id)
|
|
77
|
+
};
|
|
78
|
+
}, V = (e, t) => D("ecs_", e, t), H = (e, t) => D("ecd_", e, t), U = (e, t, n) => D("ecr_", e, t, n), W = (e, t) => E([e, t]), G = (t) => e.insertIgnore(g.tableName, {
|
|
79
|
+
id: D("ect_", t),
|
|
80
|
+
tenantId: t,
|
|
81
|
+
admission: 0
|
|
82
|
+
}, { conflictColumns: ["id"] }), K = (t, n) => e.insertIgnore(h.tableName, {
|
|
83
|
+
id: V(t, n),
|
|
84
|
+
tenantId: t,
|
|
85
|
+
ownerId: n,
|
|
86
|
+
erased: !1,
|
|
87
|
+
admission: 0
|
|
88
|
+
}, { conflictColumns: ["id"] }), q = async (e, n) => {
|
|
89
|
+
let r = D("ect_", n);
|
|
90
|
+
if (await e.updateMany(g.tableName, { admission: 0 }, { where: C("id", r) }) !== 1) throw t("unavailable");
|
|
91
|
+
let i = (await e.query(w(g).where(C("id", r)).limit(1).descriptor))[0];
|
|
92
|
+
if (!i || i.tenantId !== n || i.admission !== 0) throw t("invalid_record");
|
|
93
|
+
}, J = async (e, n, r, i = !1) => {
|
|
94
|
+
let a = V(n, r);
|
|
95
|
+
if (await e.updateMany(h.tableName, { admission: 0 }, { where: C("id", a) }) !== 1) throw t("unavailable");
|
|
96
|
+
let o = (await e.query(w(h).where(C("id", a)).limit(1).descriptor))[0];
|
|
97
|
+
if (!o || o.tenantId !== n || o.admission !== 0 || o.erased !== !0 && o.erased !== !1 || o.erased === !1 && o.ownerId !== r || o.erased === !0 && o.ownerId !== null) throw t("invalid_record");
|
|
98
|
+
if (o.erased === !0 && !i) throw t("erased");
|
|
99
|
+
}, Y = async (e, n, r, i) => {
|
|
100
|
+
let a = (await e.query(w(m).where(C("id", i)).limit(1).descriptor))[0];
|
|
101
|
+
if (a && (a.tenantId !== n || a.ownerKey !== V(n, r))) throw t("invalid_record");
|
|
102
|
+
return a;
|
|
103
|
+
}, X = (e) => {
|
|
104
|
+
if (e.state !== "fulfilled" || e.accepted !== !0 || e.ownerId === null) throw t("invalid_record");
|
|
105
|
+
let n = i(e.intent, [
|
|
106
|
+
"definition",
|
|
107
|
+
"currencies",
|
|
108
|
+
"requestId",
|
|
109
|
+
"codeDigest",
|
|
110
|
+
"rulesVersion",
|
|
111
|
+
"at"
|
|
112
|
+
]), l = f(n.definition, s(n.currencies));
|
|
113
|
+
if (a(n.rulesVersion), typeof n.codeDigest != "string" || !/^[a-f0-9]{64}$/.test(n.codeDigest) || e.ownerKey !== V(a(e.tenantId), a(e.ownerId)) || e.digest !== E([
|
|
114
|
+
e.tenantId,
|
|
115
|
+
e.ownerId,
|
|
116
|
+
n.requestId,
|
|
117
|
+
n.codeDigest
|
|
118
|
+
]) || e.id !== U(String(e.tenantId), String(e.ownerId), a(n.requestId)) || e.codeKey !== "ecd_" + String(n.codeDigest).slice(0, 56)) throw t("invalid_record");
|
|
119
|
+
let u = i(e.receipt, [
|
|
120
|
+
"operationId",
|
|
121
|
+
"codeId",
|
|
122
|
+
"version",
|
|
123
|
+
"campaign",
|
|
124
|
+
"at",
|
|
125
|
+
"transfers",
|
|
126
|
+
"grants"
|
|
127
|
+
]), d = c(u.at, M(e.createdAt), 864e13);
|
|
128
|
+
if (u.operationId !== e.id || u.codeId !== l.id || u.version !== l.version || u.campaign !== l.campaign || d !== n.at || M(e.fulfilledAt) !== d || l.from !== null && d < l.from || l.until !== null && d >= l.until) throw t("invalid_record");
|
|
129
|
+
let p = Object.freeze(r(u.transfers, 16).map((n, r) => {
|
|
130
|
+
let s = i(n, [
|
|
131
|
+
"txId",
|
|
132
|
+
"currency",
|
|
133
|
+
"amount",
|
|
134
|
+
"at"
|
|
135
|
+
]), u = l.currencies[r];
|
|
136
|
+
if (!u || s.currency !== u.currency || s.amount !== u.amount || typeof s.amount != "string") throw t("invalid_record");
|
|
137
|
+
return Object.freeze({
|
|
138
|
+
txId: a(s.txId),
|
|
139
|
+
currency: u.currency,
|
|
140
|
+
amount: o(s.amount).toString(),
|
|
141
|
+
at: c(s.at, M(e.createdAt), d)
|
|
142
|
+
});
|
|
143
|
+
})), m = Object.freeze(r(u.grants, 16).map((n, o) => {
|
|
144
|
+
let s = i(n, [
|
|
145
|
+
"operationId",
|
|
146
|
+
"kind",
|
|
147
|
+
"changes",
|
|
148
|
+
"at"
|
|
149
|
+
]), u = l.items[o];
|
|
150
|
+
if (!u || s.kind !== "grant") throw t("invalid_record");
|
|
151
|
+
let f = Object.freeze(r(s.changes, 1e4).map((e) => {
|
|
152
|
+
let t = i(e, ["instanceId", "delta"]);
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
instanceId: a(t.instanceId),
|
|
155
|
+
delta: c(t.delta, 1, 1e6)
|
|
156
|
+
});
|
|
157
|
+
}));
|
|
158
|
+
if (f.reduce((e, t) => e + t.delta, 0) !== u.qty || new Set(f.map((e) => e.instanceId)).size !== f.length) throw t("invalid_record");
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
operationId: a(s.operationId),
|
|
161
|
+
kind: "grant",
|
|
162
|
+
changes: f,
|
|
163
|
+
at: c(s.at, M(e.createdAt), d)
|
|
164
|
+
});
|
|
165
|
+
}));
|
|
166
|
+
if (p.length !== l.currencies.length || m.length !== l.items.length || new Set(p.map((e) => e.txId)).size !== p.length || new Set(m.map((e) => e.operationId)).size !== m.length) throw t("invalid_record");
|
|
167
|
+
return Object.freeze({
|
|
168
|
+
operationId: a(e.id),
|
|
169
|
+
codeId: l.id,
|
|
170
|
+
version: l.version,
|
|
171
|
+
campaign: l.campaign,
|
|
172
|
+
at: d,
|
|
173
|
+
transfers: p,
|
|
174
|
+
grants: m
|
|
175
|
+
});
|
|
176
|
+
}, Z = async (e, n, r) => {
|
|
177
|
+
let i = await e.query(w(m).where(S(C("tenantId", n), r)).limit(L + 1).descriptor);
|
|
178
|
+
if (i.length > L) throw t("limit_exceeded");
|
|
179
|
+
for (let e of i) {
|
|
180
|
+
if (!/^ecr_[a-f0-9]{56}$/.test(String(e.id)) || !/^ecs_[a-f0-9]{56}$/.test(String(e.ownerKey)) || !/^ecd_[a-f0-9]{56}$/.test(String(e.codeKey)) || e.tenantId !== n || typeof e.accepted != "boolean" || ![
|
|
181
|
+
"pending",
|
|
182
|
+
"fulfilled",
|
|
183
|
+
"erased"
|
|
184
|
+
].includes(String(e.state))) throw t("invalid_record");
|
|
185
|
+
if (e.state === "fulfilled") X(e);
|
|
186
|
+
else if (e.state === "pending" && (e.accepted !== !1 || e.receipt !== null || e.fulfilledAt !== null)) throw t("invalid_record");
|
|
187
|
+
else if (e.state === "erased" && (e.ownerId !== null || e.digest !== null || e.intent !== null || e.receipt !== null || e.accepted && e.fulfilledAt === null || !e.accepted && e.fulfilledAt !== null)) throw t("invalid_record");
|
|
188
|
+
}
|
|
189
|
+
return i;
|
|
190
|
+
}, Q = (n, r, i, a) => e.transactional(async (e) => {
|
|
191
|
+
await q(e, n), await J(e, n, r);
|
|
192
|
+
let o = await Y(e, n, r, i);
|
|
193
|
+
if (o) {
|
|
194
|
+
if (o.digest !== a) throw t("conflict");
|
|
195
|
+
if (o.state === "fulfilled") return X(o);
|
|
196
|
+
if (o.state !== "pending" || o.accepted !== !1) throw t("invalid_record");
|
|
197
|
+
}
|
|
198
|
+
}), ae = (n, r) => A(() => {
|
|
199
|
+
if (n.type !== "system" && (n.type !== "service" || !ie.has(n.service))) throw t("forbidden");
|
|
200
|
+
let o = i(r, [
|
|
201
|
+
"tenantId",
|
|
202
|
+
"code",
|
|
203
|
+
"definition"
|
|
204
|
+
]), s = a(o.tenantId), c = u(o.code), l = f(o.definition, y);
|
|
205
|
+
if (l.items.length && !T) throw t("unavailable");
|
|
206
|
+
let d = H(s, c), p = W(s, c);
|
|
207
|
+
return k(async () => {
|
|
208
|
+
let n = R();
|
|
209
|
+
await G(s), await e.transactional(async (e) => {
|
|
210
|
+
await q(e, s);
|
|
211
|
+
let r = (await e.query(w(_).where(C("id", d)).limit(1).descriptor))[0];
|
|
212
|
+
if (r) {
|
|
213
|
+
if (r.tenantId !== s || r.codeDigest !== p || r.rulesVersion !== I || JSON.stringify(f(r.definition, y)) !== JSON.stringify(l)) throw t("conflict");
|
|
214
|
+
} else await e.insert(_.tableName, {
|
|
215
|
+
id: d,
|
|
216
|
+
tenantId: s,
|
|
217
|
+
codeDigest: p,
|
|
218
|
+
definition: l,
|
|
219
|
+
rulesVersion: I,
|
|
220
|
+
createdAt: new Date(n)
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}), oe = (n, r) => A(() => {
|
|
225
|
+
let { tenant: i, owner: a } = B(n), o = l(r), s = U(i, a, o.requestId), c = H(i, o.code), u = W(i, o.code), h = E([
|
|
226
|
+
i,
|
|
227
|
+
a,
|
|
228
|
+
o.requestId,
|
|
229
|
+
u
|
|
230
|
+
]), g = o.requestId;
|
|
231
|
+
return k(async () => {
|
|
232
|
+
await G(i), await K(i, a);
|
|
233
|
+
let n = await Q(i, a, s, h);
|
|
234
|
+
if (n) return n;
|
|
235
|
+
let r = R(), o = await e.transactional(async (e) => {
|
|
236
|
+
await q(e, i), await J(e, i, a);
|
|
237
|
+
let n = (await e.query(w(_).where(C("id", c)).limit(1).descriptor))[0];
|
|
238
|
+
if (!n || n.tenantId !== i || n.codeDigest !== u) throw t("not_found");
|
|
239
|
+
if (n.rulesVersion !== I) throw t("conflict");
|
|
240
|
+
if (M(n.createdAt) > r) throw t("invalid_record");
|
|
241
|
+
let o = f(n.definition, y);
|
|
242
|
+
if (o.from !== null && r < o.from || o.until !== null && r >= o.until) throw t("forbidden");
|
|
243
|
+
let l = await Z(e, i, C("ownerKey", V(i, a))), d = await Y(e, i, a, s);
|
|
244
|
+
if (!d) {
|
|
245
|
+
if (l.length >= L) throw t("limit_exceeded");
|
|
246
|
+
await e.insert(m.tableName, {
|
|
247
|
+
id: s,
|
|
248
|
+
tenantId: i,
|
|
249
|
+
ownerId: a,
|
|
250
|
+
ownerKey: V(i, a),
|
|
251
|
+
codeKey: c,
|
|
252
|
+
state: "pending",
|
|
253
|
+
accepted: !1,
|
|
254
|
+
digest: h,
|
|
255
|
+
intent: null,
|
|
256
|
+
receipt: null,
|
|
257
|
+
createdAt: new Date(r),
|
|
258
|
+
fulfilledAt: null
|
|
259
|
+
});
|
|
260
|
+
} else if (d.digest !== h || d.codeKey !== c) throw t("conflict");
|
|
261
|
+
let p = (await Z(e, i, C("codeKey", c))).filter((e) => e.accepted);
|
|
262
|
+
return Object.freeze({
|
|
263
|
+
definition: o,
|
|
264
|
+
claims: p.length,
|
|
265
|
+
playerClaims: p.filter((e) => e.ownerKey === V(i, a)).length,
|
|
266
|
+
at: r
|
|
267
|
+
});
|
|
268
|
+
}), l = o.definition;
|
|
269
|
+
if (o.claims >= l.limits.global || o.playerClaims >= l.limits.perPlayer) throw t("limit_exceeded");
|
|
270
|
+
if (!p(o, y, F)) throw t("forbidden");
|
|
271
|
+
let v = [];
|
|
272
|
+
v.push(te(e, async (e) => {
|
|
273
|
+
await q(e, i), await J(e, i, a);
|
|
274
|
+
let n = await Y(e, i, a, s);
|
|
275
|
+
if (!n || n.digest !== h || n.codeKey !== c || n.ownerId !== a) throw t("invalid_record");
|
|
276
|
+
if (n.state === "fulfilled") throw X(n), new N();
|
|
277
|
+
if (n.state !== "pending" || n.accepted !== !1) throw t("invalid_record");
|
|
278
|
+
let r = (await Z(e, i, C("codeKey", c))).filter((e) => e.accepted);
|
|
279
|
+
if (F !== d && (r.length !== o.claims || r.filter((e) => e.ownerKey === V(i, a)).length !== o.playerClaims)) throw t("conflict");
|
|
280
|
+
if (r.length >= l.limits.global || r.filter((e) => e.ownerKey === V(i, a)).length >= l.limits.perPlayer) throw t("limit_exceeded");
|
|
281
|
+
return null;
|
|
282
|
+
}));
|
|
283
|
+
for (let [e, t] of l.currencies.entries()) v.push(await j(x.prepareTransfer(z, {
|
|
284
|
+
tenantId: i,
|
|
285
|
+
requestId: D("eck_", s, "transfer", String(e)),
|
|
286
|
+
currency: t.currency,
|
|
287
|
+
amount: t.amount,
|
|
288
|
+
from: {
|
|
289
|
+
kind: "system",
|
|
290
|
+
id: O
|
|
291
|
+
},
|
|
292
|
+
to: {
|
|
293
|
+
kind: "wallet",
|
|
294
|
+
ownerKind: "player",
|
|
295
|
+
ownerId: a
|
|
296
|
+
},
|
|
297
|
+
reason: "code.redeem",
|
|
298
|
+
ref: {
|
|
299
|
+
kind: "code-claim",
|
|
300
|
+
id: s
|
|
301
|
+
}
|
|
302
|
+
})));
|
|
303
|
+
if (l.items.length && !T) throw t("unavailable");
|
|
304
|
+
for (let [e, t] of l.items.entries()) v.push(await j(T.prepareGrant(z, {
|
|
305
|
+
tenantId: i,
|
|
306
|
+
owner: {
|
|
307
|
+
ownerKind: "player",
|
|
308
|
+
ownerId: a
|
|
309
|
+
},
|
|
310
|
+
itemId: t.itemId,
|
|
311
|
+
qty: t.qty,
|
|
312
|
+
requestId: D("eck_", s, "grant", String(e)),
|
|
313
|
+
reason: "code.redeem",
|
|
314
|
+
ref: {
|
|
315
|
+
kind: "code-claim",
|
|
316
|
+
id: s
|
|
317
|
+
}
|
|
318
|
+
})));
|
|
319
|
+
let b = R();
|
|
320
|
+
if (b < r || l.from !== null && b < l.from || l.until !== null && b >= l.until) throw t("forbidden");
|
|
321
|
+
let S = ne(e, v, async (e, n) => {
|
|
322
|
+
let r = {
|
|
323
|
+
operationId: s,
|
|
324
|
+
codeId: l.id,
|
|
325
|
+
version: l.version,
|
|
326
|
+
campaign: l.campaign,
|
|
327
|
+
at: b,
|
|
328
|
+
transfers: n.slice(1, 1 + l.currencies.length),
|
|
329
|
+
grants: n.slice(1 + l.currencies.length)
|
|
330
|
+
};
|
|
331
|
+
await e.update(m.tableName, s, {
|
|
332
|
+
state: "fulfilled",
|
|
333
|
+
accepted: !0,
|
|
334
|
+
intent: {
|
|
335
|
+
definition: l,
|
|
336
|
+
currencies: y,
|
|
337
|
+
requestId: g,
|
|
338
|
+
codeDigest: u,
|
|
339
|
+
rulesVersion: I,
|
|
340
|
+
at: b
|
|
341
|
+
},
|
|
342
|
+
receipt: r,
|
|
343
|
+
fulfilledAt: new Date(b)
|
|
344
|
+
});
|
|
345
|
+
let o = await Y(e, i, a, s);
|
|
346
|
+
if (!o) throw t("invalid_record");
|
|
347
|
+
return X(o);
|
|
348
|
+
});
|
|
349
|
+
try {
|
|
350
|
+
return (await ee(e, [S]))[0];
|
|
351
|
+
} catch (e) {
|
|
352
|
+
if (e instanceof N) {
|
|
353
|
+
let e = await Q(i, a, s, h);
|
|
354
|
+
if (e) return e;
|
|
355
|
+
}
|
|
356
|
+
throw e;
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}), se = (n, r) => A(() => {
|
|
360
|
+
let { tenant: i, owner: a } = B(n), o = l(r), s = U(i, a, o.requestId), c = E([
|
|
361
|
+
i,
|
|
362
|
+
a,
|
|
363
|
+
o.requestId,
|
|
364
|
+
W(i, o.code)
|
|
365
|
+
]), u = o.requestId;
|
|
366
|
+
return k(async () => e.transactional(async (e) => {
|
|
367
|
+
let n = (await e.query(w(h).where(C("id", V(i, a))).limit(1).descriptor))[0];
|
|
368
|
+
if (n && n.tenantId !== i) throw t("invalid_record");
|
|
369
|
+
if (n?.erased === !0) throw n.ownerId === null ? t("erased") : t("invalid_record");
|
|
370
|
+
if (n && (n.tenantId !== i || n.ownerId !== a || n.erased !== !1)) throw t("invalid_record");
|
|
371
|
+
let r = await Y(e, i, a, s);
|
|
372
|
+
if (!r) return Object.freeze({
|
|
373
|
+
requestId: u,
|
|
374
|
+
state: "not-observed"
|
|
375
|
+
});
|
|
376
|
+
if (r.digest !== c) throw t("conflict");
|
|
377
|
+
if (r.state === "fulfilled") return Object.freeze({
|
|
378
|
+
requestId: u,
|
|
379
|
+
state: "fulfilled",
|
|
380
|
+
receipt: X(r)
|
|
381
|
+
});
|
|
382
|
+
if (r.state !== "pending" || r.accepted !== !1) throw t("invalid_record");
|
|
383
|
+
return Object.freeze({
|
|
384
|
+
requestId: u,
|
|
385
|
+
state: "pending"
|
|
386
|
+
});
|
|
387
|
+
}));
|
|
388
|
+
}), $ = (e) => {
|
|
389
|
+
let n = i(e, [
|
|
390
|
+
"tenantId",
|
|
391
|
+
"subjectId",
|
|
392
|
+
"kind"
|
|
393
|
+
]);
|
|
394
|
+
if (n.kind !== "guest" && n.kind !== "user") throw t("invalid_input");
|
|
395
|
+
return {
|
|
396
|
+
tenant: a(n.tenantId),
|
|
397
|
+
owner: a(n.subjectId)
|
|
398
|
+
};
|
|
399
|
+
};
|
|
400
|
+
return Object.freeze({
|
|
401
|
+
create: ae,
|
|
402
|
+
redeem: oe,
|
|
403
|
+
receipt: se,
|
|
404
|
+
exportSubject: (n) => A(() => {
|
|
405
|
+
let { tenant: r, owner: i } = $(n);
|
|
406
|
+
return k(async () => e.transactional(async (e) => {
|
|
407
|
+
let n = (await e.query(w(h).where(C("id", V(r, i))).limit(1).descriptor))[0];
|
|
408
|
+
if (n && n.tenantId !== r) throw t("invalid_record");
|
|
409
|
+
if (n?.erased === !0) {
|
|
410
|
+
if (n.ownerId !== null) throw t("invalid_record");
|
|
411
|
+
return { claims: [] };
|
|
412
|
+
}
|
|
413
|
+
if (n && (n.tenantId !== r || n.ownerId !== i || n.erased !== !1)) throw t("invalid_record");
|
|
414
|
+
return { claims: await Z(e, r, C("ownerKey", V(r, i))) };
|
|
415
|
+
}));
|
|
416
|
+
}),
|
|
417
|
+
eraseSubject: (t) => A(() => {
|
|
418
|
+
let { tenant: n, owner: r } = $(t);
|
|
419
|
+
return k(async () => {
|
|
420
|
+
await G(n), await K(n, r), await e.transactional(async (e) => {
|
|
421
|
+
await q(e, n), await J(e, n, r, !0);
|
|
422
|
+
let t = await Z(e, n, C("ownerKey", V(n, r)));
|
|
423
|
+
for (let n of t) await e.update(m.tableName, String(n.id), {
|
|
424
|
+
ownerId: null,
|
|
425
|
+
state: "erased",
|
|
426
|
+
digest: null,
|
|
427
|
+
intent: null,
|
|
428
|
+
receipt: null
|
|
429
|
+
});
|
|
430
|
+
await e.update(h.tableName, V(n, r), {
|
|
431
|
+
ownerId: null,
|
|
432
|
+
erased: !0
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
})
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
//#endregion
|
|
440
|
+
export { F as dataStoreCodes, P as resolveCodeStoreOptions };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { boolean as e, id as t, integer as n, json as r, table as i, text as a, timestamp as o } from "@voltro/database";
|
|
2
|
+
//#region src/codeTables.entity.ts
|
|
3
|
+
var s = () => a().maxLength(120).externalId().sensitive("pii"), c = i("_voltro_eco_code_tenants", {
|
|
4
|
+
id: t({ prefix: "ect" }),
|
|
5
|
+
tenantId: s(),
|
|
6
|
+
admission: n().serverOnly()
|
|
7
|
+
}).index(["tenantId"]), l = i("_voltro_eco_codes", {
|
|
8
|
+
id: t({ prefix: "ecd" }),
|
|
9
|
+
tenantId: s(),
|
|
10
|
+
codeDigest: a().maxLength(64).serverOnly(),
|
|
11
|
+
definition: r().serverOnly(),
|
|
12
|
+
rulesVersion: a().maxLength(120).serverOnly(),
|
|
13
|
+
createdAt: o().serverOnly()
|
|
14
|
+
}).index(["tenantId"]), u = i("_voltro_eco_code_subjects", {
|
|
15
|
+
id: t({ prefix: "ecs" }),
|
|
16
|
+
tenantId: s(),
|
|
17
|
+
ownerId: a().maxLength(120).nullable().externalId().sensitive("pii"),
|
|
18
|
+
erased: e().serverOnly(),
|
|
19
|
+
admission: n().serverOnly()
|
|
20
|
+
}).index(["tenantId", "ownerId"]), d = i("_voltro_eco_code_claims", {
|
|
21
|
+
id: t({ prefix: "ecr" }),
|
|
22
|
+
tenantId: s(),
|
|
23
|
+
ownerId: a().maxLength(120).nullable().externalId().sensitive("pii"),
|
|
24
|
+
ownerKey: a().maxLength(64).serverOnly(),
|
|
25
|
+
codeKey: a().maxLength(64).serverOnly(),
|
|
26
|
+
state: a().maxLength(16).oneOf([
|
|
27
|
+
"pending",
|
|
28
|
+
"fulfilled",
|
|
29
|
+
"erased"
|
|
30
|
+
]).serverOnly(),
|
|
31
|
+
accepted: e().serverOnly(),
|
|
32
|
+
digest: a().maxLength(64).nullable().serverOnly(),
|
|
33
|
+
intent: r().nullable().serverOnly(),
|
|
34
|
+
receipt: r().nullable().serverOnly(),
|
|
35
|
+
createdAt: o().serverOnly(),
|
|
36
|
+
fulfilledAt: o().nullable().serverOnly()
|
|
37
|
+
}).index(["tenantId", "ownerKey"]).index(["tenantId", "codeKey"]), f = [
|
|
38
|
+
c,
|
|
39
|
+
l,
|
|
40
|
+
u,
|
|
41
|
+
d
|
|
42
|
+
];
|
|
43
|
+
//#endregion
|
|
44
|
+
export { d as codeClaimsTable, u as codeSubjectsTable, f as codeTables, c as codeTenantsTable, l as codesTable };
|