@voltro/plugin-saves 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 +90 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +5007 -0
- package/dist/contract.js +79 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.js +25 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +5 -0
- package/dist/inspect.d.ts +82 -0
- package/dist/inspect.js +49 -0
- package/dist/plugin.js +336 -0
- package/dist/port.d.ts +103 -0
- package/dist/port.js +34 -0
- package/dist/rpc.d.ts +128 -0
- package/dist/rpc.js +86 -0
- package/dist/server.d.ts +286 -0
- package/dist/server.js +7 -0
- package/dist/service.server.js +413 -0
- package/dist/strategies.d.ts +76 -0
- package/dist/strategies.js +48 -0
- package/dist/tables.entity.js +61 -0
- package/dist/testing.d.ts +213 -0
- package/dist/testing.js +117 -0
- package/dist/web.d.ts +116 -0
- package/dist/web.js +5 -0
- package/package.json +88 -4
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { SaveConflict as e, SavesError as t, savesError as n } from "./errors.js";
|
|
2
|
+
import { captureSavePut as r, saveData as i, saveId as a, saveInteger as o, saveOwner as s } from "./contract.js";
|
|
3
|
+
import { SavesPort as c, captureSavesService as l } from "./port.js";
|
|
4
|
+
import { defaultSaveValidator as u, resolveSavesOptions as d } from "./strategies.js";
|
|
5
|
+
import { savesTables as f } from "./tables.entity.js";
|
|
6
|
+
import { dataStoreSaves as p } from "./service.server.js";
|
|
7
|
+
import { savesGetDescriptor as m, savesHistoryDescriptor as h, savesListDescriptor as g, savesPutDescriptor as _, savesRemoveDescriptor as v, savesRollbackDescriptor as y, savesRpcClientImports as b } from "./rpc.js";
|
|
8
|
+
import { defineLicensedGamePlugin as x } from "@voltro/plugin-licensing/gate";
|
|
9
|
+
import { replicaEvidence as S } from "@voltro/protocol/identity";
|
|
10
|
+
import { ConnectionRegistry as C } from "@voltro/kv/connection";
|
|
11
|
+
import { HotStore as w, layerRoutedHotRedis as T } from "@voltro/kv/hot";
|
|
12
|
+
import { Effect as E, Layer as D, ManagedRuntime as O } from "effect";
|
|
13
|
+
//#region src/plugin.ts
|
|
14
|
+
var k = Object.freeze({
|
|
15
|
+
slots: { validator: {
|
|
16
|
+
defaultExport: "defaultSaveValidator",
|
|
17
|
+
fixtureExport: "saveValidatorSystemFixture"
|
|
18
|
+
} },
|
|
19
|
+
ports: { saves: {
|
|
20
|
+
tagExport: "SavesPort",
|
|
21
|
+
fakeLayerExport: "savesFakeLayer",
|
|
22
|
+
fixtureExport: "savesPortSystemFixture"
|
|
23
|
+
} },
|
|
24
|
+
events: []
|
|
25
|
+
}), A = (k) => {
|
|
26
|
+
let A = [
|
|
27
|
+
"service",
|
|
28
|
+
"querySources",
|
|
29
|
+
"connection",
|
|
30
|
+
"hotStore"
|
|
31
|
+
], j = i(k, [
|
|
32
|
+
"gameId",
|
|
33
|
+
"slots",
|
|
34
|
+
"maxBytes",
|
|
35
|
+
"historyVersions",
|
|
36
|
+
"maxRequests",
|
|
37
|
+
"deleteGraceMs",
|
|
38
|
+
"version",
|
|
39
|
+
"validator",
|
|
40
|
+
"schema",
|
|
41
|
+
"schemaVersion",
|
|
42
|
+
"now",
|
|
43
|
+
...A
|
|
44
|
+
]), M = Object.fromEntries(Object.entries(j).filter(([e]) => !A.includes(e))), N = d(M), P = j.service === void 0 ? void 0 : l(j.service);
|
|
45
|
+
if (P && (N.version === "default.v1" || [
|
|
46
|
+
"maxBytes",
|
|
47
|
+
"historyVersions",
|
|
48
|
+
"maxRequests",
|
|
49
|
+
"deleteGraceMs",
|
|
50
|
+
"validator",
|
|
51
|
+
"schema",
|
|
52
|
+
"schemaVersion",
|
|
53
|
+
"now",
|
|
54
|
+
"connection",
|
|
55
|
+
"hotStore"
|
|
56
|
+
].some((e) => j[e] !== void 0)) || !P && j.querySources !== void 0) throw n("invalid_input");
|
|
57
|
+
let F;
|
|
58
|
+
if (P) {
|
|
59
|
+
let e = j.querySources;
|
|
60
|
+
if (!Array.isArray(e) || e.length < 1 || e.length > 32 || Reflect.ownKeys(e).length !== e.length + 1) throw n("invalid_input");
|
|
61
|
+
let t = [];
|
|
62
|
+
for (let r = 0; r < e.length; r++) {
|
|
63
|
+
let i = Object.getOwnPropertyDescriptor(e, String(r));
|
|
64
|
+
if (!i || !("value" in i) || typeof i.value != "string" || !/^[A-Za-z_][A-Za-z0-9_]{0,119}$/.test(i.value)) throw n("invalid_input");
|
|
65
|
+
t.push(i.value);
|
|
66
|
+
}
|
|
67
|
+
if (new Set(t).size !== t.length) throw n("invalid_input");
|
|
68
|
+
F = Object.freeze(t);
|
|
69
|
+
}
|
|
70
|
+
if (j.connection !== void 0 && (typeof j.connection != "string" || !/^redis:[A-Za-z][A-Za-z0-9_-]*$/.test(j.connection)) || j.hotStore !== void 0 && j.connection !== void 0) throw n("invalid_input");
|
|
71
|
+
let I = j.hotStore;
|
|
72
|
+
if (I !== void 0 && typeof I.rateLimit != "function") throw n("invalid_input");
|
|
73
|
+
let L, R, z = !1, B = !1, V = 0, H, U, W, G, K = () => E.fail(n("unavailable")), q = (e) => {
|
|
74
|
+
let t = P ?? R, n = V;
|
|
75
|
+
return E.suspend(() => z && B && n === V && t && t === (P ?? R) ? e(t).pipe(E.flatMap((e) => z && B && n === V && t === (P ?? R) ? E.succeed(e) : K())) : K()).pipe(E.catchDefect(() => K()));
|
|
76
|
+
}, J = (r) => {
|
|
77
|
+
try {
|
|
78
|
+
return r();
|
|
79
|
+
} catch (r) {
|
|
80
|
+
return E.fail(r instanceof t || r instanceof e ? r : n("invalid_input"));
|
|
81
|
+
}
|
|
82
|
+
}, Y = (e) => {
|
|
83
|
+
let t = a(e);
|
|
84
|
+
if (!N.slots.includes(t)) throw n("not_found");
|
|
85
|
+
return t;
|
|
86
|
+
}, X = (e) => {
|
|
87
|
+
let t = i(e, [
|
|
88
|
+
"tenantId",
|
|
89
|
+
"subjectId",
|
|
90
|
+
"kind"
|
|
91
|
+
]);
|
|
92
|
+
if (t.kind !== "guest" && t.kind !== "user") throw n("invalid_input");
|
|
93
|
+
return Object.freeze({
|
|
94
|
+
tenantId: a(t.tenantId),
|
|
95
|
+
subjectId: a(t.subjectId),
|
|
96
|
+
kind: t.kind
|
|
97
|
+
});
|
|
98
|
+
}, Z = Object.freeze({
|
|
99
|
+
get: (e, t) => J(() => {
|
|
100
|
+
s(e);
|
|
101
|
+
let n = structuredClone(e), r = Y(t);
|
|
102
|
+
return q((e) => e.get(n, r));
|
|
103
|
+
}),
|
|
104
|
+
list: (e) => J(() => {
|
|
105
|
+
s(e);
|
|
106
|
+
let t = structuredClone(e);
|
|
107
|
+
return q((e) => e.list(t));
|
|
108
|
+
}),
|
|
109
|
+
history: (e, t) => J(() => {
|
|
110
|
+
s(e);
|
|
111
|
+
let n = structuredClone(e), r = Y(t);
|
|
112
|
+
return q((e) => e.history(n, r));
|
|
113
|
+
}),
|
|
114
|
+
put: (e, t) => J(() => {
|
|
115
|
+
s(e);
|
|
116
|
+
let n = structuredClone(e), i = r(t, N.maxBytes);
|
|
117
|
+
return Y(i.slot), q((e) => e.put(n, i));
|
|
118
|
+
}),
|
|
119
|
+
rollback: (e, t) => J(() => {
|
|
120
|
+
s(e);
|
|
121
|
+
let n = structuredClone(e), r = i(t, [
|
|
122
|
+
"slot",
|
|
123
|
+
"requestId",
|
|
124
|
+
"baseVersion",
|
|
125
|
+
"version"
|
|
126
|
+
]), c = Object.freeze({
|
|
127
|
+
slot: Y(r.slot),
|
|
128
|
+
requestId: a(r.requestId),
|
|
129
|
+
baseVersion: o(r.baseVersion),
|
|
130
|
+
version: o(r.version, 1)
|
|
131
|
+
});
|
|
132
|
+
return q((e) => e.rollback(n, c));
|
|
133
|
+
}),
|
|
134
|
+
remove: (e, t) => J(() => {
|
|
135
|
+
s(e);
|
|
136
|
+
let n = structuredClone(e), r = i(t, [
|
|
137
|
+
"slot",
|
|
138
|
+
"requestId",
|
|
139
|
+
"baseVersion"
|
|
140
|
+
]), c = Object.freeze({
|
|
141
|
+
slot: Y(r.slot),
|
|
142
|
+
requestId: a(r.requestId),
|
|
143
|
+
baseVersion: o(r.baseVersion)
|
|
144
|
+
});
|
|
145
|
+
return q((e) => e.remove(n, c));
|
|
146
|
+
}),
|
|
147
|
+
exportSubject: (e) => J(() => {
|
|
148
|
+
let t = X(e);
|
|
149
|
+
return q((e) => e.exportSubject(t));
|
|
150
|
+
}),
|
|
151
|
+
eraseSubject: (e) => J(() => {
|
|
152
|
+
let t = X(e);
|
|
153
|
+
return q((e) => e.eraseSubject(t));
|
|
154
|
+
})
|
|
155
|
+
}), Q = P ? [] : [...f], $ = (e) => F ? {
|
|
156
|
+
...e,
|
|
157
|
+
source: F
|
|
158
|
+
} : e;
|
|
159
|
+
return {
|
|
160
|
+
saves: () => Z,
|
|
161
|
+
...x({
|
|
162
|
+
name: "@voltro/plugin-saves",
|
|
163
|
+
description: "Owner-scoped opaque saves with SQL versions, original-intent replay, history and privacy.",
|
|
164
|
+
permissions: ["store:write", "inspect:read"],
|
|
165
|
+
services: D.succeed(c, Z),
|
|
166
|
+
...!P && !I ? {
|
|
167
|
+
needs: {
|
|
168
|
+
hot: "local",
|
|
169
|
+
connections: [{
|
|
170
|
+
kind: "redis",
|
|
171
|
+
role: "hot",
|
|
172
|
+
onUnavailable: "fail"
|
|
173
|
+
}]
|
|
174
|
+
},
|
|
175
|
+
...j.connection === void 0 ? {} : { connectionBindings: { hot: j.connection } }
|
|
176
|
+
} : {},
|
|
177
|
+
routes: [
|
|
178
|
+
{
|
|
179
|
+
...$(m),
|
|
180
|
+
execute: (e, t) => Z.get(t.request.subject, e.slot)
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
...$(g),
|
|
184
|
+
execute: (e, t) => Z.list(t.request.subject)
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
...$(h),
|
|
188
|
+
execute: (e, t) => Z.history(t.request.subject, e.slot)
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
..._,
|
|
192
|
+
execute: (e, t) => Z.put(t.request.subject, e)
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
...y,
|
|
196
|
+
execute: (e, t) => Z.rollback(t.request.subject, e)
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
...v,
|
|
200
|
+
execute: (e, t) => Z.remove(t.request.subject, e)
|
|
201
|
+
}
|
|
202
|
+
],
|
|
203
|
+
rpcClientDescriptors: b,
|
|
204
|
+
errorSchemas: [{
|
|
205
|
+
schema: t,
|
|
206
|
+
import: {
|
|
207
|
+
module: "@voltro/plugin-saves/errors",
|
|
208
|
+
name: "SavesError"
|
|
209
|
+
}
|
|
210
|
+
}, {
|
|
211
|
+
schema: e,
|
|
212
|
+
import: {
|
|
213
|
+
module: "@voltro/plugin-saves/errors",
|
|
214
|
+
name: "SaveConflict"
|
|
215
|
+
}
|
|
216
|
+
}],
|
|
217
|
+
extendSchema: { tables: Q },
|
|
218
|
+
subjectPrivacy: {
|
|
219
|
+
tables: Q.map((e) => e.tableName),
|
|
220
|
+
kinds: ["user", "guest"]
|
|
221
|
+
},
|
|
222
|
+
bindDataStore: (e) => {
|
|
223
|
+
if (z && B || W || G) throw n("unavailable");
|
|
224
|
+
if (L && L !== e) throw Error("Saves plugin instance cannot cross app stores");
|
|
225
|
+
B = !1, L = e, V++;
|
|
226
|
+
let t = V;
|
|
227
|
+
P || (R = p(e, {
|
|
228
|
+
...N,
|
|
229
|
+
schemaVersion: N.schemaVersion ?? void 0,
|
|
230
|
+
admitWrite: (e, t) => E.suspend(() => !z || !B || !H ? K() : H.rateLimit(e, {
|
|
231
|
+
limit: 10,
|
|
232
|
+
windowMs: 6e4,
|
|
233
|
+
burst: 10,
|
|
234
|
+
algorithm: "fixed-window"
|
|
235
|
+
}, t).pipe(E.mapError(() => n("unavailable")), E.flatMap((e) => e.allowed ? E.void : E.fail(n("rate_limited")))))
|
|
236
|
+
}, () => {
|
|
237
|
+
if (!z || !B || !L || V !== t) throw n("unavailable");
|
|
238
|
+
})), B = !0;
|
|
239
|
+
},
|
|
240
|
+
onActivate: (e) => E.tryPromise({
|
|
241
|
+
try: () => {
|
|
242
|
+
if (G) throw n("unavailable");
|
|
243
|
+
if (z) return Promise.resolve();
|
|
244
|
+
if (W || L && !B) throw n("unavailable");
|
|
245
|
+
let t = V, r = (async () => {
|
|
246
|
+
let r, i;
|
|
247
|
+
try {
|
|
248
|
+
if (!P) {
|
|
249
|
+
if (I) {
|
|
250
|
+
if (S(e.env) !== null) throw n("unavailable");
|
|
251
|
+
i = I;
|
|
252
|
+
} else {
|
|
253
|
+
if (!e.connectionRegistry) throw n("unavailable");
|
|
254
|
+
let t = e.connectionReference?.("hot", "redis") ?? j.connection;
|
|
255
|
+
if (r = O.make(T({
|
|
256
|
+
consumer: "@voltro/plugin-saves",
|
|
257
|
+
namespace: "voltro:saves",
|
|
258
|
+
...t ? { connection: t } : {}
|
|
259
|
+
}).pipe(D.provide(D.succeed(C, e.connectionRegistry)))), i = await r.runPromise(w), S(e.env) !== null && !i.capabilities.crossNode) throw n("unavailable");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (t !== V || G) throw n("unavailable");
|
|
263
|
+
H = i, U = r, r = void 0, z = !0;
|
|
264
|
+
} finally {
|
|
265
|
+
await r?.dispose();
|
|
266
|
+
}
|
|
267
|
+
})();
|
|
268
|
+
return W = r, r.finally(() => {
|
|
269
|
+
W === r && (W = void 0);
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
catch: () => n("unavailable")
|
|
273
|
+
}),
|
|
274
|
+
onDeactivate: () => E.tryPromise({
|
|
275
|
+
try: () => {
|
|
276
|
+
if (G) return G;
|
|
277
|
+
z = !1, B = !1, V++, R = void 0, H = void 0;
|
|
278
|
+
let e = (async () => {
|
|
279
|
+
await W?.catch(() => {});
|
|
280
|
+
let e = U;
|
|
281
|
+
await e?.dispose(), U === e && (U = void 0);
|
|
282
|
+
})();
|
|
283
|
+
return G = e, e.then(() => {
|
|
284
|
+
G === e && (G = void 0);
|
|
285
|
+
});
|
|
286
|
+
},
|
|
287
|
+
catch: () => n("unavailable")
|
|
288
|
+
}),
|
|
289
|
+
onExport: (e, t) => Z.exportSubject({
|
|
290
|
+
subjectId: e,
|
|
291
|
+
tenantId: t.tenantId,
|
|
292
|
+
kind: t.kind
|
|
293
|
+
}),
|
|
294
|
+
onErase: (e, t) => Z.eraseSubject({
|
|
295
|
+
subjectId: e,
|
|
296
|
+
tenantId: t.tenantId,
|
|
297
|
+
kind: t.kind
|
|
298
|
+
}),
|
|
299
|
+
inspectEndpoints: [{
|
|
300
|
+
method: "GET",
|
|
301
|
+
path: "/status",
|
|
302
|
+
scope: "process",
|
|
303
|
+
description: "Configured save ownership, bounds and binding; no save, owner or device data.",
|
|
304
|
+
handler: () => E.sync(() => ({
|
|
305
|
+
kind: "json",
|
|
306
|
+
data: {
|
|
307
|
+
observedAt: Date.now(),
|
|
308
|
+
bound: z && B,
|
|
309
|
+
backend: P ? "custom" : "sql",
|
|
310
|
+
gameId: N.gameId,
|
|
311
|
+
version: N.version,
|
|
312
|
+
slots: N.slots,
|
|
313
|
+
validator: P ? "app-owned" : N.validator === u ? "default" : "custom",
|
|
314
|
+
budgets: P ? null : {
|
|
315
|
+
maxBytes: N.maxBytes,
|
|
316
|
+
historyVersions: N.historyVersions,
|
|
317
|
+
maxRequests: N.maxRequests,
|
|
318
|
+
deleteGraceMs: N.deleteGraceMs,
|
|
319
|
+
writesPerMinute: 10
|
|
320
|
+
},
|
|
321
|
+
schemaVersion: P ? null : N.schemaVersion,
|
|
322
|
+
rateActive: !P && z && B && H !== void 0,
|
|
323
|
+
tables: Q.map((e) => e.tableName),
|
|
324
|
+
offlineInstalled: !1,
|
|
325
|
+
signaturesInstalled: !1,
|
|
326
|
+
externalStorageInstalled: !1,
|
|
327
|
+
purgeInstalled: !1,
|
|
328
|
+
gmRollbackInstalled: !1
|
|
329
|
+
}
|
|
330
|
+
}))
|
|
331
|
+
}]
|
|
332
|
+
})
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
//#endregion
|
|
336
|
+
export { A as savesPlugin, k as savesSystemContract };
|
package/dist/port.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
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 captureSavesService: (input: SavesService) => SavesService;
|
|
9
|
+
|
|
10
|
+
declare class SaveConflict extends SaveConflict_base {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare const SaveConflict_base: Schema.Class<SaveConflict, Schema.TaggedStruct<"SaveConflict", {
|
|
14
|
+
readonly serverVersion: Schema.Number;
|
|
15
|
+
readonly server: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
16
|
+
readonly client: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
17
|
+
}>, YieldableError>;
|
|
18
|
+
|
|
19
|
+
declare interface SaveDeleteIntent {
|
|
20
|
+
readonly slot: string;
|
|
21
|
+
readonly requestId: string;
|
|
22
|
+
readonly baseVersion: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare type SaveDocument = typeof SaveDocumentSchema.Type;
|
|
26
|
+
|
|
27
|
+
declare const SaveDocumentSchema: Schema.Struct<{
|
|
28
|
+
readonly bytes: Schema.Uint8ArrayFromBase64;
|
|
29
|
+
readonly slot: Schema.String;
|
|
30
|
+
readonly version: Schema.Number;
|
|
31
|
+
readonly schemaVersion: Schema.Number;
|
|
32
|
+
readonly byteLength: Schema.Number;
|
|
33
|
+
readonly checksum: Schema.String;
|
|
34
|
+
readonly deviceId: Schema.String;
|
|
35
|
+
readonly clientVersion: Schema.String;
|
|
36
|
+
readonly savedAt: Schema.Number;
|
|
37
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
38
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
39
|
+
}>;
|
|
40
|
+
|
|
41
|
+
declare type SaveError = SavesError | SaveConflict;
|
|
42
|
+
|
|
43
|
+
declare type SaveHeader = typeof SaveHeaderSchema.Type;
|
|
44
|
+
|
|
45
|
+
declare const SaveHeaderSchema: Schema.Struct<{
|
|
46
|
+
readonly slot: Schema.String;
|
|
47
|
+
readonly version: Schema.Number;
|
|
48
|
+
readonly schemaVersion: Schema.Number;
|
|
49
|
+
readonly byteLength: Schema.Number;
|
|
50
|
+
readonly checksum: Schema.String;
|
|
51
|
+
readonly deviceId: Schema.String;
|
|
52
|
+
readonly clientVersion: Schema.String;
|
|
53
|
+
readonly savedAt: Schema.Number;
|
|
54
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
55
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
56
|
+
}>;
|
|
57
|
+
|
|
58
|
+
declare interface SavePutIntent {
|
|
59
|
+
readonly slot: string;
|
|
60
|
+
readonly requestId: string;
|
|
61
|
+
readonly baseVersion: number;
|
|
62
|
+
readonly schemaVersion: number;
|
|
63
|
+
readonly bytes: Uint8Array;
|
|
64
|
+
readonly deviceId: string;
|
|
65
|
+
readonly clientVersion: string;
|
|
66
|
+
readonly summary: SaveSummary;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare interface SaveRollbackIntent {
|
|
70
|
+
readonly slot: string;
|
|
71
|
+
readonly requestId: string;
|
|
72
|
+
readonly baseVersion: number;
|
|
73
|
+
readonly version: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
declare class SavesError extends SavesError_base {
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
declare const SavesError_base: Schema.Class<SavesError, Schema.TaggedStruct<"SavesError", {
|
|
80
|
+
readonly code: Schema.Literals<readonly ["invalid_input", "invalid_record", "forbidden", "not_found", "erased", "limit_exceeded", "invalid_save", "rate_limited", "unavailable", "request_conflict"]>;
|
|
81
|
+
}>, YieldableError>;
|
|
82
|
+
|
|
83
|
+
export declare class SavesPort extends SavesPort_base {
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare const SavesPort_base: Context.ServiceClass<SavesPort, "@voltro/SavesPort", SavesService>;
|
|
87
|
+
|
|
88
|
+
export declare interface SavesService {
|
|
89
|
+
readonly get: (subject: Subject, slot: string) => Effect.Effect<SaveDocument | null, SaveError>;
|
|
90
|
+
readonly list: (subject: Subject) => Effect.Effect<readonly SaveHeader[], SaveError>;
|
|
91
|
+
readonly history: (subject: Subject, slot: string) => Effect.Effect<readonly SaveHeader[], SaveError>;
|
|
92
|
+
readonly put: (subject: Subject, intent: SavePutIntent) => Effect.Effect<SaveHeader, SaveError>;
|
|
93
|
+
readonly rollback: (subject: Subject, intent: SaveRollbackIntent) => Effect.Effect<SaveHeader, SaveError>;
|
|
94
|
+
readonly remove: (subject: Subject, intent: SaveDeleteIntent) => Effect.Effect<SaveHeader, SaveError>;
|
|
95
|
+
readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, SaveError>;
|
|
96
|
+
readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, SaveError>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
declare type SaveSummary = typeof SaveSummarySchema.Type;
|
|
100
|
+
|
|
101
|
+
declare const SaveSummarySchema: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
102
|
+
|
|
103
|
+
export { }
|
package/dist/port.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { savesError as e } from "./errors.js";
|
|
2
|
+
import { Context as t } from "effect";
|
|
3
|
+
//#region src/port.ts
|
|
4
|
+
var n = class extends t.Service()("@voltro/SavesPort") {}, r = (t) => {
|
|
5
|
+
if (typeof t != "object" || !t || Array.isArray(t)) throw e("invalid_input");
|
|
6
|
+
let n = Object.create(null);
|
|
7
|
+
for (let r of [
|
|
8
|
+
"get",
|
|
9
|
+
"list",
|
|
10
|
+
"history",
|
|
11
|
+
"put",
|
|
12
|
+
"rollback",
|
|
13
|
+
"remove",
|
|
14
|
+
"exportSubject",
|
|
15
|
+
"eraseSubject"
|
|
16
|
+
]) {
|
|
17
|
+
let i = t, a = !1;
|
|
18
|
+
for (let o = 0; i && o < 8; o++, i = Object.getPrototypeOf(i)) {
|
|
19
|
+
let o = Object.getOwnPropertyDescriptor(i, r);
|
|
20
|
+
if (o) {
|
|
21
|
+
if (!("value" in o) || typeof o.value != "function") throw e("invalid_input");
|
|
22
|
+
Object.defineProperty(n, r, {
|
|
23
|
+
value: o.value.bind(t),
|
|
24
|
+
enumerable: !0
|
|
25
|
+
}), a = !0;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!a) throw e("invalid_input");
|
|
30
|
+
}
|
|
31
|
+
return Object.freeze(n);
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { n as SavesPort, r as captureSavesService };
|
package/dist/rpc.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ActionProcedureDescriptor } from '@voltro/protocol';
|
|
2
|
+
import { PluginRpcClientDescriptor } from '@voltro/protocol';
|
|
3
|
+
import { QueryProcedureDescriptor } from '@voltro/protocol';
|
|
4
|
+
import { Schema } from 'effect';
|
|
5
|
+
import { YieldableError } from 'effect/Cause';
|
|
6
|
+
|
|
7
|
+
declare class SaveConflict extends SaveConflict_base {
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare const SaveConflict_base: Schema.Class<SaveConflict, Schema.TaggedStruct<"SaveConflict", {
|
|
11
|
+
readonly serverVersion: Schema.Number;
|
|
12
|
+
readonly server: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
13
|
+
readonly client: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
14
|
+
}>, YieldableError>;
|
|
15
|
+
|
|
16
|
+
declare class SavesError extends SavesError_base {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare const SavesError_base: Schema.Class<SavesError, Schema.TaggedStruct<"SavesError", {
|
|
20
|
+
readonly code: Schema.Literals<readonly ["invalid_input", "invalid_record", "forbidden", "not_found", "erased", "limit_exceeded", "invalid_save", "rate_limited", "unavailable", "request_conflict"]>;
|
|
21
|
+
}>, YieldableError>;
|
|
22
|
+
|
|
23
|
+
export declare const savesGetDescriptor: QueryProcedureDescriptor<"saves.get", Schema.Struct<{
|
|
24
|
+
readonly slot: Schema.NonEmptyString;
|
|
25
|
+
}>, Schema.NullOr<Schema.Struct<{
|
|
26
|
+
readonly bytes: Schema.Uint8ArrayFromBase64;
|
|
27
|
+
readonly slot: Schema.String;
|
|
28
|
+
readonly version: Schema.Number;
|
|
29
|
+
readonly schemaVersion: Schema.Number;
|
|
30
|
+
readonly byteLength: Schema.Number;
|
|
31
|
+
readonly checksum: Schema.String;
|
|
32
|
+
readonly deviceId: Schema.String;
|
|
33
|
+
readonly clientVersion: Schema.String;
|
|
34
|
+
readonly savedAt: Schema.Number;
|
|
35
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
36
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
37
|
+
}>>, Schema.Union<readonly [typeof SavesError, typeof SaveConflict]>>;
|
|
38
|
+
|
|
39
|
+
export declare const savesHistoryDescriptor: QueryProcedureDescriptor<"saves.history", Schema.Struct<{
|
|
40
|
+
readonly slot: Schema.NonEmptyString;
|
|
41
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
42
|
+
readonly slot: Schema.String;
|
|
43
|
+
readonly version: Schema.Number;
|
|
44
|
+
readonly schemaVersion: Schema.Number;
|
|
45
|
+
readonly byteLength: Schema.Number;
|
|
46
|
+
readonly checksum: Schema.String;
|
|
47
|
+
readonly deviceId: Schema.String;
|
|
48
|
+
readonly clientVersion: Schema.String;
|
|
49
|
+
readonly savedAt: Schema.Number;
|
|
50
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
51
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
52
|
+
}>>, Schema.Union<readonly [typeof SavesError, typeof SaveConflict]>>;
|
|
53
|
+
|
|
54
|
+
export declare const savesListDescriptor: QueryProcedureDescriptor<"saves.list", Schema.Struct<{}>, Schema.$Array<Schema.Struct<{
|
|
55
|
+
readonly slot: Schema.String;
|
|
56
|
+
readonly version: Schema.Number;
|
|
57
|
+
readonly schemaVersion: Schema.Number;
|
|
58
|
+
readonly byteLength: Schema.Number;
|
|
59
|
+
readonly checksum: Schema.String;
|
|
60
|
+
readonly deviceId: Schema.String;
|
|
61
|
+
readonly clientVersion: Schema.String;
|
|
62
|
+
readonly savedAt: Schema.Number;
|
|
63
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
64
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
65
|
+
}>>, Schema.Union<readonly [typeof SavesError, typeof SaveConflict]>>;
|
|
66
|
+
|
|
67
|
+
export declare const savesPutDescriptor: ActionProcedureDescriptor<"saves.put", Schema.Struct<{
|
|
68
|
+
readonly schemaVersion: Schema.Number;
|
|
69
|
+
readonly bytes: Schema.Uint8ArrayFromBase64;
|
|
70
|
+
readonly deviceId: Schema.NonEmptyString;
|
|
71
|
+
readonly clientVersion: Schema.NonEmptyString;
|
|
72
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
73
|
+
readonly slot: Schema.NonEmptyString;
|
|
74
|
+
readonly requestId: Schema.NonEmptyString;
|
|
75
|
+
readonly baseVersion: Schema.Number;
|
|
76
|
+
}>, Schema.Struct<{
|
|
77
|
+
readonly slot: Schema.String;
|
|
78
|
+
readonly version: Schema.Number;
|
|
79
|
+
readonly schemaVersion: Schema.Number;
|
|
80
|
+
readonly byteLength: Schema.Number;
|
|
81
|
+
readonly checksum: Schema.String;
|
|
82
|
+
readonly deviceId: Schema.String;
|
|
83
|
+
readonly clientVersion: Schema.String;
|
|
84
|
+
readonly savedAt: Schema.Number;
|
|
85
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
86
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
87
|
+
}>, Schema.Union<readonly [typeof SavesError, typeof SaveConflict]>>;
|
|
88
|
+
|
|
89
|
+
export declare const savesRemoveDescriptor: ActionProcedureDescriptor<"saves.remove", Schema.Struct<{
|
|
90
|
+
slot: Schema.NonEmptyString;
|
|
91
|
+
requestId: Schema.NonEmptyString;
|
|
92
|
+
baseVersion: Schema.Number;
|
|
93
|
+
}>, Schema.Struct<{
|
|
94
|
+
readonly slot: Schema.String;
|
|
95
|
+
readonly version: Schema.Number;
|
|
96
|
+
readonly schemaVersion: Schema.Number;
|
|
97
|
+
readonly byteLength: Schema.Number;
|
|
98
|
+
readonly checksum: Schema.String;
|
|
99
|
+
readonly deviceId: Schema.String;
|
|
100
|
+
readonly clientVersion: Schema.String;
|
|
101
|
+
readonly savedAt: Schema.Number;
|
|
102
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
103
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
104
|
+
}>, Schema.Union<readonly [typeof SavesError, typeof SaveConflict]>>;
|
|
105
|
+
|
|
106
|
+
export declare const savesRequestSource = "_voltro_saves_requests";
|
|
107
|
+
|
|
108
|
+
export declare const savesRollbackDescriptor: ActionProcedureDescriptor<"saves.rollback", Schema.Struct<{
|
|
109
|
+
readonly version: Schema.Number;
|
|
110
|
+
readonly slot: Schema.NonEmptyString;
|
|
111
|
+
readonly requestId: Schema.NonEmptyString;
|
|
112
|
+
readonly baseVersion: Schema.Number;
|
|
113
|
+
}>, Schema.Struct<{
|
|
114
|
+
readonly slot: Schema.String;
|
|
115
|
+
readonly version: Schema.Number;
|
|
116
|
+
readonly schemaVersion: Schema.Number;
|
|
117
|
+
readonly byteLength: Schema.Number;
|
|
118
|
+
readonly checksum: Schema.String;
|
|
119
|
+
readonly deviceId: Schema.String;
|
|
120
|
+
readonly clientVersion: Schema.String;
|
|
121
|
+
readonly savedAt: Schema.Number;
|
|
122
|
+
readonly summary: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean, Schema.Null]>>;
|
|
123
|
+
readonly deletedUntil: Schema.NullOr<Schema.Number>;
|
|
124
|
+
}>, Schema.Union<readonly [typeof SavesError, typeof SaveConflict]>>;
|
|
125
|
+
|
|
126
|
+
export declare const savesRpcClientImports: readonly PluginRpcClientDescriptor[];
|
|
127
|
+
|
|
128
|
+
export { }
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SaveConflict as e, SaveSummarySchema as t, SavesError as n } from "./errors.js";
|
|
2
|
+
import { SAVE_HISTORY_TABLE as r, SAVE_REQUEST_TABLE as i, SAVE_SUBJECT_TABLE as a, SAVE_TABLE as o, SaveDocumentSchema as s, SaveHeaderSchema as c } from "./contract.js";
|
|
3
|
+
import { Schema as l } from "effect";
|
|
4
|
+
import { defineAction as u, defineQuery as d } from "@voltro/protocol";
|
|
5
|
+
//#region src/rpc.ts
|
|
6
|
+
var f = "Only the authenticated owner in the configured game and exact tenant may read or change opaque saves. Saves never grant server-owned values.", p = l.NonEmptyString, m = l.Number.pipe(l.check(l.isInt()), l.check(l.isBetween({
|
|
7
|
+
minimum: 0,
|
|
8
|
+
maximum: 2147483647
|
|
9
|
+
}))), h = l.Union([n, e]), g = [o, a], _ = l.Struct({ slot: p }), v = {
|
|
10
|
+
slot: p,
|
|
11
|
+
requestId: p,
|
|
12
|
+
baseVersion: m
|
|
13
|
+
}, y = d({
|
|
14
|
+
name: "saves.get",
|
|
15
|
+
input: _,
|
|
16
|
+
output: l.NullOr(s),
|
|
17
|
+
source: g,
|
|
18
|
+
error: h,
|
|
19
|
+
openAccess: f
|
|
20
|
+
}), b = d({
|
|
21
|
+
name: "saves.list",
|
|
22
|
+
input: l.Struct({}),
|
|
23
|
+
output: l.Array(c),
|
|
24
|
+
source: g,
|
|
25
|
+
error: h,
|
|
26
|
+
openAccess: f
|
|
27
|
+
}), x = d({
|
|
28
|
+
name: "saves.history",
|
|
29
|
+
input: _,
|
|
30
|
+
output: l.Array(c),
|
|
31
|
+
source: [r, a],
|
|
32
|
+
error: h,
|
|
33
|
+
openAccess: f
|
|
34
|
+
}), S = u({
|
|
35
|
+
name: "saves.put",
|
|
36
|
+
input: l.Struct({
|
|
37
|
+
...v,
|
|
38
|
+
schemaVersion: m,
|
|
39
|
+
bytes: l.Uint8ArrayFromBase64,
|
|
40
|
+
deviceId: p,
|
|
41
|
+
clientVersion: p,
|
|
42
|
+
summary: t
|
|
43
|
+
}),
|
|
44
|
+
output: c,
|
|
45
|
+
error: h,
|
|
46
|
+
openAccess: f
|
|
47
|
+
}), C = u({
|
|
48
|
+
name: "saves.rollback",
|
|
49
|
+
input: l.Struct({
|
|
50
|
+
...v,
|
|
51
|
+
version: m
|
|
52
|
+
}),
|
|
53
|
+
output: c,
|
|
54
|
+
error: h,
|
|
55
|
+
openAccess: f
|
|
56
|
+
}), w = u({
|
|
57
|
+
name: "saves.remove",
|
|
58
|
+
input: l.Struct(v),
|
|
59
|
+
output: c,
|
|
60
|
+
error: h,
|
|
61
|
+
openAccess: f
|
|
62
|
+
}), T = i, E = [...[
|
|
63
|
+
"Get",
|
|
64
|
+
"List",
|
|
65
|
+
"History"
|
|
66
|
+
].map((e) => ({
|
|
67
|
+
tag: "saves." + e.toLowerCase(),
|
|
68
|
+
kind: "query",
|
|
69
|
+
import: {
|
|
70
|
+
module: "@voltro/plugin-saves/rpc",
|
|
71
|
+
name: "saves" + e + "Descriptor"
|
|
72
|
+
}
|
|
73
|
+
})), ...[
|
|
74
|
+
"Put",
|
|
75
|
+
"Rollback",
|
|
76
|
+
"Remove"
|
|
77
|
+
].map((e) => ({
|
|
78
|
+
tag: "saves." + e.toLowerCase(),
|
|
79
|
+
kind: "action",
|
|
80
|
+
import: {
|
|
81
|
+
module: "@voltro/plugin-saves/rpc",
|
|
82
|
+
name: "saves" + e + "Descriptor"
|
|
83
|
+
}
|
|
84
|
+
}))];
|
|
85
|
+
//#endregion
|
|
86
|
+
export { y as savesGetDescriptor, x as savesHistoryDescriptor, b as savesListDescriptor, S as savesPutDescriptor, w as savesRemoveDescriptor, T as savesRequestSource, C as savesRollbackDescriptor, E as savesRpcClientImports };
|