@fougere/core 0.11.0-alpha.0 → 0.12.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EffectiveOperationModel.d.ts.map +1 -1
- package/dist/EffectiveOperationModel.js +18 -0
- package/dist/EffectiveOperationModel.js.map +1 -1
- package/dist/FougereConfig.d.ts +10 -6
- package/dist/FougereConfig.d.ts.map +1 -1
- package/dist/FougereConfig.js +19 -4
- package/dist/FougereConfig.js.map +1 -1
- package/dist/FrondConfig.d.ts +0 -6
- package/dist/FrondConfig.d.ts.map +1 -1
- package/dist/FrondConfig.js.map +1 -1
- package/dist/FrondDeclaration.d.ts +2 -9
- package/dist/FrondDeclaration.d.ts.map +1 -1
- package/dist/FrondDeclaration.js +0 -1
- package/dist/FrondDeclaration.js.map +1 -1
- package/dist/FrondsStated.d.ts +54 -0
- package/dist/FrondsStated.d.ts.map +1 -0
- package/dist/FrondsStated.js +26 -0
- package/dist/FrondsStated.js.map +1 -0
- package/dist/StatedFrond.d.ts +21 -0
- package/dist/StatedFrond.d.ts.map +1 -0
- package/dist/StatedFrond.js +34 -0
- package/dist/StatedFrond.js.map +1 -0
- package/dist/StatedModules.d.ts +19 -0
- package/dist/StatedModules.d.ts.map +1 -0
- package/dist/StatedModules.js +37 -0
- package/dist/StatedModules.js.map +1 -0
- package/dist/boot/CreateAppOptions.d.ts +19 -2
- package/dist/boot/CreateAppOptions.d.ts.map +1 -1
- package/dist/boot/Emissions.d.ts +9 -0
- package/dist/boot/Emissions.d.ts.map +1 -1
- package/dist/boot/Emissions.js +11 -5
- package/dist/boot/Emissions.js.map +1 -1
- package/dist/boot/bootstrap.d.ts.map +1 -1
- package/dist/boot/bootstrap.js +342 -307
- package/dist/boot/bootstrap.js.map +1 -1
- package/dist/boot/declared.d.ts.map +1 -1
- package/dist/boot/declared.js +3 -0
- package/dist/boot/declared.js.map +1 -1
- package/dist/boot/frame.d.ts +0 -4
- package/dist/boot/frame.d.ts.map +1 -1
- package/dist/boot/frame.js +31 -14
- package/dist/boot/frame.js.map +1 -1
- package/dist/boot/hosted.d.ts.map +1 -1
- package/dist/boot/hosted.js +4 -3
- package/dist/boot/hosted.js.map +1 -1
- package/dist/boot/install.d.ts +9 -0
- package/dist/boot/install.d.ts.map +1 -1
- package/dist/boot/install.js +275 -185
- package/dist/boot/install.js.map +1 -1
- package/dist/boot/nesting.d.ts +33 -0
- package/dist/boot/nesting.d.ts.map +1 -0
- package/dist/boot/nesting.js +164 -0
- package/dist/boot/nesting.js.map +1 -0
- package/dist/boot/ownership.d.ts +0 -1
- package/dist/boot/ownership.d.ts.map +1 -1
- package/dist/boot/ownership.js +66 -51
- package/dist/boot/ownership.js.map +1 -1
- package/dist/boot/ports.js +30 -25
- package/dist/boot/ports.js.map +1 -1
- package/dist/boot/remote.d.ts.map +1 -1
- package/dist/boot/remote.js +39 -30
- package/dist/boot/remote.js.map +1 -1
- package/dist/boot/seed.d.ts.map +1 -1
- package/dist/boot/seed.js +27 -20
- package/dist/boot/seed.js.map +1 -1
- package/dist/boot/together.d.ts.map +1 -1
- package/dist/boot/together.js +71 -56
- package/dist/boot/together.js.map +1 -1
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -1
- package/dist/descriptor/FrondDescriptor.d.ts +7 -0
- package/dist/descriptor/FrondDescriptor.d.ts.map +1 -1
- package/dist/descriptor/MiddlewareEntry.d.ts +6 -5
- package/dist/descriptor/MiddlewareEntry.d.ts.map +1 -1
- package/dist/descriptor/ProviderEntry.d.ts +7 -0
- package/dist/descriptor/ProviderEntry.d.ts.map +1 -1
- package/dist/dispatch/HandlerFacade.d.ts +6 -1
- package/dist/dispatch/HandlerFacade.d.ts.map +1 -1
- package/dist/dispatch/HandlerFacade.js +23 -20
- package/dist/dispatch/HandlerFacade.js.map +1 -1
- package/dist/dispatch/OutputView.d.ts.map +1 -1
- package/dist/dispatch/OutputView.js +7 -0
- package/dist/dispatch/OutputView.js.map +1 -1
- package/dist/dispatch/PresenterExecutor.d.ts +6 -0
- package/dist/dispatch/PresenterExecutor.d.ts.map +1 -1
- package/dist/dispatch/PresenterExecutor.js +37 -22
- package/dist/dispatch/PresenterExecutor.js.map +1 -1
- package/dist/dispatch/Received.d.ts +3 -0
- package/dist/dispatch/Received.d.ts.map +1 -0
- package/dist/dispatch/Received.js +2 -0
- package/dist/dispatch/Received.js.map +1 -0
- package/dist/dispatch/Release.js +53 -47
- package/dist/dispatch/Release.js.map +1 -1
- package/dist/dispatch/StorageGuard.d.ts +21 -0
- package/dist/dispatch/StorageGuard.d.ts.map +1 -1
- package/dist/dispatch/StorageGuard.js +84 -66
- package/dist/dispatch/StorageGuard.js.map +1 -1
- package/dist/dispatch/decoded.d.ts +19 -0
- package/dist/dispatch/decoded.d.ts.map +1 -0
- package/dist/dispatch/decoded.js +36 -0
- package/dist/dispatch/decoded.js.map +1 -0
- package/dist/entry/facade.d.ts +9 -2
- package/dist/entry/facade.d.ts.map +1 -1
- package/dist/entry/facade.js +16 -7
- package/dist/entry/facade.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +2 -1
- package/dist/node.js.map +1 -1
- package/dist/prefab/CrudConstructor.d.ts.map +1 -1
- package/dist/prefab/CrudConstructor.js +3 -2
- package/dist/prefab/CrudConstructor.js.map +1 -1
- package/dist/prefab/CrudOps.d.ts +2 -2
- package/dist/prefab/CrudOps.d.ts.map +1 -1
- package/dist/prefab/MirrorConstructor.js +1 -1
- package/dist/prefab/MirrorConstructor.js.map +1 -1
- package/dist/prefab/Refreshed.d.ts +7 -2
- package/dist/prefab/Refreshed.d.ts.map +1 -1
- package/dist/storage/Storage.js +1 -1
- package/dist/storage/Storage.js.map +1 -1
- package/dist/storage/Store.d.ts.map +1 -1
- package/dist/storage/Store.js +35 -31
- package/dist/storage/Store.js.map +1 -1
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +14 -0
- package/dist/verify.js.map +1 -1
- package/dist/wire/AppMiddleware.d.ts +10 -1
- package/dist/wire/AppMiddleware.d.ts.map +1 -1
- package/dist/wire/AppMiddleware.js.map +1 -1
- package/dist/wire/AppNext.d.ts +2 -1
- package/dist/wire/AppNext.d.ts.map +1 -1
- package/dist/wire/Facade.d.ts +14 -2
- package/dist/wire/Facade.d.ts.map +1 -1
- package/dist/wire/Facade.js.map +1 -1
- package/dist/wire/FougereError.js +1 -1
- package/dist/wire/FougereError.js.map +1 -1
- package/dist/wire/Invocation.d.ts +1 -1
- package/dist/wire/Invocation.d.ts.map +1 -1
- package/dist/wire/Invocation.js +1 -1
- package/dist/wire/Invocation.js.map +1 -1
- package/dist/wire/OperationContract.js +1 -1
- package/dist/wire/OperationContract.js.map +1 -1
- package/dist/wire/Page.d.ts +32 -0
- package/dist/wire/Page.d.ts.map +1 -0
- package/dist/wire/Page.js +32 -0
- package/dist/wire/Page.js.map +1 -0
- package/dist/wire/Signature.d.ts +7 -0
- package/dist/wire/Signature.d.ts.map +1 -1
- package/dist/wire/binding.d.ts.map +1 -1
- package/dist/wire/binding.js +29 -57
- package/dist/wire/binding.js.map +1 -1
- package/dist/wire/drift.d.ts.map +1 -1
- package/dist/wire/drift.js +22 -19
- package/dist/wire/drift.js.map +1 -1
- package/package.json +4 -4
- package/src/EffectiveOperationModel.ts +19 -0
- package/src/FougereConfig.ts +24 -10
- package/src/FrondConfig.ts +0 -6
- package/src/FrondDeclaration.ts +2 -7
- package/src/FrondsStated.ts +72 -0
- package/src/StatedFrond.ts +51 -0
- package/src/StatedModules.ts +58 -0
- package/src/boot/CreateAppOptions.ts +19 -2
- package/src/boot/Emissions.ts +16 -5
- package/src/boot/bootstrap.ts +478 -359
- package/src/boot/declared.ts +3 -0
- package/src/boot/frame.ts +42 -11
- package/src/boot/hosted.ts +4 -3
- package/src/boot/install.ts +392 -212
- package/src/boot/nesting.ts +196 -0
- package/src/boot/ownership.ts +79 -51
- package/src/boot/ports.ts +45 -31
- package/src/boot/remote.ts +57 -32
- package/src/boot/seed.ts +36 -27
- package/src/boot/together.ts +98 -63
- package/src/contract.ts +2 -0
- package/src/descriptor/FrondDescriptor.ts +7 -0
- package/src/descriptor/MiddlewareEntry.ts +6 -5
- package/src/descriptor/ProviderEntry.ts +7 -0
- package/src/dispatch/HandlerFacade.ts +27 -17
- package/src/dispatch/OutputView.ts +7 -0
- package/src/dispatch/PresenterExecutor.ts +40 -22
- package/src/dispatch/Received.ts +2 -0
- package/src/dispatch/Release.ts +68 -44
- package/src/dispatch/StorageGuard.ts +90 -69
- package/src/dispatch/decoded.ts +37 -0
- package/src/entry/facade.ts +21 -9
- package/src/index.ts +4 -0
- package/src/node.ts +2 -1
- package/src/prefab/CrudConstructor.ts +3 -2
- package/src/prefab/CrudOps.ts +2 -2
- package/src/prefab/MirrorConstructor.ts +1 -1
- package/src/prefab/Refreshed.ts +7 -2
- package/src/storage/Storage.ts +2 -2
- package/src/storage/Store.ts +45 -27
- package/src/verify.ts +15 -0
- package/src/wire/AppMiddleware.ts +10 -1
- package/src/wire/AppNext.ts +2 -1
- package/src/wire/Facade.ts +14 -2
- package/src/wire/FougereError.ts +1 -1
- package/src/wire/Invocation.ts +2 -1
- package/src/wire/OperationContract.ts +1 -1
- package/src/wire/Page.ts +50 -0
- package/src/wire/Signature.ts +7 -0
- package/src/wire/binding.ts +26 -58
- package/src/wire/drift.ts +48 -21
package/src/wire/Page.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a paged operation answers — the rows, and what the storage said about the rest of them.
|
|
3
|
+
*
|
|
4
|
+
* An ENVELOPE rather than the array `Storage.list` hands back: that one carries `total` and
|
|
5
|
+
* `hasMore` as properties OF the array, and `JSON.stringify` keeps only the indices. Measured
|
|
6
|
+
* 2026-09-18 — `page.total` is 42 in this process and `undefined` behind `fronds:`, off the same
|
|
7
|
+
* code. What crosses has to be named, so it is a field.
|
|
8
|
+
*
|
|
9
|
+
* `Storage.list` keeps its array: it answers inside a frond and never crosses.
|
|
10
|
+
*/
|
|
11
|
+
export interface Page<Row> {
|
|
12
|
+
items: Row[];
|
|
13
|
+
/** Only when the caller asked to count. */
|
|
14
|
+
total?: number;
|
|
15
|
+
hasMore?: boolean;
|
|
16
|
+
endCursor?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The array `Storage.list` answers, read as the page an operation hands over.
|
|
21
|
+
*
|
|
22
|
+
* A storage handed in by a host may answer the envelope already — `storageFactory` is its door,
|
|
23
|
+
* and nothing there is obliged to subclass an array. Taken as it comes in that case.
|
|
24
|
+
*/
|
|
25
|
+
export function pageOf<Row>(rows: Page<Row> | readonly Row[]): Page<Row> {
|
|
26
|
+
if (!Array.isArray(rows)) return rows as Page<Row>;
|
|
27
|
+
|
|
28
|
+
const { total, hasMore, endCursor } = rows as unknown as Omit<Page<Row>, 'items'>;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
items: [...rows],
|
|
32
|
+
...(total !== undefined && { total }),
|
|
33
|
+
...(hasMore !== undefined && { hasMore }),
|
|
34
|
+
...(endCursor !== undefined && { endCursor }),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The value read as a page, or nothing.
|
|
40
|
+
*
|
|
41
|
+
* Read from the FORM, and arbitrated by the schema: an entity legally declaring a field named
|
|
42
|
+
* `items` answers a row, not a page, and it is the only thing that could tell the two apart.
|
|
43
|
+
*/
|
|
44
|
+
export function asPage(value: unknown, fields: Record<string, unknown>): Page<unknown> | undefined {
|
|
45
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
46
|
+
if ('items' in fields) return undefined;
|
|
47
|
+
const page = value as Page<unknown>;
|
|
48
|
+
|
|
49
|
+
return Array.isArray(page.items) ? page : undefined;
|
|
50
|
+
}
|
package/src/wire/Signature.ts
CHANGED
|
@@ -10,4 +10,11 @@ export interface Signature {
|
|
|
10
10
|
inherited?: boolean;
|
|
11
11
|
/** The operation in words — the first sentence of the method's doc comment. */
|
|
12
12
|
description?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The first thing in the return type that is not data — its path, and the type sitting there.
|
|
15
|
+
*
|
|
16
|
+
* Written whenever a checker read the signature, and judged only where nothing converts the
|
|
17
|
+
* output: what a declared entity answers is encoded by its fields.
|
|
18
|
+
*/
|
|
19
|
+
notData?: string;
|
|
13
20
|
}
|
package/src/wire/binding.ts
CHANGED
|
@@ -48,66 +48,34 @@ export function computeBindingPlan(
|
|
|
48
48
|
params: Param[],
|
|
49
49
|
collectorTypeNames: Set<string>,
|
|
50
50
|
): BindingPlan {
|
|
51
|
-
return params.map((param) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// 0. Fact, or the same fact before it is final. Both name themselves, so nothing has
|
|
59
|
-
// to be known in advance, and they come FIRST because branch 4 would otherwise hand
|
|
60
|
-
// either the caller's input under the name of something that happened.
|
|
61
|
-
const announced = param.type.name === 'Fact' || param.type.name === 'Pipe'
|
|
62
|
-
? param.type.generics?.[0]?.name
|
|
63
|
-
: undefined;
|
|
64
|
-
if (announced) {
|
|
65
|
-
return {
|
|
66
|
-
name: param.name,
|
|
67
|
-
source: {
|
|
68
|
-
kind: (param.type.name === 'Pipe' ? 'pipe' : 'fact') as 'pipe' | 'fact',
|
|
69
|
-
factName: lowerFirst(announced),
|
|
70
|
-
},
|
|
71
|
-
optional: param.optional ?? false,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
51
|
+
return params.map((param) => ({
|
|
52
|
+
name: param.name,
|
|
53
|
+
source: sourceOf(param, collectorTypeNames),
|
|
54
|
+
optional: param.optional ?? false,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
74
57
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Where ONE parameter gets its value, in the order the branches must be asked.
|
|
60
|
+
*
|
|
61
|
+
* A fact and a link name themselves, so they come FIRST: asked later, the last branch would hand
|
|
62
|
+
* the caller's input under the name of something that happened.
|
|
63
|
+
*/
|
|
64
|
+
function sourceOf(param: Param, collectorTypeNames: Set<string>): ParamSource {
|
|
65
|
+
const typeName = param.type.name;
|
|
66
|
+
// `lowerFirst`, never `toLowerCase()`: the collector set is keyed the way the scan spells it,
|
|
67
|
+
// and the two agree on one word only — `AuthorUser` looked up as `authoruser` missed
|
|
68
|
+
// `authorUser` and fell through to the request input.
|
|
69
|
+
const typeKey = lowerFirst(typeName);
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
source: { kind: 'context' as const },
|
|
89
|
-
optional: param.optional ?? false,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
71
|
+
const announced = typeName === 'Fact' || typeName === 'Pipe' ? param.type.generics?.[0]?.name : undefined;
|
|
72
|
+
if (announced) {
|
|
73
|
+
return { kind: typeName === 'Pipe' ? 'pipe' : 'fact', factName: lowerFirst(announced) };
|
|
74
|
+
}
|
|
92
75
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
name: param.name,
|
|
97
|
-
source: {
|
|
98
|
-
kind: 'param' as const,
|
|
99
|
-
name: param.name,
|
|
100
|
-
coerce: coercionFor(typeName),
|
|
101
|
-
},
|
|
102
|
-
optional: param.optional ?? false,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
76
|
+
if (collectorTypeNames.has(typeKey)) return { kind: 'collector', typeName: typeKey };
|
|
77
|
+
if (typeName === 'InvocationContext') return { kind: 'context' };
|
|
78
|
+
if (PRIMITIVES.has(typeName)) return { kind: 'param', name: param.name, coerce: coercionFor(typeName) };
|
|
105
79
|
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
name: param.name,
|
|
109
|
-
source: { kind: 'input' as const },
|
|
110
|
-
optional: param.optional ?? false,
|
|
111
|
-
};
|
|
112
|
-
});
|
|
80
|
+
return { kind: 'input' };
|
|
113
81
|
}
|
package/src/wire/drift.ts
CHANGED
|
@@ -15,8 +15,14 @@ export interface CardDrift {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/** Every facade of a card, by name. */
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/** What a door of that card answers, and the shape of the row it hands back. */
|
|
19
|
+
interface Answering {
|
|
20
|
+
ops: Set<string>;
|
|
21
|
+
schema?: SchemaDescriptor;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function facadesOf(card: IdentityCard, frond: string): Map<string, Answering> {
|
|
25
|
+
const found = new Map<string, Answering>();
|
|
20
26
|
for (const one of card.fronds) {
|
|
21
27
|
if (one.name !== frond) continue;
|
|
22
28
|
for (const facade of one.facades) {
|
|
@@ -37,37 +43,58 @@ function factsOf(card: IdentityCard, frond: string): Map<string, SchemaDescripto
|
|
|
37
43
|
|
|
38
44
|
/** What a consumer's synced copy no longer matches in what the producer serves. */
|
|
39
45
|
export function driftOf(mine: IdentityCard, theirs: IdentityCard, frond: string): CardDrift {
|
|
40
|
-
const mineFacades = facadesOf(mine, frond);
|
|
41
|
-
const served = facadesOf(theirs, frond);
|
|
42
46
|
const drift: CardDrift = { frond, missingDoors: [], missingOps: [], shapes: [], facts: [] };
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
driftInDoors(facadesOf(mine, frond), facadesOf(theirs, frond), drift);
|
|
49
|
+
driftInFacts(factsOf(mine, frond), factsOf(theirs, frond), drift);
|
|
50
|
+
|
|
51
|
+
return drift;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A door that is gone, an operation it no longer answers, or a row that changed shape. */
|
|
55
|
+
function driftInDoors(
|
|
56
|
+
mine: Map<string, Answering>,
|
|
57
|
+
theirs: Map<string, Answering>,
|
|
58
|
+
drift: CardDrift,
|
|
59
|
+
): void {
|
|
60
|
+
for (const [name, facade] of mine) {
|
|
61
|
+
const there = theirs.get(name);
|
|
62
|
+
if (!there) {
|
|
63
|
+
drift.missingDoors.push(name);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
47
66
|
|
|
48
67
|
const missing = [...facade.ops].filter((op) => !there.ops.has(op));
|
|
49
68
|
if (missing.length > 0) drift.missingOps.push({ facade: name, ops: missing.sort() });
|
|
50
69
|
|
|
51
|
-
if (facade.schema
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
70
|
+
if (!facade.schema || !there.schema) continue;
|
|
71
|
+
|
|
72
|
+
// `Card.diff` never guesses a rename — a field gone plus a field appeared lands in
|
|
73
|
+
// `ambiguous`, and only a declaration settles it. Here nobody can declare one, so the
|
|
74
|
+
// pair is reported as it is and a human reads it.
|
|
75
|
+
const moved = Card.fromDescriptor(facade.schema).diff(Card.fromDescriptor(there.schema));
|
|
76
|
+
if (moved.changes.length > 0) drift.shapes.push({ facade: name, changes: moved.changes });
|
|
58
77
|
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A fact nobody announces any more, or one whose payload moved under its readers. */
|
|
81
|
+
function driftInFacts(
|
|
82
|
+
mine: Map<string, SchemaDescriptor | undefined>,
|
|
83
|
+
theirs: Map<string, SchemaDescriptor | undefined>,
|
|
84
|
+
drift: CardDrift,
|
|
85
|
+
): void {
|
|
86
|
+
for (const [name, shape] of mine) {
|
|
87
|
+
if (!theirs.has(name)) {
|
|
88
|
+
drift.facts.push({ fact: name, changes: 'gone' });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
59
91
|
|
|
60
|
-
|
|
61
|
-
const servedFacts = factsOf(theirs, frond);
|
|
62
|
-
for (const [name, shape] of heldFacts) {
|
|
63
|
-
if (!servedFacts.has(name)) { drift.facts.push({ fact: name, changes: 'gone' }); continue; }
|
|
64
|
-
const there = servedFacts.get(name);
|
|
92
|
+
const there = theirs.get(name);
|
|
65
93
|
if (!shape || !there) continue;
|
|
94
|
+
|
|
66
95
|
const moved = Card.fromDescriptor(shape).diff(Card.fromDescriptor(there));
|
|
67
96
|
if (moved.changes.length > 0) drift.facts.push({ fact: name, changes: moved.changes });
|
|
68
97
|
}
|
|
69
|
-
|
|
70
|
-
return drift;
|
|
71
98
|
}
|
|
72
99
|
|
|
73
100
|
/** Whether anything at all separates the two cards. */
|