@volter/world-runtime 3.0.26 → 3.0.28
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.
|
@@ -84,6 +84,10 @@ export class LocalBranches {
|
|
|
84
84
|
create: (at, ttl, _origin, parentKey, byKey) => this.create(parent, at, ttl, parentKey, byKey),
|
|
85
85
|
remove: async (name) => { const w = this.o.worlds.get(name); if (!w || this.record(w)?.from !== parent)
|
|
86
86
|
return false; await this.remove(name); return true; },
|
|
87
|
+
board: async (name, frame) => {
|
|
88
|
+
const w = this.o.worlds.get(name);
|
|
89
|
+
return w && this.record(w)?.from === parent ? w.branchBoard(frame) : null;
|
|
90
|
+
},
|
|
87
91
|
// a branch of this parent only, served in this process beside it: its own content plane's pass
|
|
88
92
|
pagesPass: async (name, scope, land, grant) => {
|
|
89
93
|
const w = this.o.worlds.get(name);
|
|
@@ -63,6 +63,7 @@ export type MountedWorld = {
|
|
|
63
63
|
framer?: string;
|
|
64
64
|
asker?: string;
|
|
65
65
|
}) => string | null;
|
|
66
|
+
branchBoard: (frame?: string) => Promise<Response>;
|
|
66
67
|
boot: (url: string) => Promise<void>;
|
|
67
68
|
stop: () => Promise<void>;
|
|
68
69
|
};
|
|
@@ -208,6 +209,8 @@ export type BranchDoors = {
|
|
|
208
209
|
framer?: string;
|
|
209
210
|
asker?: string;
|
|
210
211
|
}) => Promise<string | null>;
|
|
212
|
+
/** the branch's own board, counting from its fork; a frame names its changes alone */
|
|
213
|
+
board?: (name: string, frame?: string) => Promise<Response | null>;
|
|
211
214
|
};
|
|
212
215
|
/** A World's browser-origin label, `<world>--<org>`, or null when its names cannot make one (a DNS
|
|
213
216
|
* label: lowercase letters, digits and single hyphens, 63 at most). A host maps a label back by the
|
|
@@ -444,6 +447,7 @@ export declare class WorldDoors {
|
|
|
444
447
|
* screen draws yet) and the changes not yet cut on the records it covers, counted by the request that made them, with
|
|
445
448
|
* when the first was made; and the boards as this World's people arranged them. A frame's changes themselves are read
|
|
446
449
|
* one frame at a time (`board/changes`). */
|
|
450
|
+
branchBoard(frame?: string): Promise<Response>;
|
|
447
451
|
private board;
|
|
448
452
|
/** One frame's changes not yet cut, newest first: each the writes one request made, as words read them (the operation,
|
|
449
453
|
* the record, its few naming fields), the frame named `<vendor>:<frame id>`. */
|
package/dist/src/served-world.js
CHANGED
|
@@ -18,6 +18,9 @@ import { getActiveWorldStore, withHistoryLocks, captureHistory, historyAtInstant
|
|
|
18
18
|
// GET map | timeline | diff every twin and what it holds; the logs merged newest first; the changes since the base
|
|
19
19
|
// GET/PUT clock, POST clock/advance the frozen instant every twin stamps from, forward only
|
|
20
20
|
// GET/POST branches, DELETE branches/<org>/<world> the host's branches of this World ("as of" views)
|
|
21
|
+
// GET branches/<org>/<world>/board[/changes] a branch's pages and changes from its fork, through its host
|
|
22
|
+
// GET branches/<org>/<world>/pages?land=<path> a read pass to the branch's own content plane
|
|
23
|
+
// GET/PUT board, GET board/changes the pages and their changes; the boards as people arranged them
|
|
21
24
|
// GET history?at=<instant> each twin's history cut at an instant (where such a branch starts)
|
|
22
25
|
// GET/POST marks where the World's changes count from; POST { note? } marks it now
|
|
23
26
|
// GET /<org>/<world>/<vendor>/<path> the vendor's own screens, the pack's workspaces (its `GET /twin` lists them)
|
|
@@ -216,6 +219,7 @@ export async function mountWorld(name, opts = {}) {
|
|
|
216
219
|
handle: (request) => world.handle(request),
|
|
217
220
|
site: (request, host) => world.site(request, host),
|
|
218
221
|
pagesPass: (scope, land, grant) => world.pagesPass(scope, land, grant),
|
|
222
|
+
branchBoard: (frame) => world.branchBoard(frame),
|
|
219
223
|
async boot(url) {
|
|
220
224
|
keep();
|
|
221
225
|
const port = Number(new URL(url).port) || 0;
|
|
@@ -2121,6 +2125,16 @@ export class WorldDoors {
|
|
|
2121
2125
|
* and removes them; a host that makes none answers 404, and a view offers no "as of". */
|
|
2122
2126
|
async branchesDoor(request, url, rest, via) {
|
|
2123
2127
|
const branches = this.host.branches;
|
|
2128
|
+
if (rest[2] === 'board' && request.method === 'GET' && (rest.length === 3 || (rest.length === 4 && rest[3] === 'changes'))) {
|
|
2129
|
+
if (!branches)
|
|
2130
|
+
return Response.json({ error: `this host cannot read branches of ${this.served}` }, { status: 501 });
|
|
2131
|
+
const name = `${decodeURIComponent(rest[0])}/${decodeURIComponent(rest[1])}`;
|
|
2132
|
+
if (!(await branches.list()).some((b) => b.name === name))
|
|
2133
|
+
return Response.json({ error: `no branch ${name} of ${this.served}` }, { status: 404 });
|
|
2134
|
+
if (!branches.board)
|
|
2135
|
+
return Response.json({ error: `this host cannot read ${name}'s board` }, { status: 501 });
|
|
2136
|
+
return (await branches.board(name, rest[3] === 'changes' ? url.searchParams.get('frame') ?? '' : undefined)) ?? Response.json({ error: `no branch ${name} of ${this.served}` }, { status: 404 });
|
|
2137
|
+
}
|
|
2124
2138
|
if (!branches)
|
|
2125
2139
|
return Response.json({ error: `this host makes no branches of ${this.served}` }, { status: 404 });
|
|
2126
2140
|
if (rest.length === 0 && request.method === 'GET') {
|
|
@@ -2255,18 +2269,26 @@ export class WorldDoors {
|
|
|
2255
2269
|
* screen draws yet) and the changes not yet cut on the records it covers, counted by the request that made them, with
|
|
2256
2270
|
* when the first was made; and the boards as this World's people arranged them. A frame's changes themselves are read
|
|
2257
2271
|
* one frame at a time (`board/changes`). */
|
|
2258
|
-
async
|
|
2259
|
-
const
|
|
2260
|
-
|
|
2272
|
+
async branchBoard(frame) {
|
|
2273
|
+
const url = new URL(this.host.browserOrigin?.() ?? 'http://world.local');
|
|
2274
|
+
if (frame !== undefined) {
|
|
2275
|
+
url.searchParams.set('frame', frame);
|
|
2276
|
+
return this.boardChanges(url, true);
|
|
2277
|
+
}
|
|
2278
|
+
return Response.json(await this.board(url, true));
|
|
2279
|
+
}
|
|
2280
|
+
async board(url, fork = false) {
|
|
2281
|
+
const { frames, delta } = await this.boardFrames(url, fork);
|
|
2282
|
+
// the World's last cut, or the branch's whole ledger when its parent is looking at it
|
|
2261
2283
|
return { world: this.served, base: { id: delta.base.id, at: delta.base.createdAt ?? null }, frames: frames.map(({ frame, mine }) => ({ ...frame, changes: new Set(mine.map(actOf)).size, since: mine.map((c) => c.action.occurredAt).sort()[0] ?? null })), ...this.savedBoards() };
|
|
2262
2284
|
}
|
|
2263
2285
|
/** One frame's changes not yet cut, newest first: each the writes one request made, as words read them (the operation,
|
|
2264
2286
|
* the record, its few naming fields), the frame named `<vendor>:<frame id>`. */
|
|
2265
|
-
async boardChanges(url) {
|
|
2287
|
+
async boardChanges(url, fork = false) {
|
|
2266
2288
|
const key = url.searchParams.get('frame') ?? '';
|
|
2267
2289
|
if (!key)
|
|
2268
2290
|
return Response.json({ error: 'board/changes?frame=<vendor>:<frame id>' }, { status: 400 });
|
|
2269
|
-
const found = (await this.boardFrames(url)).frames.find(({ frame }) => `${frame.vendor}:${frame.id}` === key);
|
|
2291
|
+
const found = (await this.boardFrames(url, fork)).frames.find(({ frame }) => `${frame.vendor}:${frame.id}` === key);
|
|
2270
2292
|
if (!found)
|
|
2271
2293
|
return Response.json({ error: `no frame ${key} on ${this.served}'s board` }, { status: 404 });
|
|
2272
2294
|
const acts = new Map();
|
|
@@ -2284,7 +2306,7 @@ export class WorldDoors {
|
|
|
2284
2306
|
return Response.json({ frame: key, total: all.length, since: found.mine.map((c) => c.action.occurredAt).sort()[0] ?? null, acts: all.slice(0, 100) });
|
|
2285
2307
|
}
|
|
2286
2308
|
/** Every twin's frames, where each opens here, and each with the changes not yet cut that are its own. */
|
|
2287
|
-
async boardFrames(url) {
|
|
2309
|
+
async boardFrames(url, fork = false) {
|
|
2288
2310
|
const claimed = new Map((await this.claimedHosts(url.origin)).map(({ vendor, hosts }) => [vendor, new Set(hosts)]));
|
|
2289
2311
|
const sitesAt = this.host.sitesTemplate?.() ?? null;
|
|
2290
2312
|
// a site private to the World's people (a `{site}` template: a hosted World) opens through this World's own origin,
|
|
@@ -2300,9 +2322,10 @@ export class WorldDoors {
|
|
|
2300
2322
|
const own = this.host.browserOrigin?.() ?? null;
|
|
2301
2323
|
return own ? `${own}/-/${this.served}/sites/go?${new URLSearchParams({ host: hostname, land: rest }).toString()}` : null;
|
|
2302
2324
|
};
|
|
2303
|
-
|
|
2325
|
+
// inherited history is the parent log: the branch's own ledger starts at its fork, even after a cut
|
|
2326
|
+
const delta = diffWorld(this.name, { root: this.worldRoot, ...(fork ? { base: 'world-boot' } : {}) });
|
|
2304
2327
|
const received = new Set(listWorldChangesets({ root: this.worldRoot, world: this.name }).filter((l) => l.changeset.pushedBy).flatMap((l) => l.changeset.actions.map((a) => a.action.id)));
|
|
2305
|
-
const changes = delta.actions.filter((a) => !received.has(a.action.id));
|
|
2328
|
+
const changes = fork ? delta.actions : delta.actions.filter((a) => !received.has(a.action.id));
|
|
2306
2329
|
const frames = [];
|
|
2307
2330
|
for (const vendor of Object.keys(this.up().services)) {
|
|
2308
2331
|
const twin = this.host.twinFetch(vendor);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-runtime",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.28",
|
|
4
4
|
"description": "World configs for twins: boot named local runtimes, allocate ports, generate world.env/instance.json, and run apps against fake-key twin worlds.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -68,14 +68,14 @@
|
|
|
68
68
|
"node": ">=22.3"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@volter/world-core": "3.0.
|
|
72
|
-
"@volter/world-console": "3.0.
|
|
71
|
+
"@volter/world-core": "3.0.28",
|
|
72
|
+
"@volter/world-console": "3.0.23"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@electric-sql/pglite": "0.5.8",
|
|
76
76
|
"@electric-sql/pglite-pgvector": "0.0.9",
|
|
77
77
|
"@volter/world-access": "3.0.1",
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.28",
|
|
79
79
|
"pg-gateway": "0.3.0-beta.4",
|
|
80
80
|
"smol-toml": "^1.8.0",
|
|
81
81
|
"yaml": "2.9.0"
|
package/src/local-branches.ts
CHANGED
|
@@ -93,6 +93,10 @@ export class LocalBranches {
|
|
|
93
93
|
list: async () => this.list(parent),
|
|
94
94
|
create: (at, ttl, _origin, parentKey, byKey) => this.create(parent, at, ttl, parentKey, byKey),
|
|
95
95
|
remove: async (name) => { const w = this.o.worlds.get(name); if (!w || this.record(w)?.from !== parent) return false; await this.remove(name); return true; },
|
|
96
|
+
board: async (name, frame) => {
|
|
97
|
+
const w = this.o.worlds.get(name);
|
|
98
|
+
return w && this.record(w)?.from === parent ? w.branchBoard(frame) : null;
|
|
99
|
+
},
|
|
96
100
|
// a branch of this parent only, served in this process beside it: its own content plane's pass
|
|
97
101
|
pagesPass: async (name, scope, land, grant) => {
|
|
98
102
|
const w = this.o.worlds.get(name);
|
package/src/served-world.ts
CHANGED
|
@@ -18,6 +18,9 @@ import { getActiveWorldStore, withHistoryLocks, captureHistory, historyAtInstant
|
|
|
18
18
|
// GET map | timeline | diff every twin and what it holds; the logs merged newest first; the changes since the base
|
|
19
19
|
// GET/PUT clock, POST clock/advance the frozen instant every twin stamps from, forward only
|
|
20
20
|
// GET/POST branches, DELETE branches/<org>/<world> the host's branches of this World ("as of" views)
|
|
21
|
+
// GET branches/<org>/<world>/board[/changes] a branch's pages and changes from its fork, through its host
|
|
22
|
+
// GET branches/<org>/<world>/pages?land=<path> a read pass to the branch's own content plane
|
|
23
|
+
// GET/PUT board, GET board/changes the pages and their changes; the boards as people arranged them
|
|
21
24
|
// GET history?at=<instant> each twin's history cut at an instant (where such a branch starts)
|
|
22
25
|
// GET/POST marks where the World's changes count from; POST { note? } marks it now
|
|
23
26
|
// GET /<org>/<world>/<vendor>/<path> the vendor's own screens, the pack's workspaces (its `GET /twin` lists them)
|
|
@@ -160,7 +163,7 @@ function configRefFor(worldRoot: string, name: string): string {
|
|
|
160
163
|
/** A world mounted by a host. `token`/`readToken` read the CURRENT tokens (rotate changes them);
|
|
161
164
|
* `twins()` names the world's twins once booted; `rotate()` mints both tokens anew — the old die
|
|
162
165
|
* with the call — and keeps them beside the world like `boot` does. */
|
|
163
|
-
export type MountedWorld = { name: string; served: string; root: string; readonly token: string; readonly readToken: string; twins: () => string[]; rotate: () => { token: string; readToken: string }; handle: (request: Request) => Promise<Response>; /** a hostname this World's twins answer, as a browser reads it (siteOfHost) */ site: (request: Request, host: string) => Promise<Response>; /** WorldDoors.pagesPass */ pagesPass: (scope: 'read' | 'write', land: string, grant?: { who?: string; framer?: string; asker?: string }) => string | null; boot: (url: string) => Promise<void>; stop: () => Promise<void> };
|
|
166
|
+
export type MountedWorld = { name: string; served: string; root: string; readonly token: string; readonly readToken: string; twins: () => string[]; rotate: () => { token: string; readToken: string }; handle: (request: Request) => Promise<Response>; /** a hostname this World's twins answer, as a browser reads it (siteOfHost) */ site: (request: Request, host: string) => Promise<Response>; /** WorldDoors.pagesPass */ pagesPass: (scope: 'read' | 'write', land: string, grant?: { who?: string; framer?: string; asker?: string }) => string | null; branchBoard: (frame?: string) => Promise<Response>; boot: (url: string) => Promise<void>; stop: () => Promise<void> };
|
|
164
167
|
/** A browser session a World keeps: its scope, when it ends, and the person a pass named, when one did. */
|
|
165
168
|
/** What a content-plane pass, and the session it opens, carry: its scope; the control session that asked (the content
|
|
166
169
|
* session ends with it; none when the World's token asked); the person; the console that frames it; and whether it
|
|
@@ -225,6 +228,7 @@ export async function mountWorld(name: string, opts: { /** the platforms whose p
|
|
|
225
228
|
handle: (request) => world.handle(request),
|
|
226
229
|
site: (request, host) => world.site(request, host),
|
|
227
230
|
pagesPass: (scope, land, grant) => world.pagesPass(scope, land, grant),
|
|
231
|
+
branchBoard: (frame) => world.branchBoard(frame),
|
|
228
232
|
async boot(url) {
|
|
229
233
|
keep();
|
|
230
234
|
const port = Number(new URL(url).port) || 0;
|
|
@@ -396,6 +400,8 @@ export type BranchDoors = {
|
|
|
396
400
|
/** the address that opens a branch's content plane with a one-time pass at `scope`, landing on `land` (a path of the
|
|
397
401
|
* branch): how its parent's console frames the branch's pages beside its own (the comparison) */
|
|
398
402
|
pagesPass?: (name: string, scope: 'read' | 'write', land: string, grant?: { who?: string; framer?: string; asker?: string }) => Promise<string | null>;
|
|
403
|
+
/** the branch's own board, counting from its fork; a frame names its changes alone */
|
|
404
|
+
board?: (name: string, frame?: string) => Promise<Response | null>;
|
|
399
405
|
};
|
|
400
406
|
|
|
401
407
|
/** A World's browser-origin label, `<world>--<org>`, or null when its names cannot make one (a DNS
|
|
@@ -1793,6 +1799,13 @@ export class WorldDoors {
|
|
|
1793
1799
|
* and removes them; a host that makes none answers 404, and a view offers no "as of". */
|
|
1794
1800
|
private async branchesDoor(request: Request, url: URL, rest: string[], via: 'token' | 'key' | 'session' | null): Promise<Response> {
|
|
1795
1801
|
const branches = this.host.branches;
|
|
1802
|
+
if (rest[2] === 'board' && request.method === 'GET' && (rest.length === 3 || (rest.length === 4 && rest[3] === 'changes'))) {
|
|
1803
|
+
if (!branches) return Response.json({ error: `this host cannot read branches of ${this.served}` }, { status: 501 });
|
|
1804
|
+
const name = `${decodeURIComponent(rest[0]!)}/${decodeURIComponent(rest[1]!)}`;
|
|
1805
|
+
if (!(await branches.list()).some((b) => b.name === name)) return Response.json({ error: `no branch ${name} of ${this.served}` }, { status: 404 });
|
|
1806
|
+
if (!branches.board) return Response.json({ error: `this host cannot read ${name}'s board` }, { status: 501 });
|
|
1807
|
+
return (await branches.board(name, rest[3] === 'changes' ? url.searchParams.get('frame') ?? '' : undefined)) ?? Response.json({ error: `no branch ${name} of ${this.served}` }, { status: 404 });
|
|
1808
|
+
}
|
|
1796
1809
|
if (!branches) return Response.json({ error: `this host makes no branches of ${this.served}` }, { status: 404 });
|
|
1797
1810
|
if (rest.length === 0 && request.method === 'GET') {
|
|
1798
1811
|
// beside the host's snapshots, the World's own NAMED branches (`volter world branch`): one runs at a time, and
|
|
@@ -1899,17 +1912,22 @@ export class WorldDoors {
|
|
|
1899
1912
|
* screen draws yet) and the changes not yet cut on the records it covers, counted by the request that made them, with
|
|
1900
1913
|
* when the first was made; and the boards as this World's people arranged them. A frame's changes themselves are read
|
|
1901
1914
|
* one frame at a time (`board/changes`). */
|
|
1902
|
-
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1915
|
+
async branchBoard(frame?: string): Promise<Response> {
|
|
1916
|
+
const url = new URL(this.host.browserOrigin?.() ?? 'http://world.local');
|
|
1917
|
+
if (frame !== undefined) { url.searchParams.set('frame', frame); return this.boardChanges(url, true); }
|
|
1918
|
+
return Response.json(await this.board(url, true));
|
|
1919
|
+
}
|
|
1920
|
+
private async board(url: URL, fork = false): Promise<unknown> {
|
|
1921
|
+
const { frames, delta } = await this.boardFrames(url, fork);
|
|
1922
|
+
// the World's last cut, or the branch's whole ledger when its parent is looking at it
|
|
1905
1923
|
return { world: this.served, base: { id: delta.base.id, at: delta.base.createdAt ?? null }, frames: frames.map(({ frame, mine }) => ({ ...frame, changes: new Set(mine.map(actOf)).size, since: mine.map((c) => c.action.occurredAt).sort()[0] ?? null })), ...this.savedBoards() };
|
|
1906
1924
|
}
|
|
1907
1925
|
/** One frame's changes not yet cut, newest first: each the writes one request made, as words read them (the operation,
|
|
1908
1926
|
* the record, its few naming fields), the frame named `<vendor>:<frame id>`. */
|
|
1909
|
-
private async boardChanges(url: URL): Promise<Response> {
|
|
1927
|
+
private async boardChanges(url: URL, fork = false): Promise<Response> {
|
|
1910
1928
|
const key = url.searchParams.get('frame') ?? '';
|
|
1911
1929
|
if (!key) return Response.json({ error: 'board/changes?frame=<vendor>:<frame id>' }, { status: 400 });
|
|
1912
|
-
const found = (await this.boardFrames(url)).frames.find(({ frame }) => `${frame.vendor}:${frame.id}` === key);
|
|
1930
|
+
const found = (await this.boardFrames(url, fork)).frames.find(({ frame }) => `${frame.vendor}:${frame.id}` === key);
|
|
1913
1931
|
if (!found) return Response.json({ error: `no frame ${key} on ${this.served}'s board` }, { status: 404 });
|
|
1914
1932
|
const acts = new Map<string, { at: string; writes: number; entries: Array<{ op: string; operation?: string; subject: { type: string; id: string }; fields: Record<string, string> }> }>();
|
|
1915
1933
|
for (const c of found.mine) {
|
|
@@ -1924,7 +1942,7 @@ export class WorldDoors {
|
|
|
1924
1942
|
return Response.json({ frame: key, total: all.length, since: found.mine.map((c) => c.action.occurredAt).sort()[0] ?? null, acts: all.slice(0, 100) });
|
|
1925
1943
|
}
|
|
1926
1944
|
/** Every twin's frames, where each opens here, and each with the changes not yet cut that are its own. */
|
|
1927
|
-
private async boardFrames(url: URL): Promise<{ delta: ReturnType<typeof diffWorld>; frames: Array<{ frame: Record<string, unknown> & { vendor: string; id: string }; mine: Array<ReturnType<typeof diffWorld>['actions'][number]> }> }> {
|
|
1945
|
+
private async boardFrames(url: URL, fork = false): Promise<{ delta: ReturnType<typeof diffWorld>; frames: Array<{ frame: Record<string, unknown> & { vendor: string; id: string }; mine: Array<ReturnType<typeof diffWorld>['actions'][number]> }> }> {
|
|
1928
1946
|
const claimed = new Map((await this.claimedHosts(url.origin)).map(({ vendor, hosts }) => [vendor, new Set(hosts)]));
|
|
1929
1947
|
const sitesAt = this.host.sitesTemplate?.() ?? null;
|
|
1930
1948
|
// a site private to the World's people (a `{site}` template: a hosted World) opens through this World's own origin,
|
|
@@ -1937,9 +1955,10 @@ export class WorldDoors {
|
|
|
1937
1955
|
const own = this.host.browserOrigin?.() ?? null;
|
|
1938
1956
|
return own ? `${own}/-/${this.served}/sites/go?${new URLSearchParams({ host: hostname, land: rest }).toString()}` : null;
|
|
1939
1957
|
};
|
|
1940
|
-
|
|
1958
|
+
// inherited history is the parent log: the branch's own ledger starts at its fork, even after a cut
|
|
1959
|
+
const delta = diffWorld(this.name, { root: this.worldRoot, ...(fork ? { base: 'world-boot' } : {}) });
|
|
1941
1960
|
const received = new Set(listWorldChangesets({ root: this.worldRoot, world: this.name }).filter((l) => l.changeset.pushedBy).flatMap((l) => l.changeset.actions.map((a) => a.action.id)));
|
|
1942
|
-
const changes = delta.actions.filter((a) => !received.has(a.action.id));
|
|
1961
|
+
const changes = fork ? delta.actions : delta.actions.filter((a) => !received.has(a.action.id));
|
|
1943
1962
|
const frames: Array<{ frame: Record<string, unknown> & { vendor: string; id: string }; mine: typeof changes }> = [];
|
|
1944
1963
|
for (const vendor of Object.keys(this.up().services)) {
|
|
1945
1964
|
const twin = this.host.twinFetch(vendor);
|