@tinycloud/node-sdk 3.2.0-beta.2 → 3.2.0-beta.3
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/dist/{core-DjLtgeVT.d.cts → WasmKeyProvider-DjfRu9aL.d.cts} +162 -2
- package/dist/{core-DjLtgeVT.d.ts → WasmKeyProvider-DjfRu9aL.d.ts} +162 -2
- package/dist/authority-mTQEgN56.d.cts +97 -0
- package/dist/authority-mTQEgN56.d.ts +97 -0
- package/dist/core.cjs +1049 -443
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +13 -3
- package/dist/core.d.ts +13 -3
- package/dist/core.js +725 -115
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +2348 -494
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +1996 -128
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.cjs +278 -0
- package/dist/replication/authority.cjs.map +1 -0
- package/dist/replication/authority.d.cts +3 -0
- package/dist/replication/authority.d.ts +3 -0
- package/dist/replication/authority.js +257 -0
- package/dist/replication/authority.js.map +1 -0
- package/dist/replication/runtime.cjs +224 -0
- package/dist/replication/runtime.cjs.map +1 -0
- package/dist/replication/runtime.d.cts +3 -0
- package/dist/replication/runtime.d.ts +3 -0
- package/dist/replication/runtime.js +189 -0
- package/dist/replication/runtime.js.map +1 -0
- package/dist/runtime-DyYwHW8j.d.cts +64 -0
- package/dist/runtime-DyYwHW8j.d.ts +64 -0
- package/package.json +25 -3
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/replication/runtime.ts
|
|
31
|
+
var runtime_exports = {};
|
|
32
|
+
__export(runtime_exports, {
|
|
33
|
+
ReplicationRuntime: () => ReplicationRuntime
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(runtime_exports);
|
|
36
|
+
var import_sdk_core = require("@tinycloud/sdk-core");
|
|
37
|
+
var scheduler = {
|
|
38
|
+
now: () => Date.now(),
|
|
39
|
+
setTimeout(fn, ms) {
|
|
40
|
+
const timer = setTimeout(fn, ms);
|
|
41
|
+
timer.unref?.();
|
|
42
|
+
return () => clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function resolvedOptions(options) {
|
|
46
|
+
return {
|
|
47
|
+
...options,
|
|
48
|
+
syncIntervalMs: options.syncIntervalMs ?? 6e4,
|
|
49
|
+
maxStalenessMs: options.maxStalenessMs ?? 12e4,
|
|
50
|
+
staleSyncTimeoutMs: options.staleSyncTimeoutMs ?? 5e3,
|
|
51
|
+
allowSecrets: options.allowSecrets === true,
|
|
52
|
+
verify: options.verify === true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
var ReplicationRuntime = class {
|
|
56
|
+
constructor(options, clock = scheduler) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
this.clock = clock;
|
|
59
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
60
|
+
this.attached = /* @__PURE__ */ new Map();
|
|
61
|
+
this.previous = Promise.resolve();
|
|
62
|
+
this.binding = Promise.resolve();
|
|
63
|
+
this.generation = 0;
|
|
64
|
+
this.bindingActive = false;
|
|
65
|
+
this.pendingPurgeSet = false;
|
|
66
|
+
this.closed = false;
|
|
67
|
+
this.control = {
|
|
68
|
+
status: async () => {
|
|
69
|
+
await this.binding;
|
|
70
|
+
return this.current ? this.current.status() : [];
|
|
71
|
+
},
|
|
72
|
+
sync: async (input) => {
|
|
73
|
+
await this.binding;
|
|
74
|
+
if (this.purgedGeneration === this.generation) return;
|
|
75
|
+
if (this.current) await this.current.sync(input);
|
|
76
|
+
},
|
|
77
|
+
purge: (input) => {
|
|
78
|
+
if (this.bindingActive) {
|
|
79
|
+
this.pendingPurge = input;
|
|
80
|
+
this.pendingPurgeSet = true;
|
|
81
|
+
const existing = this.current ?? this.lastController;
|
|
82
|
+
if (existing) {
|
|
83
|
+
this.pendingPurgeResult = existing.purge(input);
|
|
84
|
+
this.purgedGeneration = this.generation;
|
|
85
|
+
}
|
|
86
|
+
return this.binding.then(() => this.pendingPurgeResult ?? { purged: [], failed: [] });
|
|
87
|
+
}
|
|
88
|
+
const controller = this.current ?? this.lastController;
|
|
89
|
+
if (!controller) return Promise.resolve({ purged: [], failed: [] });
|
|
90
|
+
this.purgedGeneration = this.generation;
|
|
91
|
+
return controller.purge(input);
|
|
92
|
+
},
|
|
93
|
+
clearPending: async () => {
|
|
94
|
+
await this.binding;
|
|
95
|
+
if (this.current) return this.current.clearPending();
|
|
96
|
+
const pending = this.lastPending;
|
|
97
|
+
if (!pending) return 0;
|
|
98
|
+
return pending.update((state) => {
|
|
99
|
+
const before = state.records.length;
|
|
100
|
+
state.records = state.records.filter(
|
|
101
|
+
(record) => record.state === "committed" || record.state === "in_flight" && this.clock.now() - Date.parse(record.at) <= 10 * 6e4
|
|
102
|
+
);
|
|
103
|
+
return before - state.records.length;
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
close: async () => {
|
|
107
|
+
this.closed = true;
|
|
108
|
+
this.unbind();
|
|
109
|
+
await this.binding.catch(() => void 0);
|
|
110
|
+
await this.previous;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
loadServices() {
|
|
115
|
+
return this.services ?? (this.services = import("@tinycloud/sdk-services/kv/replication"));
|
|
116
|
+
}
|
|
117
|
+
detachAll() {
|
|
118
|
+
for (const kv of this.attached.keys()) kv.setReadThrough(null);
|
|
119
|
+
this.attached.clear();
|
|
120
|
+
}
|
|
121
|
+
bind(input) {
|
|
122
|
+
if (this.closed || !this.options.enabled) return Promise.resolve();
|
|
123
|
+
this.bindingActive = true;
|
|
124
|
+
const generation = ++this.generation;
|
|
125
|
+
this.purgedGeneration = void 0;
|
|
126
|
+
const { context, session } = input;
|
|
127
|
+
const emit = (event) => {
|
|
128
|
+
try {
|
|
129
|
+
this.options.onEvent?.(event);
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
const { type, ...data } = event;
|
|
133
|
+
context.emit(type, data);
|
|
134
|
+
};
|
|
135
|
+
const previous = this.current;
|
|
136
|
+
this.current = void 0;
|
|
137
|
+
for (const kv of this.attached.keys()) kv.setReadThrough(null);
|
|
138
|
+
this.primarySpace = session.spaceId;
|
|
139
|
+
const close = previous ? previous.close().catch(() => void 0) : Promise.resolve();
|
|
140
|
+
this.previous = this.previous.then(() => close);
|
|
141
|
+
const previousClose = this.previous;
|
|
142
|
+
this.binding = this.loadServices().then((services) => {
|
|
143
|
+
if (generation !== this.generation || this.closed) return;
|
|
144
|
+
let identity;
|
|
145
|
+
try {
|
|
146
|
+
identity = services.canonicalReplicationIdentity({
|
|
147
|
+
host: context.hosts[0],
|
|
148
|
+
space: session.spaceId,
|
|
149
|
+
principal: input.principal ?? (0, import_sdk_core.pkhDid)(input.address, input.chainId)
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
for (const prefix of this.options.prefixes) {
|
|
153
|
+
emit({ type: "replication.state", at: new Date(this.clock.now()).toISOString(), space: session.spaceId, replica: prefix, state: "unavailable", code: "CONFIG_INVALID" });
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const key = services.replicationIdentityKey(identity);
|
|
158
|
+
let pending = this.pending.get(key);
|
|
159
|
+
if (!pending) {
|
|
160
|
+
pending = this.options.storage.pendingWrites?.(identity) ?? services.createMemoryPendingStore(identity);
|
|
161
|
+
this.pending.set(key, pending);
|
|
162
|
+
}
|
|
163
|
+
this.lastPending = pending;
|
|
164
|
+
const controller = services.createKVReplication({
|
|
165
|
+
options: resolvedOptions(this.options),
|
|
166
|
+
mode: this.options.mode ?? "background",
|
|
167
|
+
storage: this.options.storage,
|
|
168
|
+
identity,
|
|
169
|
+
session: { id: session.delegationCid, did: session.verificationMethod, space: session.spaceId },
|
|
170
|
+
authority: input.authority,
|
|
171
|
+
pending,
|
|
172
|
+
scheduler: this.clock,
|
|
173
|
+
emit,
|
|
174
|
+
fetch: context.fetch,
|
|
175
|
+
previous: previousClose
|
|
176
|
+
});
|
|
177
|
+
this.current = controller;
|
|
178
|
+
this.lastController = controller;
|
|
179
|
+
this.lastIdentity = identity;
|
|
180
|
+
for (const [kv, space] of this.attached) {
|
|
181
|
+
kv.setReadThrough(space === this.primarySpace ? controller : null);
|
|
182
|
+
}
|
|
183
|
+
for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);
|
|
184
|
+
if (this.pendingPurgeSet) {
|
|
185
|
+
this.pendingPurgeResult = controller.purge(this.pendingPurge);
|
|
186
|
+
this.purgedGeneration = generation;
|
|
187
|
+
this.pendingPurge = void 0;
|
|
188
|
+
this.pendingPurgeSet = false;
|
|
189
|
+
}
|
|
190
|
+
}).finally(() => {
|
|
191
|
+
if (generation === this.generation) this.bindingActive = false;
|
|
192
|
+
});
|
|
193
|
+
for (const kv of this.attached.keys()) kv.setReadThroughReady?.(this.binding);
|
|
194
|
+
return this.binding;
|
|
195
|
+
}
|
|
196
|
+
attach(space, kv) {
|
|
197
|
+
this.attached.set(kv, space);
|
|
198
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);
|
|
199
|
+
return this.binding.then(() => {
|
|
200
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
unbind() {
|
|
204
|
+
this.generation++;
|
|
205
|
+
this.bindingActive = false;
|
|
206
|
+
this.pendingPurgeSet = false;
|
|
207
|
+
const controller = this.current;
|
|
208
|
+
this.current = void 0;
|
|
209
|
+
this.primarySpace = void 0;
|
|
210
|
+
this.detachAll();
|
|
211
|
+
const binding = this.binding;
|
|
212
|
+
const close = binding.then(() => controller?.close()).catch(() => void 0);
|
|
213
|
+
this.previous = this.previous.then(() => close);
|
|
214
|
+
return this.previous;
|
|
215
|
+
}
|
|
216
|
+
get boundIdentity() {
|
|
217
|
+
return this.lastIdentity;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
221
|
+
0 && (module.exports = {
|
|
222
|
+
ReplicationRuntime
|
|
223
|
+
});
|
|
224
|
+
//# sourceMappingURL=runtime.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/replication/runtime.ts"],"sourcesContent":["import { pkhDid } from \"@tinycloud/sdk-core\";\nimport type { FetchFunction, ServiceContext, ServiceSession } from \"@tinycloud/sdk-core\";\nimport type {\n KVReadThrough,\n KVReplicaStorage,\n KVReplicationController,\n PendingWriteStore,\n ReplicationAuthority,\n ReplicationControl,\n ReplicationEvent,\n ReplicationIdentity,\n ReplicationOptions,\n ReplicationScheduler,\n} from \"@tinycloud/sdk-services\";\ntype ReplicationServices = typeof import(\"@tinycloud/sdk-services/kv/replication\");\n\nexport interface ReplicationRuntimeOptions extends ReplicationOptions {\n storage: KVReplicaStorage;\n mode?: \"background\" | \"foreground\";\n}\n\nexport interface ReplicationRuntimeBinding {\n context: ServiceContext;\n session: ServiceSession;\n address?: string;\n chainId?: number;\n principal?: string;\n authority: ReplicationAuthority;\n primaryKV: Array<{ space: string; kv: { setReadThrough(readThrough: KVReadThrough | null): void } }>;\n}\n\nconst scheduler: ReplicationScheduler = {\n now: () => Date.now(),\n setTimeout(fn, ms) {\n const timer = setTimeout(fn, ms);\n timer.unref?.();\n return () => clearTimeout(timer);\n },\n};\n\nfunction resolvedOptions(options: ReplicationRuntimeOptions) {\n return {\n ...options,\n syncIntervalMs: options.syncIntervalMs ?? 60_000,\n maxStalenessMs: options.maxStalenessMs ?? 120_000,\n staleSyncTimeoutMs: options.staleSyncTimeoutMs ?? 5_000,\n allowSecrets: options.allowSecrets === true,\n verify: options.verify === true,\n };\n}\ntype ReadThroughKV = {\n setReadThrough(readThrough: KVReadThrough | null): void;\n setReadThroughReady?(ready: Promise<unknown>): void;\n};\n\nexport class ReplicationRuntime {\n readonly control: ReplicationControl;\n private readonly pending = new Map<string, PendingWriteStore>();\n private readonly attached = new Map<ReadThroughKV, string>();\n private current?: KVReplicationController;\n private lastController?: KVReplicationController;\n private previous: Promise<void> = Promise.resolve();\n private binding: Promise<void> = Promise.resolve();\n private services?: Promise<ReplicationServices>;\n private generation = 0;\n private lastIdentity?: ReplicationIdentity;\n private lastPending?: PendingWriteStore;\n private primarySpace?: string;\n private pendingPurge?: Parameters<KVReplicationController[\"purge\"]>[0];\n private pendingPurgeResult?: ReturnType<KVReplicationController[\"purge\"]>;\n private bindingActive = false;\n private pendingPurgeSet = false;\n private purgedGeneration?: number;\n private closed = false;\n constructor(\n private readonly options: ReplicationRuntimeOptions,\n private readonly clock: ReplicationScheduler = scheduler,\n ) {\n this.control = {\n status: async () => {\n await this.binding;\n return this.current ? this.current.status() : [];\n },\n sync: async (input) => {\n await this.binding;\n if (this.purgedGeneration === this.generation) return;\n if (this.current) await this.current.sync(input);\n },\n purge: (input) => {\n if (this.bindingActive) {\n this.pendingPurge = input;\n this.pendingPurgeSet = true;\n const existing = this.current ?? this.lastController;\n if (existing) {\n this.pendingPurgeResult = existing.purge(input);\n this.purgedGeneration = this.generation;\n }\n return this.binding.then(() => this.pendingPurgeResult ?? { purged: [], failed: [] });\n }\n const controller = this.current ?? this.lastController;\n if (!controller) return Promise.resolve({ purged: [], failed: [] });\n this.purgedGeneration = this.generation;\n return controller.purge(input);\n },\n clearPending: async () => {\n await this.binding;\n if (this.current) return this.current.clearPending();\n const pending = this.lastPending;\n if (!pending) return 0;\n return pending.update((state) => {\n const before = state.records.length;\n state.records = state.records.filter((record) =>\n record.state === \"committed\" ||\n (record.state === \"in_flight\" && this.clock.now() - Date.parse(record.at) <= 10 * 60_000),\n );\n return before - state.records.length;\n });\n },\n close: async () => {\n this.closed = true;\n this.unbind();\n await this.binding.catch(() => undefined);\n await this.previous;\n },\n };\n }\n\n private loadServices(): Promise<ReplicationServices> {\n return (this.services ??= import(\"@tinycloud/sdk-services/kv/replication\"));\n }\n\n private detachAll(): void {\n for (const kv of this.attached.keys()) kv.setReadThrough(null);\n this.attached.clear();\n }\n\n bind(input: ReplicationRuntimeBinding): Promise<void> {\n if (this.closed || !this.options.enabled) return Promise.resolve();\n this.bindingActive = true;\n const generation = ++this.generation;\n this.purgedGeneration = undefined;\n const { context, session } = input;\n const emit = (event: ReplicationEvent): void => {\n try { this.options.onEvent?.(event); } catch { /* Observability must not affect KV behavior. */ }\n const { type, ...data } = event;\n context.emit(type, data);\n };\n const previous = this.current;\n this.current = undefined;\n for (const kv of this.attached.keys()) kv.setReadThrough(null);\n this.primarySpace = session.spaceId;\n const close = previous ? previous.close().catch(() => undefined) : Promise.resolve();\n this.previous = this.previous.then(() => close);\n const previousClose = this.previous;\n this.binding = this.loadServices().then((services) => {\n if (generation !== this.generation || this.closed) return;\n let identity: ReplicationIdentity;\n try {\n identity = services.canonicalReplicationIdentity({\n host: context.hosts[0]!,\n space: session.spaceId,\n principal: input.principal ?? pkhDid(input.address!, input.chainId!),\n });\n } catch {\n for (const prefix of this.options.prefixes) {\n emit({ type: \"replication.state\", at: new Date(this.clock.now()).toISOString(), space: session.spaceId, replica: prefix, state: \"unavailable\", code: \"CONFIG_INVALID\" });\n }\n return;\n }\n const key = services.replicationIdentityKey(identity);\n let pending = this.pending.get(key);\n if (!pending) {\n pending = this.options.storage.pendingWrites?.(identity) ?? services.createMemoryPendingStore(identity);\n this.pending.set(key, pending);\n }\n this.lastPending = pending;\n const controller = services.createKVReplication({\n options: resolvedOptions(this.options),\n mode: this.options.mode ?? \"background\",\n storage: this.options.storage,\n identity,\n session: { id: session.delegationCid, did: session.verificationMethod, space: session.spaceId },\n authority: input.authority,\n pending,\n scheduler: this.clock,\n emit,\n fetch: context.fetch as FetchFunction,\n previous: previousClose,\n });\n this.current = controller;\n this.lastController = controller;\n this.lastIdentity = identity;\n for (const [kv, space] of this.attached) {\n kv.setReadThrough(space === this.primarySpace ? controller : null);\n }\n for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);\n if (this.pendingPurgeSet) {\n this.pendingPurgeResult = controller.purge(this.pendingPurge);\n this.purgedGeneration = generation;\n this.pendingPurge = undefined;\n this.pendingPurgeSet = false;\n }\n }).finally(() => {\n if (generation === this.generation) this.bindingActive = false;\n });\n for (const kv of this.attached.keys()) kv.setReadThroughReady?.(this.binding);\n return this.binding;\n }\n\n attach(space: string, kv: ReadThroughKV): Promise<void> {\n this.attached.set(kv, space);\n kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);\n return this.binding.then(() => {\n kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);\n });\n }\n\n unbind(): Promise<void> {\n this.generation++;\n this.bindingActive = false;\n this.pendingPurgeSet = false;\n const controller = this.current;\n this.current = undefined;\n this.primarySpace = undefined;\n this.detachAll();\n const binding = this.binding;\n const close = binding.then(() => controller?.close()).catch(() => undefined);\n this.previous = this.previous.then(() => close);\n return this.previous;\n }\n\n get boundIdentity(): ReplicationIdentity | undefined {\n return this.lastIdentity;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAuB;AA+BvB,IAAM,YAAkC;AAAA,EACtC,KAAK,MAAM,KAAK,IAAI;AAAA,EACpB,WAAW,IAAI,IAAI;AACjB,UAAM,QAAQ,WAAW,IAAI,EAAE;AAC/B,UAAM,QAAQ;AACd,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC;AACF;AAEA,SAAS,gBAAgB,SAAoC;AAC3D,SAAO;AAAA,IACL,GAAG;AAAA,IACH,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,oBAAoB,QAAQ,sBAAsB;AAAA,IAClD,cAAc,QAAQ,iBAAiB;AAAA,IACvC,QAAQ,QAAQ,WAAW;AAAA,EAC7B;AACF;AAMO,IAAM,qBAAN,MAAyB;AAAA,EAmB9B,YACmB,SACA,QAA8B,WAC/C;AAFiB;AACA;AAnBnB,SAAiB,UAAU,oBAAI,IAA+B;AAC9D,SAAiB,WAAW,oBAAI,IAA2B;AAG3D,SAAQ,WAA0B,QAAQ,QAAQ;AAClD,SAAQ,UAAyB,QAAQ,QAAQ;AAEjD,SAAQ,aAAa;AAMrB,SAAQ,gBAAgB;AACxB,SAAQ,kBAAkB;AAE1B,SAAQ,SAAS;AAKf,SAAK,UAAU;AAAA,MACb,QAAQ,YAAY;AAClB,cAAM,KAAK;AACX,eAAO,KAAK,UAAU,KAAK,QAAQ,OAAO,IAAI,CAAC;AAAA,MACjD;AAAA,MACA,MAAM,OAAO,UAAU;AACrB,cAAM,KAAK;AACX,YAAI,KAAK,qBAAqB,KAAK,WAAY;AAC/C,YAAI,KAAK,QAAS,OAAM,KAAK,QAAQ,KAAK,KAAK;AAAA,MACjD;AAAA,MACA,OAAO,CAAC,UAAU;AAChB,YAAI,KAAK,eAAe;AACtB,eAAK,eAAe;AACpB,eAAK,kBAAkB;AACvB,gBAAM,WAAW,KAAK,WAAW,KAAK;AACtC,cAAI,UAAU;AACZ,iBAAK,qBAAqB,SAAS,MAAM,KAAK;AAC9C,iBAAK,mBAAmB,KAAK;AAAA,UAC/B;AACA,iBAAO,KAAK,QAAQ,KAAK,MAAM,KAAK,sBAAsB,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;AAAA,QACtF;AACA,cAAM,aAAa,KAAK,WAAW,KAAK;AACxC,YAAI,CAAC,WAAY,QAAO,QAAQ,QAAQ,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;AAClE,aAAK,mBAAmB,KAAK;AAC7B,eAAO,WAAW,MAAM,KAAK;AAAA,MAC/B;AAAA,MACA,cAAc,YAAY;AACxB,cAAM,KAAK;AACX,YAAI,KAAK,QAAS,QAAO,KAAK,QAAQ,aAAa;AACnD,cAAM,UAAU,KAAK;AACrB,YAAI,CAAC,QAAS,QAAO;AACrB,eAAO,QAAQ,OAAO,CAAC,UAAU;AAC/B,gBAAM,SAAS,MAAM,QAAQ;AAC7B,gBAAM,UAAU,MAAM,QAAQ;AAAA,YAAO,CAAC,WACpC,OAAO,UAAU,eAChB,OAAO,UAAU,eAAe,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,OAAO,EAAE,KAAK,KAAK;AAAA,UACpF;AACA,iBAAO,SAAS,MAAM,QAAQ;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,MACA,OAAO,YAAY;AACjB,aAAK,SAAS;AACd,aAAK,OAAO;AACZ,cAAM,KAAK,QAAQ,MAAM,MAAM,MAAS;AACxC,cAAM,KAAK;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAA6C;AACnD,WAAQ,KAAK,aAAL,KAAK,WAAa,OAAO,wCAAwC;AAAA,EAC3E;AAAA,EAEQ,YAAkB;AACxB,eAAW,MAAM,KAAK,SAAS,KAAK,EAAG,IAAG,eAAe,IAAI;AAC7D,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA,EAEA,KAAK,OAAiD;AACpD,QAAI,KAAK,UAAU,CAAC,KAAK,QAAQ,QAAS,QAAO,QAAQ,QAAQ;AACjE,SAAK,gBAAgB;AACrB,UAAM,aAAa,EAAE,KAAK;AAC1B,SAAK,mBAAmB;AACxB,UAAM,EAAE,SAAS,QAAQ,IAAI;AAC7B,UAAM,OAAO,CAAC,UAAkC;AAC9C,UAAI;AAAE,aAAK,QAAQ,UAAU,KAAK;AAAA,MAAG,QAAQ;AAAA,MAAmD;AAChG,YAAM,EAAE,MAAM,GAAG,KAAK,IAAI;AAC1B,cAAQ,KAAK,MAAM,IAAI;AAAA,IACzB;AACA,UAAM,WAAW,KAAK;AACtB,SAAK,UAAU;AACf,eAAW,MAAM,KAAK,SAAS,KAAK,EAAG,IAAG,eAAe,IAAI;AAC7D,SAAK,eAAe,QAAQ;AAC5B,UAAM,QAAQ,WAAW,SAAS,MAAM,EAAE,MAAM,MAAM,MAAS,IAAI,QAAQ,QAAQ;AACnF,SAAK,WAAW,KAAK,SAAS,KAAK,MAAM,KAAK;AAC9C,UAAM,gBAAgB,KAAK;AAC3B,SAAK,UAAU,KAAK,aAAa,EAAE,KAAK,CAAC,aAAa;AACpD,UAAI,eAAe,KAAK,cAAc,KAAK,OAAQ;AACnD,UAAI;AACJ,UAAI;AACF,mBAAW,SAAS,6BAA6B;AAAA,UAC/C,MAAM,QAAQ,MAAM,CAAC;AAAA,UACrB,OAAO,QAAQ;AAAA,UACf,WAAW,MAAM,iBAAa,wBAAO,MAAM,SAAU,MAAM,OAAQ;AAAA,QACrE,CAAC;AAAA,MACH,QAAQ;AACN,mBAAW,UAAU,KAAK,QAAQ,UAAU;AAC1C,eAAK,EAAE,MAAM,qBAAqB,IAAI,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,QAAQ,SAAS,SAAS,QAAQ,OAAO,eAAe,MAAM,iBAAiB,CAAC;AAAA,QACzK;AACA;AAAA,MACF;AACA,YAAM,MAAM,SAAS,uBAAuB,QAAQ;AACpD,UAAI,UAAU,KAAK,QAAQ,IAAI,GAAG;AAClC,UAAI,CAAC,SAAS;AACZ,kBAAU,KAAK,QAAQ,QAAQ,gBAAgB,QAAQ,KAAK,SAAS,yBAAyB,QAAQ;AACtG,aAAK,QAAQ,IAAI,KAAK,OAAO;AAAA,MAC/B;AACA,WAAK,cAAc;AACnB,YAAM,aAAa,SAAS,oBAAoB;AAAA,QAC9C,SAAS,gBAAgB,KAAK,OAAO;AAAA,QACrC,MAAM,KAAK,QAAQ,QAAQ;AAAA,QAC3B,SAAS,KAAK,QAAQ;AAAA,QACtB;AAAA,QACA,SAAS,EAAE,IAAI,QAAQ,eAAe,KAAK,QAAQ,oBAAoB,OAAO,QAAQ,QAAQ;AAAA,QAC9F,WAAW,MAAM;AAAA,QACjB;AAAA,QACA,WAAW,KAAK;AAAA,QAChB;AAAA,QACA,OAAO,QAAQ;AAAA,QACf,UAAU;AAAA,MACZ,CAAC;AACD,WAAK,UAAU;AACf,WAAK,iBAAiB;AACtB,WAAK,eAAe;AACpB,iBAAW,CAAC,IAAI,KAAK,KAAK,KAAK,UAAU;AACvC,WAAG,eAAe,UAAU,KAAK,eAAe,aAAa,IAAI;AAAA,MACnE;AACA,iBAAW,UAAU,MAAM,UAAW,MAAK,OAAO,OAAO,OAAO,OAAO,EAAE;AACzE,UAAI,KAAK,iBAAiB;AACxB,aAAK,qBAAqB,WAAW,MAAM,KAAK,YAAY;AAC5D,aAAK,mBAAmB;AACxB,aAAK,eAAe;AACpB,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF,CAAC,EAAE,QAAQ,MAAM;AACf,UAAI,eAAe,KAAK,WAAY,MAAK,gBAAgB;AAAA,IAC3D,CAAC;AACD,eAAW,MAAM,KAAK,SAAS,KAAK,EAAG,IAAG,sBAAsB,KAAK,OAAO;AAC5E,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAO,OAAe,IAAkC;AACtD,SAAK,SAAS,IAAI,IAAI,KAAK;AAC3B,OAAG,eAAe,UAAU,KAAK,eAAe,KAAK,WAAW,OAAO,IAAI;AAC3E,WAAO,KAAK,QAAQ,KAAK,MAAM;AAC7B,SAAG,eAAe,UAAU,KAAK,eAAe,KAAK,WAAW,OAAO,IAAI;AAAA,IAC7E,CAAC;AAAA,EACH;AAAA,EAEA,SAAwB;AACtB,SAAK;AACL,SAAK,gBAAgB;AACrB,SAAK,kBAAkB;AACvB,UAAM,aAAa,KAAK;AACxB,SAAK,UAAU;AACf,SAAK,eAAe;AACpB,SAAK,UAAU;AACf,UAAM,UAAU,KAAK;AACrB,UAAM,QAAQ,QAAQ,KAAK,MAAM,YAAY,MAAM,CAAC,EAAE,MAAM,MAAM,MAAS;AAC3E,SAAK,WAAW,KAAK,SAAS,KAAK,MAAM,KAAK;AAC9C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,gBAAiD;AACnD,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// src/replication/runtime.ts
|
|
2
|
+
import { pkhDid } from "@tinycloud/sdk-core";
|
|
3
|
+
var scheduler = {
|
|
4
|
+
now: () => Date.now(),
|
|
5
|
+
setTimeout(fn, ms) {
|
|
6
|
+
const timer = setTimeout(fn, ms);
|
|
7
|
+
timer.unref?.();
|
|
8
|
+
return () => clearTimeout(timer);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function resolvedOptions(options) {
|
|
12
|
+
return {
|
|
13
|
+
...options,
|
|
14
|
+
syncIntervalMs: options.syncIntervalMs ?? 6e4,
|
|
15
|
+
maxStalenessMs: options.maxStalenessMs ?? 12e4,
|
|
16
|
+
staleSyncTimeoutMs: options.staleSyncTimeoutMs ?? 5e3,
|
|
17
|
+
allowSecrets: options.allowSecrets === true,
|
|
18
|
+
verify: options.verify === true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var ReplicationRuntime = class {
|
|
22
|
+
constructor(options, clock = scheduler) {
|
|
23
|
+
this.options = options;
|
|
24
|
+
this.clock = clock;
|
|
25
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
26
|
+
this.attached = /* @__PURE__ */ new Map();
|
|
27
|
+
this.previous = Promise.resolve();
|
|
28
|
+
this.binding = Promise.resolve();
|
|
29
|
+
this.generation = 0;
|
|
30
|
+
this.bindingActive = false;
|
|
31
|
+
this.pendingPurgeSet = false;
|
|
32
|
+
this.closed = false;
|
|
33
|
+
this.control = {
|
|
34
|
+
status: async () => {
|
|
35
|
+
await this.binding;
|
|
36
|
+
return this.current ? this.current.status() : [];
|
|
37
|
+
},
|
|
38
|
+
sync: async (input) => {
|
|
39
|
+
await this.binding;
|
|
40
|
+
if (this.purgedGeneration === this.generation) return;
|
|
41
|
+
if (this.current) await this.current.sync(input);
|
|
42
|
+
},
|
|
43
|
+
purge: (input) => {
|
|
44
|
+
if (this.bindingActive) {
|
|
45
|
+
this.pendingPurge = input;
|
|
46
|
+
this.pendingPurgeSet = true;
|
|
47
|
+
const existing = this.current ?? this.lastController;
|
|
48
|
+
if (existing) {
|
|
49
|
+
this.pendingPurgeResult = existing.purge(input);
|
|
50
|
+
this.purgedGeneration = this.generation;
|
|
51
|
+
}
|
|
52
|
+
return this.binding.then(() => this.pendingPurgeResult ?? { purged: [], failed: [] });
|
|
53
|
+
}
|
|
54
|
+
const controller = this.current ?? this.lastController;
|
|
55
|
+
if (!controller) return Promise.resolve({ purged: [], failed: [] });
|
|
56
|
+
this.purgedGeneration = this.generation;
|
|
57
|
+
return controller.purge(input);
|
|
58
|
+
},
|
|
59
|
+
clearPending: async () => {
|
|
60
|
+
await this.binding;
|
|
61
|
+
if (this.current) return this.current.clearPending();
|
|
62
|
+
const pending = this.lastPending;
|
|
63
|
+
if (!pending) return 0;
|
|
64
|
+
return pending.update((state) => {
|
|
65
|
+
const before = state.records.length;
|
|
66
|
+
state.records = state.records.filter(
|
|
67
|
+
(record) => record.state === "committed" || record.state === "in_flight" && this.clock.now() - Date.parse(record.at) <= 10 * 6e4
|
|
68
|
+
);
|
|
69
|
+
return before - state.records.length;
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
close: async () => {
|
|
73
|
+
this.closed = true;
|
|
74
|
+
this.unbind();
|
|
75
|
+
await this.binding.catch(() => void 0);
|
|
76
|
+
await this.previous;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
loadServices() {
|
|
81
|
+
return this.services ?? (this.services = import("@tinycloud/sdk-services/kv/replication"));
|
|
82
|
+
}
|
|
83
|
+
detachAll() {
|
|
84
|
+
for (const kv of this.attached.keys()) kv.setReadThrough(null);
|
|
85
|
+
this.attached.clear();
|
|
86
|
+
}
|
|
87
|
+
bind(input) {
|
|
88
|
+
if (this.closed || !this.options.enabled) return Promise.resolve();
|
|
89
|
+
this.bindingActive = true;
|
|
90
|
+
const generation = ++this.generation;
|
|
91
|
+
this.purgedGeneration = void 0;
|
|
92
|
+
const { context, session } = input;
|
|
93
|
+
const emit = (event) => {
|
|
94
|
+
try {
|
|
95
|
+
this.options.onEvent?.(event);
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
const { type, ...data } = event;
|
|
99
|
+
context.emit(type, data);
|
|
100
|
+
};
|
|
101
|
+
const previous = this.current;
|
|
102
|
+
this.current = void 0;
|
|
103
|
+
for (const kv of this.attached.keys()) kv.setReadThrough(null);
|
|
104
|
+
this.primarySpace = session.spaceId;
|
|
105
|
+
const close = previous ? previous.close().catch(() => void 0) : Promise.resolve();
|
|
106
|
+
this.previous = this.previous.then(() => close);
|
|
107
|
+
const previousClose = this.previous;
|
|
108
|
+
this.binding = this.loadServices().then((services) => {
|
|
109
|
+
if (generation !== this.generation || this.closed) return;
|
|
110
|
+
let identity;
|
|
111
|
+
try {
|
|
112
|
+
identity = services.canonicalReplicationIdentity({
|
|
113
|
+
host: context.hosts[0],
|
|
114
|
+
space: session.spaceId,
|
|
115
|
+
principal: input.principal ?? pkhDid(input.address, input.chainId)
|
|
116
|
+
});
|
|
117
|
+
} catch {
|
|
118
|
+
for (const prefix of this.options.prefixes) {
|
|
119
|
+
emit({ type: "replication.state", at: new Date(this.clock.now()).toISOString(), space: session.spaceId, replica: prefix, state: "unavailable", code: "CONFIG_INVALID" });
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const key = services.replicationIdentityKey(identity);
|
|
124
|
+
let pending = this.pending.get(key);
|
|
125
|
+
if (!pending) {
|
|
126
|
+
pending = this.options.storage.pendingWrites?.(identity) ?? services.createMemoryPendingStore(identity);
|
|
127
|
+
this.pending.set(key, pending);
|
|
128
|
+
}
|
|
129
|
+
this.lastPending = pending;
|
|
130
|
+
const controller = services.createKVReplication({
|
|
131
|
+
options: resolvedOptions(this.options),
|
|
132
|
+
mode: this.options.mode ?? "background",
|
|
133
|
+
storage: this.options.storage,
|
|
134
|
+
identity,
|
|
135
|
+
session: { id: session.delegationCid, did: session.verificationMethod, space: session.spaceId },
|
|
136
|
+
authority: input.authority,
|
|
137
|
+
pending,
|
|
138
|
+
scheduler: this.clock,
|
|
139
|
+
emit,
|
|
140
|
+
fetch: context.fetch,
|
|
141
|
+
previous: previousClose
|
|
142
|
+
});
|
|
143
|
+
this.current = controller;
|
|
144
|
+
this.lastController = controller;
|
|
145
|
+
this.lastIdentity = identity;
|
|
146
|
+
for (const [kv, space] of this.attached) {
|
|
147
|
+
kv.setReadThrough(space === this.primarySpace ? controller : null);
|
|
148
|
+
}
|
|
149
|
+
for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);
|
|
150
|
+
if (this.pendingPurgeSet) {
|
|
151
|
+
this.pendingPurgeResult = controller.purge(this.pendingPurge);
|
|
152
|
+
this.purgedGeneration = generation;
|
|
153
|
+
this.pendingPurge = void 0;
|
|
154
|
+
this.pendingPurgeSet = false;
|
|
155
|
+
}
|
|
156
|
+
}).finally(() => {
|
|
157
|
+
if (generation === this.generation) this.bindingActive = false;
|
|
158
|
+
});
|
|
159
|
+
for (const kv of this.attached.keys()) kv.setReadThroughReady?.(this.binding);
|
|
160
|
+
return this.binding;
|
|
161
|
+
}
|
|
162
|
+
attach(space, kv) {
|
|
163
|
+
this.attached.set(kv, space);
|
|
164
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);
|
|
165
|
+
return this.binding.then(() => {
|
|
166
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
unbind() {
|
|
170
|
+
this.generation++;
|
|
171
|
+
this.bindingActive = false;
|
|
172
|
+
this.pendingPurgeSet = false;
|
|
173
|
+
const controller = this.current;
|
|
174
|
+
this.current = void 0;
|
|
175
|
+
this.primarySpace = void 0;
|
|
176
|
+
this.detachAll();
|
|
177
|
+
const binding = this.binding;
|
|
178
|
+
const close = binding.then(() => controller?.close()).catch(() => void 0);
|
|
179
|
+
this.previous = this.previous.then(() => close);
|
|
180
|
+
return this.previous;
|
|
181
|
+
}
|
|
182
|
+
get boundIdentity() {
|
|
183
|
+
return this.lastIdentity;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
export {
|
|
187
|
+
ReplicationRuntime
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/replication/runtime.ts"],"sourcesContent":["import { pkhDid } from \"@tinycloud/sdk-core\";\nimport type { FetchFunction, ServiceContext, ServiceSession } from \"@tinycloud/sdk-core\";\nimport type {\n KVReadThrough,\n KVReplicaStorage,\n KVReplicationController,\n PendingWriteStore,\n ReplicationAuthority,\n ReplicationControl,\n ReplicationEvent,\n ReplicationIdentity,\n ReplicationOptions,\n ReplicationScheduler,\n} from \"@tinycloud/sdk-services\";\ntype ReplicationServices = typeof import(\"@tinycloud/sdk-services/kv/replication\");\n\nexport interface ReplicationRuntimeOptions extends ReplicationOptions {\n storage: KVReplicaStorage;\n mode?: \"background\" | \"foreground\";\n}\n\nexport interface ReplicationRuntimeBinding {\n context: ServiceContext;\n session: ServiceSession;\n address?: string;\n chainId?: number;\n principal?: string;\n authority: ReplicationAuthority;\n primaryKV: Array<{ space: string; kv: { setReadThrough(readThrough: KVReadThrough | null): void } }>;\n}\n\nconst scheduler: ReplicationScheduler = {\n now: () => Date.now(),\n setTimeout(fn, ms) {\n const timer = setTimeout(fn, ms);\n timer.unref?.();\n return () => clearTimeout(timer);\n },\n};\n\nfunction resolvedOptions(options: ReplicationRuntimeOptions) {\n return {\n ...options,\n syncIntervalMs: options.syncIntervalMs ?? 60_000,\n maxStalenessMs: options.maxStalenessMs ?? 120_000,\n staleSyncTimeoutMs: options.staleSyncTimeoutMs ?? 5_000,\n allowSecrets: options.allowSecrets === true,\n verify: options.verify === true,\n };\n}\ntype ReadThroughKV = {\n setReadThrough(readThrough: KVReadThrough | null): void;\n setReadThroughReady?(ready: Promise<unknown>): void;\n};\n\nexport class ReplicationRuntime {\n readonly control: ReplicationControl;\n private readonly pending = new Map<string, PendingWriteStore>();\n private readonly attached = new Map<ReadThroughKV, string>();\n private current?: KVReplicationController;\n private lastController?: KVReplicationController;\n private previous: Promise<void> = Promise.resolve();\n private binding: Promise<void> = Promise.resolve();\n private services?: Promise<ReplicationServices>;\n private generation = 0;\n private lastIdentity?: ReplicationIdentity;\n private lastPending?: PendingWriteStore;\n private primarySpace?: string;\n private pendingPurge?: Parameters<KVReplicationController[\"purge\"]>[0];\n private pendingPurgeResult?: ReturnType<KVReplicationController[\"purge\"]>;\n private bindingActive = false;\n private pendingPurgeSet = false;\n private purgedGeneration?: number;\n private closed = false;\n constructor(\n private readonly options: ReplicationRuntimeOptions,\n private readonly clock: ReplicationScheduler = scheduler,\n ) {\n this.control = {\n status: async () => {\n await this.binding;\n return this.current ? this.current.status() : [];\n },\n sync: async (input) => {\n await this.binding;\n if (this.purgedGeneration === this.generation) return;\n if (this.current) await this.current.sync(input);\n },\n purge: (input) => {\n if (this.bindingActive) {\n this.pendingPurge = input;\n this.pendingPurgeSet = true;\n const existing = this.current ?? this.lastController;\n if (existing) {\n this.pendingPurgeResult = existing.purge(input);\n this.purgedGeneration = this.generation;\n }\n return this.binding.then(() => this.pendingPurgeResult ?? { purged: [], failed: [] });\n }\n const controller = this.current ?? this.lastController;\n if (!controller) return Promise.resolve({ purged: [], failed: [] });\n this.purgedGeneration = this.generation;\n return controller.purge(input);\n },\n clearPending: async () => {\n await this.binding;\n if (this.current) return this.current.clearPending();\n const pending = this.lastPending;\n if (!pending) return 0;\n return pending.update((state) => {\n const before = state.records.length;\n state.records = state.records.filter((record) =>\n record.state === \"committed\" ||\n (record.state === \"in_flight\" && this.clock.now() - Date.parse(record.at) <= 10 * 60_000),\n );\n return before - state.records.length;\n });\n },\n close: async () => {\n this.closed = true;\n this.unbind();\n await this.binding.catch(() => undefined);\n await this.previous;\n },\n };\n }\n\n private loadServices(): Promise<ReplicationServices> {\n return (this.services ??= import(\"@tinycloud/sdk-services/kv/replication\"));\n }\n\n private detachAll(): void {\n for (const kv of this.attached.keys()) kv.setReadThrough(null);\n this.attached.clear();\n }\n\n bind(input: ReplicationRuntimeBinding): Promise<void> {\n if (this.closed || !this.options.enabled) return Promise.resolve();\n this.bindingActive = true;\n const generation = ++this.generation;\n this.purgedGeneration = undefined;\n const { context, session } = input;\n const emit = (event: ReplicationEvent): void => {\n try { this.options.onEvent?.(event); } catch { /* Observability must not affect KV behavior. */ }\n const { type, ...data } = event;\n context.emit(type, data);\n };\n const previous = this.current;\n this.current = undefined;\n for (const kv of this.attached.keys()) kv.setReadThrough(null);\n this.primarySpace = session.spaceId;\n const close = previous ? previous.close().catch(() => undefined) : Promise.resolve();\n this.previous = this.previous.then(() => close);\n const previousClose = this.previous;\n this.binding = this.loadServices().then((services) => {\n if (generation !== this.generation || this.closed) return;\n let identity: ReplicationIdentity;\n try {\n identity = services.canonicalReplicationIdentity({\n host: context.hosts[0]!,\n space: session.spaceId,\n principal: input.principal ?? pkhDid(input.address!, input.chainId!),\n });\n } catch {\n for (const prefix of this.options.prefixes) {\n emit({ type: \"replication.state\", at: new Date(this.clock.now()).toISOString(), space: session.spaceId, replica: prefix, state: \"unavailable\", code: \"CONFIG_INVALID\" });\n }\n return;\n }\n const key = services.replicationIdentityKey(identity);\n let pending = this.pending.get(key);\n if (!pending) {\n pending = this.options.storage.pendingWrites?.(identity) ?? services.createMemoryPendingStore(identity);\n this.pending.set(key, pending);\n }\n this.lastPending = pending;\n const controller = services.createKVReplication({\n options: resolvedOptions(this.options),\n mode: this.options.mode ?? \"background\",\n storage: this.options.storage,\n identity,\n session: { id: session.delegationCid, did: session.verificationMethod, space: session.spaceId },\n authority: input.authority,\n pending,\n scheduler: this.clock,\n emit,\n fetch: context.fetch as FetchFunction,\n previous: previousClose,\n });\n this.current = controller;\n this.lastController = controller;\n this.lastIdentity = identity;\n for (const [kv, space] of this.attached) {\n kv.setReadThrough(space === this.primarySpace ? controller : null);\n }\n for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);\n if (this.pendingPurgeSet) {\n this.pendingPurgeResult = controller.purge(this.pendingPurge);\n this.purgedGeneration = generation;\n this.pendingPurge = undefined;\n this.pendingPurgeSet = false;\n }\n }).finally(() => {\n if (generation === this.generation) this.bindingActive = false;\n });\n for (const kv of this.attached.keys()) kv.setReadThroughReady?.(this.binding);\n return this.binding;\n }\n\n attach(space: string, kv: ReadThroughKV): Promise<void> {\n this.attached.set(kv, space);\n kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);\n return this.binding.then(() => {\n kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);\n });\n }\n\n unbind(): Promise<void> {\n this.generation++;\n this.bindingActive = false;\n this.pendingPurgeSet = false;\n const controller = this.current;\n this.current = undefined;\n this.primarySpace = undefined;\n this.detachAll();\n const binding = this.binding;\n const close = binding.then(() => controller?.close()).catch(() => undefined);\n this.previous = this.previous.then(() => close);\n return this.previous;\n }\n\n get boundIdentity(): ReplicationIdentity | undefined {\n return this.lastIdentity;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ServiceContext, ServiceSession } from '@tinycloud/sdk-core';
|
|
2
|
+
import { ReplicationControl, ReplicationOptions, KVReplicaStorage, ReplicationScheduler, ReplicationAuthority, KVReadThrough, ReplicationIdentity } from '@tinycloud/sdk-services';
|
|
3
|
+
|
|
4
|
+
interface ReplicationRuntimeOptions extends ReplicationOptions {
|
|
5
|
+
storage: KVReplicaStorage;
|
|
6
|
+
mode?: "background" | "foreground";
|
|
7
|
+
}
|
|
8
|
+
interface ReplicationRuntimeBinding {
|
|
9
|
+
context: ServiceContext;
|
|
10
|
+
session: ServiceSession;
|
|
11
|
+
address?: string;
|
|
12
|
+
chainId?: number;
|
|
13
|
+
principal?: string;
|
|
14
|
+
authority: ReplicationAuthority;
|
|
15
|
+
primaryKV: Array<{
|
|
16
|
+
space: string;
|
|
17
|
+
kv: {
|
|
18
|
+
setReadThrough(readThrough: KVReadThrough | null): void;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
type ReadThroughKV = {
|
|
23
|
+
setReadThrough(readThrough: KVReadThrough | null): void;
|
|
24
|
+
setReadThroughReady?(ready: Promise<unknown>): void;
|
|
25
|
+
};
|
|
26
|
+
declare class ReplicationRuntime {
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly clock;
|
|
29
|
+
readonly control: ReplicationControl;
|
|
30
|
+
private readonly pending;
|
|
31
|
+
private readonly attached;
|
|
32
|
+
private current?;
|
|
33
|
+
private lastController?;
|
|
34
|
+
private previous;
|
|
35
|
+
private binding;
|
|
36
|
+
private services?;
|
|
37
|
+
private generation;
|
|
38
|
+
private lastIdentity?;
|
|
39
|
+
private lastPending?;
|
|
40
|
+
private primarySpace?;
|
|
41
|
+
private pendingPurge?;
|
|
42
|
+
private pendingPurgeResult?;
|
|
43
|
+
private bindingActive;
|
|
44
|
+
private pendingPurgeSet;
|
|
45
|
+
private purgedGeneration?;
|
|
46
|
+
private closed;
|
|
47
|
+
constructor(options: ReplicationRuntimeOptions, clock?: ReplicationScheduler);
|
|
48
|
+
private loadServices;
|
|
49
|
+
private detachAll;
|
|
50
|
+
bind(input: ReplicationRuntimeBinding): Promise<void>;
|
|
51
|
+
attach(space: string, kv: ReadThroughKV): Promise<void>;
|
|
52
|
+
unbind(): Promise<void>;
|
|
53
|
+
get boundIdentity(): ReplicationIdentity | undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type __runtime_ReplicationRuntime = ReplicationRuntime;
|
|
57
|
+
declare const __runtime_ReplicationRuntime: typeof ReplicationRuntime;
|
|
58
|
+
type __runtime_ReplicationRuntimeBinding = ReplicationRuntimeBinding;
|
|
59
|
+
type __runtime_ReplicationRuntimeOptions = ReplicationRuntimeOptions;
|
|
60
|
+
declare namespace __runtime {
|
|
61
|
+
export { __runtime_ReplicationRuntime as ReplicationRuntime, type __runtime_ReplicationRuntimeBinding as ReplicationRuntimeBinding, type __runtime_ReplicationRuntimeOptions as ReplicationRuntimeOptions };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { ReplicationRuntime as R, __runtime as _, type ReplicationRuntimeBinding as a, type ReplicationRuntimeOptions as b };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ServiceContext, ServiceSession } from '@tinycloud/sdk-core';
|
|
2
|
+
import { ReplicationControl, ReplicationOptions, KVReplicaStorage, ReplicationScheduler, ReplicationAuthority, KVReadThrough, ReplicationIdentity } from '@tinycloud/sdk-services';
|
|
3
|
+
|
|
4
|
+
interface ReplicationRuntimeOptions extends ReplicationOptions {
|
|
5
|
+
storage: KVReplicaStorage;
|
|
6
|
+
mode?: "background" | "foreground";
|
|
7
|
+
}
|
|
8
|
+
interface ReplicationRuntimeBinding {
|
|
9
|
+
context: ServiceContext;
|
|
10
|
+
session: ServiceSession;
|
|
11
|
+
address?: string;
|
|
12
|
+
chainId?: number;
|
|
13
|
+
principal?: string;
|
|
14
|
+
authority: ReplicationAuthority;
|
|
15
|
+
primaryKV: Array<{
|
|
16
|
+
space: string;
|
|
17
|
+
kv: {
|
|
18
|
+
setReadThrough(readThrough: KVReadThrough | null): void;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
type ReadThroughKV = {
|
|
23
|
+
setReadThrough(readThrough: KVReadThrough | null): void;
|
|
24
|
+
setReadThroughReady?(ready: Promise<unknown>): void;
|
|
25
|
+
};
|
|
26
|
+
declare class ReplicationRuntime {
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly clock;
|
|
29
|
+
readonly control: ReplicationControl;
|
|
30
|
+
private readonly pending;
|
|
31
|
+
private readonly attached;
|
|
32
|
+
private current?;
|
|
33
|
+
private lastController?;
|
|
34
|
+
private previous;
|
|
35
|
+
private binding;
|
|
36
|
+
private services?;
|
|
37
|
+
private generation;
|
|
38
|
+
private lastIdentity?;
|
|
39
|
+
private lastPending?;
|
|
40
|
+
private primarySpace?;
|
|
41
|
+
private pendingPurge?;
|
|
42
|
+
private pendingPurgeResult?;
|
|
43
|
+
private bindingActive;
|
|
44
|
+
private pendingPurgeSet;
|
|
45
|
+
private purgedGeneration?;
|
|
46
|
+
private closed;
|
|
47
|
+
constructor(options: ReplicationRuntimeOptions, clock?: ReplicationScheduler);
|
|
48
|
+
private loadServices;
|
|
49
|
+
private detachAll;
|
|
50
|
+
bind(input: ReplicationRuntimeBinding): Promise<void>;
|
|
51
|
+
attach(space: string, kv: ReadThroughKV): Promise<void>;
|
|
52
|
+
unbind(): Promise<void>;
|
|
53
|
+
get boundIdentity(): ReplicationIdentity | undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type __runtime_ReplicationRuntime = ReplicationRuntime;
|
|
57
|
+
declare const __runtime_ReplicationRuntime: typeof ReplicationRuntime;
|
|
58
|
+
type __runtime_ReplicationRuntimeBinding = ReplicationRuntimeBinding;
|
|
59
|
+
type __runtime_ReplicationRuntimeOptions = ReplicationRuntimeOptions;
|
|
60
|
+
declare namespace __runtime {
|
|
61
|
+
export { __runtime_ReplicationRuntime as ReplicationRuntime, type __runtime_ReplicationRuntimeBinding as ReplicationRuntimeBinding, type __runtime_ReplicationRuntimeOptions as ReplicationRuntimeOptions };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { ReplicationRuntime as R, __runtime as _, type ReplicationRuntimeBinding as a, type ReplicationRuntimeOptions as b };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinycloud/node-sdk",
|
|
3
|
-
"version": "3.2.0-beta.
|
|
3
|
+
"version": "3.2.0-beta.3",
|
|
4
4
|
"description": "TinyCloud SDK for Node.js with configurable authorization strategies",
|
|
5
5
|
"author": "TinyCloud, Inc.",
|
|
6
6
|
"license": "EGPL",
|
|
@@ -18,6 +18,26 @@
|
|
|
18
18
|
"types": "./dist/core.d.ts",
|
|
19
19
|
"import": "./dist/core.js",
|
|
20
20
|
"require": "./dist/core.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./replication/runtime": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./dist/replication/runtime.d.ts",
|
|
25
|
+
"default": "./dist/replication/runtime.js"
|
|
26
|
+
},
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./dist/replication/runtime.d.cts",
|
|
29
|
+
"default": "./dist/replication/runtime.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"./replication/authority": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/replication/authority.d.ts",
|
|
35
|
+
"default": "./dist/replication/authority.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/replication/authority.d.cts",
|
|
39
|
+
"default": "./dist/replication/authority.cjs"
|
|
40
|
+
}
|
|
21
41
|
}
|
|
22
42
|
},
|
|
23
43
|
"engines": {
|
|
@@ -30,11 +50,13 @@
|
|
|
30
50
|
"build": "tsup",
|
|
31
51
|
"clean": "rm -rf dist",
|
|
32
52
|
"test": "bun test src/",
|
|
53
|
+
"typecheck:replica-contract": "tsc --noEmit -p tsconfig.replica-contract.json",
|
|
33
54
|
"test:integration": "echo 'Integration tests live in tests/node-sdk workspace'"
|
|
34
55
|
},
|
|
35
56
|
"dependencies": {
|
|
36
|
-
"@tinycloud/
|
|
37
|
-
"@tinycloud/sdk-
|
|
57
|
+
"@tinycloud/replica": "0.1.1-beta.0",
|
|
58
|
+
"@tinycloud/sdk-services": "3.2.0-beta.3",
|
|
59
|
+
"@tinycloud/sdk-core": "3.2.0-beta.3",
|
|
38
60
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
39
61
|
"siwe": "^3.0.0"
|
|
40
62
|
},
|