@tinycloud/node-sdk 3.2.6-beta.6 → 3.3.0-beta.7
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/{WasmKeyProvider-DEdSyQ0I.d.cts → TinyCloudNode-BchWB_zv.d.cts} +162 -283
- package/dist/{WasmKeyProvider-DEdSyQ0I.d.ts → TinyCloudNode-BchWB_zv.d.ts} +162 -283
- package/dist/WasmKeyProvider-5Ee7fcAw.d.cts +217 -0
- package/dist/WasmKeyProvider-5Ee7fcAw.d.ts +217 -0
- package/dist/authority-4HXYteKF.d.ts +118 -0
- package/dist/authority-lrA-698X.d.cts +118 -0
- package/dist/core.cjs +368 -147
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +37 -4
- package/dist/core.d.ts +37 -4
- package/dist/core.js +374 -155
- package/dist/core.js.map +1 -1
- package/dist/gated-network-BEF2XSTC.d.cts +16 -0
- package/dist/gated-network-BEF2XSTC.d.ts +16 -0
- package/dist/index.cjs +2702 -524
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -89
- package/dist/index.d.ts +35 -89
- package/dist/index.js +2702 -530
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.cjs +153 -15
- package/dist/replication/authority.cjs.map +1 -1
- package/dist/replication/authority.d.cts +4 -1
- package/dist/replication/authority.d.ts +4 -1
- package/dist/replication/authority.js +147 -14
- package/dist/replication/authority.js.map +1 -1
- package/dist/replication/gated-network.cjs +90 -0
- package/dist/replication/gated-network.cjs.map +1 -0
- package/dist/replication/gated-network.d.cts +2 -0
- package/dist/replication/gated-network.d.ts +2 -0
- package/dist/replication/gated-network.js +65 -0
- package/dist/replication/gated-network.js.map +1 -0
- package/dist/replication/runtime.cjs +84 -6
- package/dist/replication/runtime.cjs.map +1 -1
- package/dist/replication/runtime.d.cts +2 -1
- package/dist/replication/runtime.d.ts +2 -1
- package/dist/replication/runtime.js +84 -6
- package/dist/replication/runtime.js.map +1 -1
- package/dist/{runtime-B5P5SRpC.d.ts → runtime-D1o9Eybt.d.cts} +14 -3
- package/dist/{runtime-B5P5SRpC.d.cts → runtime-v62FzQHz.d.ts} +14 -3
- package/package.json +14 -4
- package/dist/authority-DDGtccBK.d.cts +0 -96
- package/dist/authority-DDGtccBK.d.ts +0 -96
|
@@ -67,10 +67,23 @@ var ReplicationRuntime = class {
|
|
|
67
67
|
this.control = {
|
|
68
68
|
status: async () => {
|
|
69
69
|
await this.binding;
|
|
70
|
+
if (this.gatedReason) return this.options.prefixes.map((prefix) => ({
|
|
71
|
+
prefix,
|
|
72
|
+
state: "gated",
|
|
73
|
+
gate: this.gatedReason,
|
|
74
|
+
errorCode: "REPLICA_GATED",
|
|
75
|
+
pending: { inFlight: 0, committed: 0, ambiguous: 0 },
|
|
76
|
+
pinned: [],
|
|
77
|
+
lagMs: null
|
|
78
|
+
}));
|
|
70
79
|
return this.current ? this.current.status() : [];
|
|
71
80
|
},
|
|
72
81
|
sync: async (input) => {
|
|
73
82
|
await this.binding;
|
|
83
|
+
if (this.gatedReason) throw Object.assign(new Error("Replica restore is gated."), {
|
|
84
|
+
code: "REPLICA_GATED",
|
|
85
|
+
gate: this.gatedReason
|
|
86
|
+
});
|
|
74
87
|
if (this.purgedGeneration === this.generation) return;
|
|
75
88
|
if (this.current) await this.current.sync(input);
|
|
76
89
|
},
|
|
@@ -118,11 +131,59 @@ var ReplicationRuntime = class {
|
|
|
118
131
|
for (const kv of this.attached.keys()) kv.setReadThrough(null);
|
|
119
132
|
this.attached.clear();
|
|
120
133
|
}
|
|
134
|
+
/** Observe a refused restore without opening replica state; loader failure leaves raw network KV usable. */
|
|
135
|
+
bindGatedNetworkOnly(input, loadGatedNetwork) {
|
|
136
|
+
if (this.closed || !this.options.enabled) return Promise.resolve();
|
|
137
|
+
const retired = this.unbind();
|
|
138
|
+
const generation = this.generation;
|
|
139
|
+
this.purgedGeneration = void 0;
|
|
140
|
+
this.pendingPurge = void 0;
|
|
141
|
+
this.pendingPurgeResult = void 0;
|
|
142
|
+
this.pending.clear();
|
|
143
|
+
this.lastController = void 0;
|
|
144
|
+
this.lastPending = void 0;
|
|
145
|
+
this.lastIdentity = void 0;
|
|
146
|
+
this.gatedReason = input.gate;
|
|
147
|
+
this.primarySpace = input.session.spaceId;
|
|
148
|
+
this.binding = retired.then(async () => {
|
|
149
|
+
let create;
|
|
150
|
+
try {
|
|
151
|
+
({ createGatedNetworkReadThrough: create } = await loadGatedNetwork());
|
|
152
|
+
} catch {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (generation !== this.generation || this.closed || typeof create !== "function") return;
|
|
156
|
+
const networkOnly = create({
|
|
157
|
+
space: input.session.spaceId,
|
|
158
|
+
prefixes: this.options.prefixes,
|
|
159
|
+
now: () => this.clock.now(),
|
|
160
|
+
emit: (event) => {
|
|
161
|
+
if (generation !== this.generation || this.closed || this.gated !== networkOnly) return;
|
|
162
|
+
try {
|
|
163
|
+
this.options.onEvent?.(event);
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
const { type, ...data } = event;
|
|
167
|
+
try {
|
|
168
|
+
input.context.emit(type, data);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
this.gated = networkOnly;
|
|
174
|
+
for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);
|
|
175
|
+
});
|
|
176
|
+
return this.binding;
|
|
177
|
+
}
|
|
121
178
|
bind(input) {
|
|
122
179
|
if (this.closed || !this.options.enabled) return Promise.resolve();
|
|
180
|
+
this.gated = void 0;
|
|
181
|
+
this.gatedReason = void 0;
|
|
123
182
|
this.bindingActive = true;
|
|
124
183
|
const generation = ++this.generation;
|
|
125
184
|
this.purgedGeneration = void 0;
|
|
185
|
+
this.unsubscribeConsentExpired?.();
|
|
186
|
+
this.unsubscribeConsentExpired = void 0;
|
|
126
187
|
const { context, session } = input;
|
|
127
188
|
const emit = (event) => {
|
|
128
189
|
try {
|
|
@@ -155,11 +216,17 @@ var ReplicationRuntime = class {
|
|
|
155
216
|
return;
|
|
156
217
|
}
|
|
157
218
|
const key = services.replicationIdentityKey(identity);
|
|
158
|
-
let
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
this.
|
|
219
|
+
let cached = this.pending.get(key);
|
|
220
|
+
const stamp = input.stamp;
|
|
221
|
+
if (!cached || cached.stamp?.incarnation !== stamp?.incarnation || cached.stamp?.lifecycleEpoch !== stamp?.lifecycleEpoch) {
|
|
222
|
+
const store = this.options.storage.pendingWrites?.(identity, { stamp });
|
|
223
|
+
if (!store && this.options.storage.lifecycle) {
|
|
224
|
+
throw Object.assign(new Error("Gated replication requires a stamped pending store"), { code: "REPLICA_GATED", gate: "gate_unavailable" });
|
|
225
|
+
}
|
|
226
|
+
cached = { stamp, store: store ?? services.createMemoryPendingStore(identity) };
|
|
227
|
+
this.pending.set(key, cached);
|
|
162
228
|
}
|
|
229
|
+
const pending = cached.store;
|
|
163
230
|
this.lastPending = pending;
|
|
164
231
|
const controller = services.createKVReplication({
|
|
165
232
|
options: resolvedOptions(this.options),
|
|
@@ -167,6 +234,7 @@ var ReplicationRuntime = class {
|
|
|
167
234
|
storage: this.options.storage,
|
|
168
235
|
identity,
|
|
169
236
|
session: { id: session.delegationCid, did: session.verificationMethod, space: session.spaceId },
|
|
237
|
+
stamp: input.stamp,
|
|
170
238
|
authority: input.authority,
|
|
171
239
|
pending,
|
|
172
240
|
scheduler: this.clock,
|
|
@@ -176,6 +244,9 @@ var ReplicationRuntime = class {
|
|
|
176
244
|
previous: previousClose
|
|
177
245
|
});
|
|
178
246
|
this.current = controller;
|
|
247
|
+
this.unsubscribeConsentExpired = this.options.storage.lifecycle?.subscribeConsentExpired?.(
|
|
248
|
+
(stamp2) => controller.observeConsentExpired(stamp2)
|
|
249
|
+
);
|
|
179
250
|
this.lastController = controller;
|
|
180
251
|
this.lastIdentity = identity;
|
|
181
252
|
for (const [kv, space] of this.attached) {
|
|
@@ -196,17 +267,24 @@ var ReplicationRuntime = class {
|
|
|
196
267
|
}
|
|
197
268
|
attach(space, kv) {
|
|
198
269
|
this.attached.set(kv, space);
|
|
199
|
-
kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);
|
|
270
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? this.gated ?? null : null);
|
|
271
|
+
const generation = this.generation;
|
|
200
272
|
return this.binding.then(() => {
|
|
201
|
-
|
|
273
|
+
if (generation === this.generation && !this.closed && this.attached.get(kv) === space) {
|
|
274
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? this.gated ?? null : null);
|
|
275
|
+
}
|
|
202
276
|
});
|
|
203
277
|
}
|
|
204
278
|
unbind() {
|
|
205
279
|
this.generation++;
|
|
206
280
|
this.bindingActive = false;
|
|
207
281
|
this.pendingPurgeSet = false;
|
|
282
|
+
this.unsubscribeConsentExpired?.();
|
|
283
|
+
this.unsubscribeConsentExpired = void 0;
|
|
208
284
|
const controller = this.current;
|
|
209
285
|
this.current = void 0;
|
|
286
|
+
this.gated = void 0;
|
|
287
|
+
this.gatedReason = void 0;
|
|
210
288
|
this.primarySpace = void 0;
|
|
211
289
|
this.detachAll();
|
|
212
290
|
const binding = this.binding;
|
|
@@ -1 +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 nodeFeatures?: readonly string[];\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 nodeFeatures: input.nodeFeatures,\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":[]}
|
|
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 ReplicaSessionStamp,\n ReplicationIdentity,\n ReplicationOptions,\n ReplicaGateReason,\n ReplicationScheduler,\n} from \"@tinycloud/sdk-services\";\n\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 stamp?: ReplicaSessionStamp;\n authority: ReplicationAuthority;\n nodeFeatures?: readonly string[];\n primaryKV: Array<{ space: string; kv: { setReadThrough(readThrough: KVReadThrough | null): void } }>;\n}\nexport type GatedReplicationRuntimeBinding = Pick<ReplicationRuntimeBinding, \"context\" | \"session\" | \"primaryKV\"> & {\n gate: ReplicaGateReason;\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, { stamp?: ReplicaSessionStamp; store: PendingWriteStore }>();\n private readonly attached = new Map<ReadThroughKV, string>();\n private current?: KVReplicationController;\n private gated?: KVReadThrough;\n private gatedReason?: ReplicaGateReason;\n private lastController?: KVReplicationController;\n private unsubscribeConsentExpired?: () => void;\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 if (this.gatedReason) return this.options.prefixes.map((prefix) => ({\n prefix, state: \"gated\" as const, gate: this.gatedReason, errorCode: \"REPLICA_GATED\",\n pending: { inFlight: 0, committed: 0, ambiguous: 0 }, pinned: [], lagMs: null,\n }));\n return this.current ? this.current.status() : [];\n },\n sync: async (input) => {\n await this.binding;\n if (this.gatedReason) throw Object.assign(new Error(\"Replica restore is gated.\"), {\n code: \"REPLICA_GATED\", gate: this.gatedReason,\n });\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 /** Observe a refused restore without opening replica state; loader failure leaves raw network KV usable. */\n bindGatedNetworkOnly(\n input: GatedReplicationRuntimeBinding,\n loadGatedNetwork: () => Promise<typeof import(\"./gated-network\")>,\n ): Promise<void> {\n if (this.closed || !this.options.enabled) return Promise.resolve();\n const retired = this.unbind();\n const generation = this.generation;\n this.purgedGeneration = undefined;\n this.pendingPurge = undefined;\n this.pendingPurgeResult = undefined;\n this.pending.clear();\n this.lastController = undefined;\n this.lastPending = undefined;\n this.lastIdentity = undefined;\n this.gatedReason = input.gate;\n this.primarySpace = input.session.spaceId;\n this.binding = retired.then(async () => {\n let create: typeof import(\"./gated-network\").createGatedNetworkReadThrough;\n try {\n ({ createGatedNetworkReadThrough: create } = await loadGatedNetwork());\n } catch {\n // Refusal already gates local delivery; a missing observer cannot end a network session.\n return;\n }\n if (generation !== this.generation || this.closed || typeof create !== \"function\") return;\n const networkOnly = create({\n space: input.session.spaceId,\n prefixes: this.options.prefixes,\n now: () => this.clock.now(),\n emit: (event) => {\n if (generation !== this.generation || this.closed || this.gated !== networkOnly) return;\n try { this.options.onEvent?.(event); } catch { /* Telemetry cannot change network results. */ }\n const { type, ...data } = event;\n try { input.context.emit(type, data); } catch { /* Telemetry cannot change network results. */ }\n },\n });\n this.gated = networkOnly;\n for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);\n });\n return this.binding;\n }\n\n bind(input: ReplicationRuntimeBinding): Promise<void> {\n if (this.closed || !this.options.enabled) return Promise.resolve();\n this.gated = undefined;\n this.gatedReason = undefined;\n this.bindingActive = true;\n const generation = ++this.generation;\n this.purgedGeneration = undefined;\n this.unsubscribeConsentExpired?.();\n this.unsubscribeConsentExpired = 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 cached = this.pending.get(key);\n const stamp = input.stamp;\n if (!cached || cached.stamp?.incarnation !== stamp?.incarnation || cached.stamp?.lifecycleEpoch !== stamp?.lifecycleEpoch) {\n const store = this.options.storage.pendingWrites?.(identity, { stamp });\n if (!store && this.options.storage.lifecycle) {\n throw Object.assign(new Error(\"Gated replication requires a stamped pending store\"), { code: \"REPLICA_GATED\", gate: \"gate_unavailable\" });\n }\n cached = { stamp, store: store ?? services.createMemoryPendingStore(identity) };\n this.pending.set(key, cached);\n }\n const pending = cached.store;\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 stamp: input.stamp,\n authority: input.authority,\n pending,\n scheduler: this.clock,\n emit,\n fetch: context.fetch as FetchFunction,\n nodeFeatures: input.nodeFeatures,\n previous: previousClose,\n });\n this.current = controller;\n this.unsubscribeConsentExpired = this.options.storage.lifecycle?.subscribeConsentExpired?.(\n (stamp) => controller.observeConsentExpired(stamp),\n );\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 ?? this.gated ?? null : null);\n const generation = this.generation;\n return this.binding.then(() => {\n if (generation === this.generation && !this.closed && this.attached.get(kv) === space) {\n kv.setReadThrough(space === this.primarySpace ? this.current ?? this.gated ?? null : null);\n }\n });\n }\n\n unbind(): Promise<void> {\n this.generation++;\n this.bindingActive = false;\n this.pendingPurgeSet = false;\n this.unsubscribeConsentExpired?.();\n this.unsubscribeConsentExpired = undefined;\n const controller = this.current;\n this.current = undefined;\n this.gated = undefined;\n this.gatedReason = 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":["stamp"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import '../gated-network-BEF2XSTC.cjs';
|
|
1
2
|
import '@tinycloud/sdk-core';
|
|
2
3
|
import '@tinycloud/sdk-services';
|
|
3
|
-
export { R as ReplicationRuntime, a as ReplicationRuntimeBinding, b as ReplicationRuntimeOptions } from '../runtime-
|
|
4
|
+
export { G as GatedReplicationRuntimeBinding, R as ReplicationRuntime, a as ReplicationRuntimeBinding, b as ReplicationRuntimeOptions } from '../runtime-D1o9Eybt.cjs';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import '../gated-network-BEF2XSTC.js';
|
|
1
2
|
import '@tinycloud/sdk-core';
|
|
2
3
|
import '@tinycloud/sdk-services';
|
|
3
|
-
export { R as ReplicationRuntime, a as ReplicationRuntimeBinding, b as ReplicationRuntimeOptions } from '../runtime-
|
|
4
|
+
export { G as GatedReplicationRuntimeBinding, R as ReplicationRuntime, a as ReplicationRuntimeBinding, b as ReplicationRuntimeOptions } from '../runtime-v62FzQHz.js';
|
|
@@ -33,10 +33,23 @@ var ReplicationRuntime = class {
|
|
|
33
33
|
this.control = {
|
|
34
34
|
status: async () => {
|
|
35
35
|
await this.binding;
|
|
36
|
+
if (this.gatedReason) return this.options.prefixes.map((prefix) => ({
|
|
37
|
+
prefix,
|
|
38
|
+
state: "gated",
|
|
39
|
+
gate: this.gatedReason,
|
|
40
|
+
errorCode: "REPLICA_GATED",
|
|
41
|
+
pending: { inFlight: 0, committed: 0, ambiguous: 0 },
|
|
42
|
+
pinned: [],
|
|
43
|
+
lagMs: null
|
|
44
|
+
}));
|
|
36
45
|
return this.current ? this.current.status() : [];
|
|
37
46
|
},
|
|
38
47
|
sync: async (input) => {
|
|
39
48
|
await this.binding;
|
|
49
|
+
if (this.gatedReason) throw Object.assign(new Error("Replica restore is gated."), {
|
|
50
|
+
code: "REPLICA_GATED",
|
|
51
|
+
gate: this.gatedReason
|
|
52
|
+
});
|
|
40
53
|
if (this.purgedGeneration === this.generation) return;
|
|
41
54
|
if (this.current) await this.current.sync(input);
|
|
42
55
|
},
|
|
@@ -84,11 +97,59 @@ var ReplicationRuntime = class {
|
|
|
84
97
|
for (const kv of this.attached.keys()) kv.setReadThrough(null);
|
|
85
98
|
this.attached.clear();
|
|
86
99
|
}
|
|
100
|
+
/** Observe a refused restore without opening replica state; loader failure leaves raw network KV usable. */
|
|
101
|
+
bindGatedNetworkOnly(input, loadGatedNetwork) {
|
|
102
|
+
if (this.closed || !this.options.enabled) return Promise.resolve();
|
|
103
|
+
const retired = this.unbind();
|
|
104
|
+
const generation = this.generation;
|
|
105
|
+
this.purgedGeneration = void 0;
|
|
106
|
+
this.pendingPurge = void 0;
|
|
107
|
+
this.pendingPurgeResult = void 0;
|
|
108
|
+
this.pending.clear();
|
|
109
|
+
this.lastController = void 0;
|
|
110
|
+
this.lastPending = void 0;
|
|
111
|
+
this.lastIdentity = void 0;
|
|
112
|
+
this.gatedReason = input.gate;
|
|
113
|
+
this.primarySpace = input.session.spaceId;
|
|
114
|
+
this.binding = retired.then(async () => {
|
|
115
|
+
let create;
|
|
116
|
+
try {
|
|
117
|
+
({ createGatedNetworkReadThrough: create } = await loadGatedNetwork());
|
|
118
|
+
} catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (generation !== this.generation || this.closed || typeof create !== "function") return;
|
|
122
|
+
const networkOnly = create({
|
|
123
|
+
space: input.session.spaceId,
|
|
124
|
+
prefixes: this.options.prefixes,
|
|
125
|
+
now: () => this.clock.now(),
|
|
126
|
+
emit: (event) => {
|
|
127
|
+
if (generation !== this.generation || this.closed || this.gated !== networkOnly) return;
|
|
128
|
+
try {
|
|
129
|
+
this.options.onEvent?.(event);
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
const { type, ...data } = event;
|
|
133
|
+
try {
|
|
134
|
+
input.context.emit(type, data);
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
this.gated = networkOnly;
|
|
140
|
+
for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);
|
|
141
|
+
});
|
|
142
|
+
return this.binding;
|
|
143
|
+
}
|
|
87
144
|
bind(input) {
|
|
88
145
|
if (this.closed || !this.options.enabled) return Promise.resolve();
|
|
146
|
+
this.gated = void 0;
|
|
147
|
+
this.gatedReason = void 0;
|
|
89
148
|
this.bindingActive = true;
|
|
90
149
|
const generation = ++this.generation;
|
|
91
150
|
this.purgedGeneration = void 0;
|
|
151
|
+
this.unsubscribeConsentExpired?.();
|
|
152
|
+
this.unsubscribeConsentExpired = void 0;
|
|
92
153
|
const { context, session } = input;
|
|
93
154
|
const emit = (event) => {
|
|
94
155
|
try {
|
|
@@ -121,11 +182,17 @@ var ReplicationRuntime = class {
|
|
|
121
182
|
return;
|
|
122
183
|
}
|
|
123
184
|
const key = services.replicationIdentityKey(identity);
|
|
124
|
-
let
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.
|
|
185
|
+
let cached = this.pending.get(key);
|
|
186
|
+
const stamp = input.stamp;
|
|
187
|
+
if (!cached || cached.stamp?.incarnation !== stamp?.incarnation || cached.stamp?.lifecycleEpoch !== stamp?.lifecycleEpoch) {
|
|
188
|
+
const store = this.options.storage.pendingWrites?.(identity, { stamp });
|
|
189
|
+
if (!store && this.options.storage.lifecycle) {
|
|
190
|
+
throw Object.assign(new Error("Gated replication requires a stamped pending store"), { code: "REPLICA_GATED", gate: "gate_unavailable" });
|
|
191
|
+
}
|
|
192
|
+
cached = { stamp, store: store ?? services.createMemoryPendingStore(identity) };
|
|
193
|
+
this.pending.set(key, cached);
|
|
128
194
|
}
|
|
195
|
+
const pending = cached.store;
|
|
129
196
|
this.lastPending = pending;
|
|
130
197
|
const controller = services.createKVReplication({
|
|
131
198
|
options: resolvedOptions(this.options),
|
|
@@ -133,6 +200,7 @@ var ReplicationRuntime = class {
|
|
|
133
200
|
storage: this.options.storage,
|
|
134
201
|
identity,
|
|
135
202
|
session: { id: session.delegationCid, did: session.verificationMethod, space: session.spaceId },
|
|
203
|
+
stamp: input.stamp,
|
|
136
204
|
authority: input.authority,
|
|
137
205
|
pending,
|
|
138
206
|
scheduler: this.clock,
|
|
@@ -142,6 +210,9 @@ var ReplicationRuntime = class {
|
|
|
142
210
|
previous: previousClose
|
|
143
211
|
});
|
|
144
212
|
this.current = controller;
|
|
213
|
+
this.unsubscribeConsentExpired = this.options.storage.lifecycle?.subscribeConsentExpired?.(
|
|
214
|
+
(stamp2) => controller.observeConsentExpired(stamp2)
|
|
215
|
+
);
|
|
145
216
|
this.lastController = controller;
|
|
146
217
|
this.lastIdentity = identity;
|
|
147
218
|
for (const [kv, space] of this.attached) {
|
|
@@ -162,17 +233,24 @@ var ReplicationRuntime = class {
|
|
|
162
233
|
}
|
|
163
234
|
attach(space, kv) {
|
|
164
235
|
this.attached.set(kv, space);
|
|
165
|
-
kv.setReadThrough(space === this.primarySpace ? this.current ?? null : null);
|
|
236
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? this.gated ?? null : null);
|
|
237
|
+
const generation = this.generation;
|
|
166
238
|
return this.binding.then(() => {
|
|
167
|
-
|
|
239
|
+
if (generation === this.generation && !this.closed && this.attached.get(kv) === space) {
|
|
240
|
+
kv.setReadThrough(space === this.primarySpace ? this.current ?? this.gated ?? null : null);
|
|
241
|
+
}
|
|
168
242
|
});
|
|
169
243
|
}
|
|
170
244
|
unbind() {
|
|
171
245
|
this.generation++;
|
|
172
246
|
this.bindingActive = false;
|
|
173
247
|
this.pendingPurgeSet = false;
|
|
248
|
+
this.unsubscribeConsentExpired?.();
|
|
249
|
+
this.unsubscribeConsentExpired = void 0;
|
|
174
250
|
const controller = this.current;
|
|
175
251
|
this.current = void 0;
|
|
252
|
+
this.gated = void 0;
|
|
253
|
+
this.gatedReason = void 0;
|
|
176
254
|
this.primarySpace = void 0;
|
|
177
255
|
this.detachAll();
|
|
178
256
|
const binding = this.binding;
|
|
@@ -1 +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 nodeFeatures?: readonly string[];\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 nodeFeatures: input.nodeFeatures,\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":[]}
|
|
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 ReplicaSessionStamp,\n ReplicationIdentity,\n ReplicationOptions,\n ReplicaGateReason,\n ReplicationScheduler,\n} from \"@tinycloud/sdk-services\";\n\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 stamp?: ReplicaSessionStamp;\n authority: ReplicationAuthority;\n nodeFeatures?: readonly string[];\n primaryKV: Array<{ space: string; kv: { setReadThrough(readThrough: KVReadThrough | null): void } }>;\n}\nexport type GatedReplicationRuntimeBinding = Pick<ReplicationRuntimeBinding, \"context\" | \"session\" | \"primaryKV\"> & {\n gate: ReplicaGateReason;\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, { stamp?: ReplicaSessionStamp; store: PendingWriteStore }>();\n private readonly attached = new Map<ReadThroughKV, string>();\n private current?: KVReplicationController;\n private gated?: KVReadThrough;\n private gatedReason?: ReplicaGateReason;\n private lastController?: KVReplicationController;\n private unsubscribeConsentExpired?: () => void;\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 if (this.gatedReason) return this.options.prefixes.map((prefix) => ({\n prefix, state: \"gated\" as const, gate: this.gatedReason, errorCode: \"REPLICA_GATED\",\n pending: { inFlight: 0, committed: 0, ambiguous: 0 }, pinned: [], lagMs: null,\n }));\n return this.current ? this.current.status() : [];\n },\n sync: async (input) => {\n await this.binding;\n if (this.gatedReason) throw Object.assign(new Error(\"Replica restore is gated.\"), {\n code: \"REPLICA_GATED\", gate: this.gatedReason,\n });\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 /** Observe a refused restore without opening replica state; loader failure leaves raw network KV usable. */\n bindGatedNetworkOnly(\n input: GatedReplicationRuntimeBinding,\n loadGatedNetwork: () => Promise<typeof import(\"./gated-network\")>,\n ): Promise<void> {\n if (this.closed || !this.options.enabled) return Promise.resolve();\n const retired = this.unbind();\n const generation = this.generation;\n this.purgedGeneration = undefined;\n this.pendingPurge = undefined;\n this.pendingPurgeResult = undefined;\n this.pending.clear();\n this.lastController = undefined;\n this.lastPending = undefined;\n this.lastIdentity = undefined;\n this.gatedReason = input.gate;\n this.primarySpace = input.session.spaceId;\n this.binding = retired.then(async () => {\n let create: typeof import(\"./gated-network\").createGatedNetworkReadThrough;\n try {\n ({ createGatedNetworkReadThrough: create } = await loadGatedNetwork());\n } catch {\n // Refusal already gates local delivery; a missing observer cannot end a network session.\n return;\n }\n if (generation !== this.generation || this.closed || typeof create !== \"function\") return;\n const networkOnly = create({\n space: input.session.spaceId,\n prefixes: this.options.prefixes,\n now: () => this.clock.now(),\n emit: (event) => {\n if (generation !== this.generation || this.closed || this.gated !== networkOnly) return;\n try { this.options.onEvent?.(event); } catch { /* Telemetry cannot change network results. */ }\n const { type, ...data } = event;\n try { input.context.emit(type, data); } catch { /* Telemetry cannot change network results. */ }\n },\n });\n this.gated = networkOnly;\n for (const scoped of input.primaryKV) this.attach(scoped.space, scoped.kv);\n });\n return this.binding;\n }\n\n bind(input: ReplicationRuntimeBinding): Promise<void> {\n if (this.closed || !this.options.enabled) return Promise.resolve();\n this.gated = undefined;\n this.gatedReason = undefined;\n this.bindingActive = true;\n const generation = ++this.generation;\n this.purgedGeneration = undefined;\n this.unsubscribeConsentExpired?.();\n this.unsubscribeConsentExpired = 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 cached = this.pending.get(key);\n const stamp = input.stamp;\n if (!cached || cached.stamp?.incarnation !== stamp?.incarnation || cached.stamp?.lifecycleEpoch !== stamp?.lifecycleEpoch) {\n const store = this.options.storage.pendingWrites?.(identity, { stamp });\n if (!store && this.options.storage.lifecycle) {\n throw Object.assign(new Error(\"Gated replication requires a stamped pending store\"), { code: \"REPLICA_GATED\", gate: \"gate_unavailable\" });\n }\n cached = { stamp, store: store ?? services.createMemoryPendingStore(identity) };\n this.pending.set(key, cached);\n }\n const pending = cached.store;\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 stamp: input.stamp,\n authority: input.authority,\n pending,\n scheduler: this.clock,\n emit,\n fetch: context.fetch as FetchFunction,\n nodeFeatures: input.nodeFeatures,\n previous: previousClose,\n });\n this.current = controller;\n this.unsubscribeConsentExpired = this.options.storage.lifecycle?.subscribeConsentExpired?.(\n (stamp) => controller.observeConsentExpired(stamp),\n );\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 ?? this.gated ?? null : null);\n const generation = this.generation;\n return this.binding.then(() => {\n if (generation === this.generation && !this.closed && this.attached.get(kv) === space) {\n kv.setReadThrough(space === this.primarySpace ? this.current ?? this.gated ?? null : null);\n }\n });\n }\n\n unbind(): Promise<void> {\n this.generation++;\n this.bindingActive = false;\n this.pendingPurgeSet = false;\n this.unsubscribeConsentExpired?.();\n this.unsubscribeConsentExpired = undefined;\n const controller = this.current;\n this.current = undefined;\n this.gated = undefined;\n this.gatedReason = 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":["stamp"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { _ as __gated_network } from './gated-network-BEF2XSTC.cjs';
|
|
1
2
|
import { ServiceContext, ServiceSession } from '@tinycloud/sdk-core';
|
|
2
|
-
import { ReplicationControl, ReplicationOptions, KVReplicaStorage, ReplicationScheduler,
|
|
3
|
+
import { ReplicaSessionStamp, ReplicationAuthority, KVReadThrough, ReplicaGateReason, ReplicationControl, ReplicationOptions, KVReplicaStorage, ReplicationScheduler, ReplicationIdentity } from '@tinycloud/sdk-services';
|
|
3
4
|
|
|
4
5
|
interface ReplicationRuntimeOptions extends ReplicationOptions {
|
|
5
6
|
storage: KVReplicaStorage;
|
|
@@ -11,6 +12,7 @@ interface ReplicationRuntimeBinding {
|
|
|
11
12
|
address?: string;
|
|
12
13
|
chainId?: number;
|
|
13
14
|
principal?: string;
|
|
15
|
+
stamp?: ReplicaSessionStamp;
|
|
14
16
|
authority: ReplicationAuthority;
|
|
15
17
|
nodeFeatures?: readonly string[];
|
|
16
18
|
primaryKV: Array<{
|
|
@@ -20,6 +22,9 @@ interface ReplicationRuntimeBinding {
|
|
|
20
22
|
};
|
|
21
23
|
}>;
|
|
22
24
|
}
|
|
25
|
+
type GatedReplicationRuntimeBinding = Pick<ReplicationRuntimeBinding, "context" | "session" | "primaryKV"> & {
|
|
26
|
+
gate: ReplicaGateReason;
|
|
27
|
+
};
|
|
23
28
|
type ReadThroughKV = {
|
|
24
29
|
setReadThrough(readThrough: KVReadThrough | null): void;
|
|
25
30
|
setReadThroughReady?(ready: Promise<unknown>): void;
|
|
@@ -31,7 +36,10 @@ declare class ReplicationRuntime {
|
|
|
31
36
|
private readonly pending;
|
|
32
37
|
private readonly attached;
|
|
33
38
|
private current?;
|
|
39
|
+
private gated?;
|
|
40
|
+
private gatedReason?;
|
|
34
41
|
private lastController?;
|
|
42
|
+
private unsubscribeConsentExpired?;
|
|
35
43
|
private previous;
|
|
36
44
|
private binding;
|
|
37
45
|
private services?;
|
|
@@ -48,18 +56,21 @@ declare class ReplicationRuntime {
|
|
|
48
56
|
constructor(options: ReplicationRuntimeOptions, clock?: ReplicationScheduler);
|
|
49
57
|
private loadServices;
|
|
50
58
|
private detachAll;
|
|
59
|
+
/** Observe a refused restore without opening replica state; loader failure leaves raw network KV usable. */
|
|
60
|
+
bindGatedNetworkOnly(input: GatedReplicationRuntimeBinding, loadGatedNetwork: () => Promise<typeof __gated_network>): Promise<void>;
|
|
51
61
|
bind(input: ReplicationRuntimeBinding): Promise<void>;
|
|
52
62
|
attach(space: string, kv: ReadThroughKV): Promise<void>;
|
|
53
63
|
unbind(): Promise<void>;
|
|
54
64
|
get boundIdentity(): ReplicationIdentity | undefined;
|
|
55
65
|
}
|
|
56
66
|
|
|
67
|
+
type __runtime_GatedReplicationRuntimeBinding = GatedReplicationRuntimeBinding;
|
|
57
68
|
type __runtime_ReplicationRuntime = ReplicationRuntime;
|
|
58
69
|
declare const __runtime_ReplicationRuntime: typeof ReplicationRuntime;
|
|
59
70
|
type __runtime_ReplicationRuntimeBinding = ReplicationRuntimeBinding;
|
|
60
71
|
type __runtime_ReplicationRuntimeOptions = ReplicationRuntimeOptions;
|
|
61
72
|
declare namespace __runtime {
|
|
62
|
-
export { __runtime_ReplicationRuntime as ReplicationRuntime, type __runtime_ReplicationRuntimeBinding as ReplicationRuntimeBinding, type __runtime_ReplicationRuntimeOptions as ReplicationRuntimeOptions };
|
|
73
|
+
export { type __runtime_GatedReplicationRuntimeBinding as GatedReplicationRuntimeBinding, __runtime_ReplicationRuntime as ReplicationRuntime, type __runtime_ReplicationRuntimeBinding as ReplicationRuntimeBinding, type __runtime_ReplicationRuntimeOptions as ReplicationRuntimeOptions };
|
|
63
74
|
}
|
|
64
75
|
|
|
65
|
-
export { ReplicationRuntime as R, __runtime as _, type ReplicationRuntimeBinding as a, type ReplicationRuntimeOptions as b };
|
|
76
|
+
export { type GatedReplicationRuntimeBinding as G, ReplicationRuntime as R, __runtime as _, type ReplicationRuntimeBinding as a, type ReplicationRuntimeOptions as b };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { _ as __gated_network } from './gated-network-BEF2XSTC.js';
|
|
1
2
|
import { ServiceContext, ServiceSession } from '@tinycloud/sdk-core';
|
|
2
|
-
import { ReplicationControl, ReplicationOptions, KVReplicaStorage, ReplicationScheduler,
|
|
3
|
+
import { ReplicaSessionStamp, ReplicationAuthority, KVReadThrough, ReplicaGateReason, ReplicationControl, ReplicationOptions, KVReplicaStorage, ReplicationScheduler, ReplicationIdentity } from '@tinycloud/sdk-services';
|
|
3
4
|
|
|
4
5
|
interface ReplicationRuntimeOptions extends ReplicationOptions {
|
|
5
6
|
storage: KVReplicaStorage;
|
|
@@ -11,6 +12,7 @@ interface ReplicationRuntimeBinding {
|
|
|
11
12
|
address?: string;
|
|
12
13
|
chainId?: number;
|
|
13
14
|
principal?: string;
|
|
15
|
+
stamp?: ReplicaSessionStamp;
|
|
14
16
|
authority: ReplicationAuthority;
|
|
15
17
|
nodeFeatures?: readonly string[];
|
|
16
18
|
primaryKV: Array<{
|
|
@@ -20,6 +22,9 @@ interface ReplicationRuntimeBinding {
|
|
|
20
22
|
};
|
|
21
23
|
}>;
|
|
22
24
|
}
|
|
25
|
+
type GatedReplicationRuntimeBinding = Pick<ReplicationRuntimeBinding, "context" | "session" | "primaryKV"> & {
|
|
26
|
+
gate: ReplicaGateReason;
|
|
27
|
+
};
|
|
23
28
|
type ReadThroughKV = {
|
|
24
29
|
setReadThrough(readThrough: KVReadThrough | null): void;
|
|
25
30
|
setReadThroughReady?(ready: Promise<unknown>): void;
|
|
@@ -31,7 +36,10 @@ declare class ReplicationRuntime {
|
|
|
31
36
|
private readonly pending;
|
|
32
37
|
private readonly attached;
|
|
33
38
|
private current?;
|
|
39
|
+
private gated?;
|
|
40
|
+
private gatedReason?;
|
|
34
41
|
private lastController?;
|
|
42
|
+
private unsubscribeConsentExpired?;
|
|
35
43
|
private previous;
|
|
36
44
|
private binding;
|
|
37
45
|
private services?;
|
|
@@ -48,18 +56,21 @@ declare class ReplicationRuntime {
|
|
|
48
56
|
constructor(options: ReplicationRuntimeOptions, clock?: ReplicationScheduler);
|
|
49
57
|
private loadServices;
|
|
50
58
|
private detachAll;
|
|
59
|
+
/** Observe a refused restore without opening replica state; loader failure leaves raw network KV usable. */
|
|
60
|
+
bindGatedNetworkOnly(input: GatedReplicationRuntimeBinding, loadGatedNetwork: () => Promise<typeof __gated_network>): Promise<void>;
|
|
51
61
|
bind(input: ReplicationRuntimeBinding): Promise<void>;
|
|
52
62
|
attach(space: string, kv: ReadThroughKV): Promise<void>;
|
|
53
63
|
unbind(): Promise<void>;
|
|
54
64
|
get boundIdentity(): ReplicationIdentity | undefined;
|
|
55
65
|
}
|
|
56
66
|
|
|
67
|
+
type __runtime_GatedReplicationRuntimeBinding = GatedReplicationRuntimeBinding;
|
|
57
68
|
type __runtime_ReplicationRuntime = ReplicationRuntime;
|
|
58
69
|
declare const __runtime_ReplicationRuntime: typeof ReplicationRuntime;
|
|
59
70
|
type __runtime_ReplicationRuntimeBinding = ReplicationRuntimeBinding;
|
|
60
71
|
type __runtime_ReplicationRuntimeOptions = ReplicationRuntimeOptions;
|
|
61
72
|
declare namespace __runtime {
|
|
62
|
-
export { __runtime_ReplicationRuntime as ReplicationRuntime, type __runtime_ReplicationRuntimeBinding as ReplicationRuntimeBinding, type __runtime_ReplicationRuntimeOptions as ReplicationRuntimeOptions };
|
|
73
|
+
export { type __runtime_GatedReplicationRuntimeBinding as GatedReplicationRuntimeBinding, __runtime_ReplicationRuntime as ReplicationRuntime, type __runtime_ReplicationRuntimeBinding as ReplicationRuntimeBinding, type __runtime_ReplicationRuntimeOptions as ReplicationRuntimeOptions };
|
|
63
74
|
}
|
|
64
75
|
|
|
65
|
-
export { ReplicationRuntime as R, __runtime as _, type ReplicationRuntimeBinding as a, type ReplicationRuntimeOptions as b };
|
|
76
|
+
export { type GatedReplicationRuntimeBinding as G, 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.
|
|
3
|
+
"version": "3.3.0-beta.7",
|
|
4
4
|
"description": "TinyCloud SDK for Node.js with configurable authorization strategies",
|
|
5
5
|
"author": "TinyCloud, Inc.",
|
|
6
6
|
"license": "EGPL",
|
|
@@ -38,6 +38,16 @@
|
|
|
38
38
|
"types": "./dist/replication/authority.d.cts",
|
|
39
39
|
"default": "./dist/replication/authority.cjs"
|
|
40
40
|
}
|
|
41
|
+
},
|
|
42
|
+
"./replication/gated-network": {
|
|
43
|
+
"import": {
|
|
44
|
+
"types": "./dist/replication/gated-network.d.ts",
|
|
45
|
+
"default": "./dist/replication/gated-network.js"
|
|
46
|
+
},
|
|
47
|
+
"require": {
|
|
48
|
+
"types": "./dist/replication/gated-network.d.cts",
|
|
49
|
+
"default": "./dist/replication/gated-network.cjs"
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
52
|
},
|
|
43
53
|
"engines": {
|
|
@@ -54,9 +64,9 @@
|
|
|
54
64
|
"test:integration": "echo 'Integration tests live in tests/node-sdk workspace'"
|
|
55
65
|
},
|
|
56
66
|
"dependencies": {
|
|
57
|
-
"@tinycloud/replica": "0.
|
|
58
|
-
"@tinycloud/sdk-services": "3.
|
|
59
|
-
"@tinycloud/sdk-core": "3.
|
|
67
|
+
"@tinycloud/replica": "0.3.0-beta.4",
|
|
68
|
+
"@tinycloud/sdk-services": "3.3.0-beta.7",
|
|
69
|
+
"@tinycloud/sdk-core": "3.3.0-beta.7",
|
|
60
70
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
61
71
|
"siwe": "^3.0.0"
|
|
62
72
|
},
|