@fougere/core 0.10.0-alpha.0 → 0.12.0-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/CallIdentity.d.ts.map +1 -1
- package/dist/CallIdentity.js +2 -0
- package/dist/CallIdentity.js.map +1 -1
- package/dist/Constraint.d.ts +1 -1
- package/dist/Constraint.d.ts.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/Source.d.ts.map +1 -1
- package/dist/Source.js +4 -3
- package/dist/Source.js.map +1 -1
- 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/Hosting.d.ts +38 -0
- package/dist/boot/Hosting.d.ts.map +1 -0
- package/dist/boot/Hosting.js +2 -0
- package/dist/boot/Hosting.js.map +1 -0
- package/dist/boot/Peer.d.ts +16 -0
- package/dist/boot/Peer.d.ts.map +1 -0
- package/dist/boot/Peer.js +2 -0
- package/dist/boot/Peer.js.map +1 -0
- package/dist/boot/bootstrap.d.ts.map +1 -1
- package/dist/boot/bootstrap.js +372 -235
- package/dist/boot/bootstrap.js.map +1 -1
- package/dist/boot/declared.d.ts.map +1 -1
- package/dist/boot/declared.js +38 -3
- 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 +19 -0
- package/dist/boot/install.d.ts.map +1 -1
- package/dist/boot/install.js +273 -178
- 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/peerOver.d.ts +4 -0
- package/dist/boot/peerOver.d.ts.map +1 -0
- package/dist/boot/peerOver.js +12 -0
- package/dist/boot/peerOver.js.map +1 -0
- package/dist/boot/ports.js +30 -25
- package/dist/boot/ports.js.map +1 -1
- package/dist/boot/relations.d.ts +54 -0
- package/dist/boot/relations.d.ts.map +1 -0
- package/dist/boot/relations.js +150 -0
- package/dist/boot/relations.js.map +1 -0
- package/dist/boot/remote.d.ts.map +1 -1
- package/dist/boot/remote.js +31 -28
- 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 +3 -0
- package/dist/boot/together.d.ts.map +1 -1
- package/dist/boot/together.js +72 -56
- package/dist/boot/together.js.map +1 -1
- package/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- 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/Dependent.d.ts +19 -0
- package/dist/dispatch/Dependent.d.ts.map +1 -0
- package/dist/dispatch/Dependent.js +2 -0
- package/dist/dispatch/Dependent.js.map +1 -0
- package/dist/dispatch/Dispatcher.d.ts +14 -1
- package/dist/dispatch/Dispatcher.d.ts.map +1 -1
- package/dist/dispatch/Dispatcher.js +36 -8
- package/dist/dispatch/Dispatcher.js.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/Journal.d.ts +25 -0
- package/dist/dispatch/Journal.d.ts.map +1 -0
- package/dist/dispatch/Journal.js +3 -0
- package/dist/dispatch/Journal.js.map +1 -0
- package/dist/dispatch/PresenterExecutor.d.ts +6 -0
- package/dist/dispatch/PresenterExecutor.d.ts.map +1 -1
- package/dist/dispatch/PresenterExecutor.js +30 -22
- package/dist/dispatch/PresenterExecutor.js.map +1 -1
- package/dist/dispatch/RelationCheck.d.ts +22 -0
- package/dist/dispatch/RelationCheck.d.ts.map +1 -0
- package/dist/dispatch/RelationCheck.js +2 -0
- package/dist/dispatch/RelationCheck.js.map +1 -0
- package/dist/dispatch/Release.d.ts +36 -0
- package/dist/dispatch/Release.d.ts.map +1 -0
- package/dist/dispatch/Release.js +130 -0
- package/dist/dispatch/Release.js.map +1 -0
- package/dist/dispatch/StorageGuard.d.ts +39 -1
- package/dist/dispatch/StorageGuard.d.ts.map +1 -1
- package/dist/dispatch/StorageGuard.js +128 -54
- package/dist/dispatch/StorageGuard.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -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/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 +29 -21
- 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/Emit.d.ts +9 -0
- package/dist/wire/Emit.d.ts.map +1 -1
- package/dist/wire/Emit.js +16 -0
- package/dist/wire/Emit.js.map +1 -1
- package/dist/wire/Invocation.d.ts +3 -2
- package/dist/wire/Invocation.d.ts.map +1 -1
- package/dist/wire/Invocation.js +5 -2
- package/dist/wire/Invocation.js.map +1 -1
- package/dist/wire/InvocationContext.d.ts +2 -0
- package/dist/wire/InvocationContext.d.ts.map +1 -1
- package/dist/wire/SignedCall.d.ts +2 -0
- package/dist/wire/SignedCall.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/CallIdentity.ts +2 -0
- package/src/Constraint.ts +1 -1
- 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/Source.ts +3 -2
- 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/Hosting.ts +38 -0
- package/src/boot/Peer.ts +15 -0
- package/src/boot/bootstrap.ts +512 -271
- package/src/boot/declared.ts +38 -3
- package/src/boot/frame.ts +42 -11
- package/src/boot/hosted.ts +4 -3
- package/src/boot/install.ts +400 -203
- package/src/boot/nesting.ts +196 -0
- package/src/boot/ownership.ts +79 -51
- package/src/boot/peerOver.ts +17 -0
- package/src/boot/ports.ts +45 -31
- package/src/boot/relations.ts +172 -0
- package/src/boot/remote.ts +47 -29
- package/src/boot/seed.ts +36 -27
- package/src/boot/together.ts +102 -63
- package/src/contract.ts +1 -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/Dependent.ts +19 -0
- package/src/dispatch/Dispatcher.ts +43 -7
- package/src/dispatch/HandlerFacade.ts +27 -17
- package/src/dispatch/Journal.ts +26 -0
- package/src/dispatch/PresenterExecutor.ts +33 -22
- package/src/dispatch/RelationCheck.ts +21 -0
- package/src/dispatch/Release.ts +180 -0
- package/src/dispatch/StorageGuard.ts +138 -55
- package/src/index.ts +8 -0
- package/src/node.ts +2 -1
- package/src/storage/Storage.ts +2 -2
- package/src/storage/Store.ts +39 -18
- package/src/verify.ts +15 -0
- package/src/wire/Emit.ts +23 -0
- package/src/wire/Invocation.ts +5 -2
- package/src/wire/InvocationContext.ts +2 -0
- package/src/wire/SignedCall.ts +2 -0
- package/src/wire/binding.ts +26 -58
- package/src/wire/drift.ts +48 -21
package/src/boot/declared.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { FrondDescriptor } from '../descriptor/FrondDescriptor.js';
|
|
7
7
|
import { facadeKeyOf } from '../wire/Facade.js';
|
|
8
|
+
import { factsAnnouncedBy, factsAwaitedBy, factsListenedTo } from '../wire/Emit.js';
|
|
8
9
|
import { type DeclaredEdge } from '../wire/topology/DeclaredEdge.js';
|
|
9
10
|
import { type DeclaredFrond } from '../wire/topology/DeclaredFrond.js';
|
|
10
11
|
import { type DeclaredTopology } from '../wire/topology/DeclaredTopology.js';
|
|
@@ -23,6 +24,9 @@ export function declaredTopologyOf({ fronds, remotes }: Declaring): DeclaredTopo
|
|
|
23
24
|
|
|
24
25
|
function placements(hosted: readonly FrondDescriptor[], remotes: Readonly<Record<string, string>>): DeclaredFrond[] {
|
|
25
26
|
const here = hosted
|
|
27
|
+
// A frond its family inherits from answers at no address, so it has no placement to
|
|
28
|
+
// report: it stands in every process that holds one of its children.
|
|
29
|
+
.filter((frond) => frond.handlers.length > 0)
|
|
26
30
|
.filter((frond) => !remotes[frond.name])
|
|
27
31
|
.map((frond) => ({ frond: frond.name, placement: 'local' as const }));
|
|
28
32
|
|
|
@@ -65,21 +69,52 @@ export function reachedBy(deps: readonly string[], from: string, index: Map<stri
|
|
|
65
69
|
return [...new Set(reached)];
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The two ways a call leaves a frond: a façade its code names, and a fact it announces.
|
|
74
|
+
*
|
|
75
|
+
* The second cannot be read the way the first is. A dependency names what it reaches, where an
|
|
76
|
+
* announcement names a SUBJECT and a subscriber names nothing at all — so the edge is found from
|
|
77
|
+
* both ends, the announcer's deps and the listener's binding plan. Reading the deps alone
|
|
78
|
+
* reported `demos/pipe-split` as four fronds with nothing between them, and three of its four
|
|
79
|
+
* fronds are reached by a fact.
|
|
80
|
+
*/
|
|
68
81
|
function crossings(hosted: readonly FrondDescriptor[]): DeclaredEdge[] {
|
|
69
82
|
const index = servedBy(hosted);
|
|
83
|
+
const heard = listenedBy(hosted);
|
|
70
84
|
const found = new Map<string, DeclaredEdge>();
|
|
85
|
+
const cross = (from: string, to: string) => {
|
|
86
|
+
if (from !== to) found.set(`${from} ${to}`, { from, to });
|
|
87
|
+
};
|
|
71
88
|
|
|
72
89
|
for (const frond of hosted) {
|
|
73
90
|
for (const handler of frond.handlers) {
|
|
74
|
-
for (const to of reachedBy(handler.deps, frond.name, index))
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
for (const to of reachedBy(handler.deps, frond.name, index)) cross(frond.name, to);
|
|
92
|
+
}
|
|
93
|
+
for (const fact of announcedIn(frond)) {
|
|
94
|
+
for (const to of heard.get(fact) ?? []) cross(frond.name, to);
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
|
|
80
98
|
return [...found.values()];
|
|
81
99
|
}
|
|
82
100
|
|
|
101
|
+
/** Which fronds listen to each fact — every one of them, since an announcement names no recipient. */
|
|
102
|
+
function listenedBy(hosted: readonly FrondDescriptor[]): Map<string, string[]> {
|
|
103
|
+
const index = new Map<string, string[]>();
|
|
104
|
+
for (const frond of hosted) {
|
|
105
|
+
for (const fact of factsListenedTo(frond.handlers)) {
|
|
106
|
+
index.set(fact, [...(index.get(fact) ?? []), frond.name]);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return index;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A fact announced here, whether the announcer waits for an answer or not. */
|
|
114
|
+
function announcedIn(frond: FrondDescriptor): string[] {
|
|
115
|
+
return [...new Set([...factsAnnouncedBy(frond.handlers), ...factsAwaitedBy(frond.handlers)])];
|
|
116
|
+
}
|
|
117
|
+
|
|
83
118
|
function hostOf(address: string): string {
|
|
84
119
|
try {
|
|
85
120
|
const url = new URL(address);
|
package/src/boot/frame.ts
CHANGED
|
@@ -45,6 +45,15 @@ function refuseAmbiguousUpsert(entity: string, gesture: string): never {
|
|
|
45
45
|
* The storage a member is handed inside a compensated frame: the same port, writing the same rows,
|
|
46
46
|
* leaving an inverse behind each time.
|
|
47
47
|
*/
|
|
48
|
+
/** What one member's writes are recorded against, inside one frame. */
|
|
49
|
+
interface Recording {
|
|
50
|
+
base: Undoable;
|
|
51
|
+
recorded: Undoable;
|
|
52
|
+
entity: string;
|
|
53
|
+
key: string;
|
|
54
|
+
journal: Undo[];
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
export function recording<T extends object>(storage: T, entity: string, schema: SchemaView, journal: Undo[]): T {
|
|
49
58
|
const base = storage as unknown as Undoable;
|
|
50
59
|
if (typeof base.create !== 'function' || typeof base.update !== 'function') return storage;
|
|
@@ -58,22 +67,37 @@ export function recording<T extends object>(storage: T, entity: string, schema:
|
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
const recorded = Object.create(storage) as T & Undoable;
|
|
70
|
+
const into: Recording = { base, recorded, entity, key, journal };
|
|
71
|
+
|
|
72
|
+
recordCreate(into);
|
|
73
|
+
recordUpdate(into);
|
|
74
|
+
recordDelete(into);
|
|
75
|
+
recordUpserts(into, schema);
|
|
76
|
+
|
|
77
|
+
return recorded;
|
|
78
|
+
}
|
|
61
79
|
|
|
80
|
+
/** The inverse of an insertion is a deletion by the key the write handed back. */
|
|
81
|
+
function recordCreate({ base, recorded, entity, key, journal }: Recording): void {
|
|
62
82
|
recorded.create = async function (...args) {
|
|
63
83
|
const row = await base.create.apply(this, args);
|
|
64
84
|
const id = String(row[key]);
|
|
65
85
|
journal.push({ what: `create ${entity}#${id}`, run: () => base.delete.call(this, id).then(() => undefined) });
|
|
66
86
|
return row;
|
|
67
87
|
};
|
|
88
|
+
}
|
|
68
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Compared against what the WRITE produced, not against the patch: an `update: 'now'` field is
|
|
92
|
+
* stamped by the storage, so the patch is not what the row now holds.
|
|
93
|
+
*/
|
|
94
|
+
function recordUpdate({ base, recorded, entity, journal }: Recording): void {
|
|
69
95
|
recorded.update = async function (...args) {
|
|
70
96
|
const [id, patch] = args;
|
|
71
97
|
const before = await base.findById.call(this, id);
|
|
72
98
|
const row = await base.update.apply(this, args);
|
|
73
99
|
if (!before) return row;
|
|
74
100
|
|
|
75
|
-
// Compare against what the WRITE produced, not against the patch: a `update: 'now'`
|
|
76
|
-
// field is stamped by the storage, so the patch is not what the row now holds.
|
|
77
101
|
const touched = Object.keys(patch);
|
|
78
102
|
const wrote = pick(row, touched);
|
|
79
103
|
journal.push({
|
|
@@ -97,7 +121,10 @@ export function recording<T extends object>(storage: T, entity: string, schema:
|
|
|
97
121
|
});
|
|
98
122
|
return row;
|
|
99
123
|
};
|
|
124
|
+
}
|
|
100
125
|
|
|
126
|
+
/** Nothing to take back when the row was already gone. */
|
|
127
|
+
function recordDelete({ base, recorded, entity, journal }: Recording): void {
|
|
101
128
|
recorded.delete = async function (id) {
|
|
102
129
|
const before = await base.findById.call(this, id);
|
|
103
130
|
const removed = await base.delete.call(this, id);
|
|
@@ -106,10 +133,14 @@ export function recording<T extends object>(storage: T, entity: string, schema:
|
|
|
106
133
|
}
|
|
107
134
|
return removed;
|
|
108
135
|
};
|
|
136
|
+
}
|
|
109
137
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
/**
|
|
139
|
+
* An upsert says neither what it wrote over nor what it inserted, so the frame reads the keys
|
|
140
|
+
* first: what was there is restored, what was not is deleted. One extra query for the page,
|
|
141
|
+
* the same bargain `update` already makes for one row.
|
|
142
|
+
*/
|
|
143
|
+
function recordUpserts({ base, recorded, entity, key, journal }: Recording, schema: SchemaView): void {
|
|
113
144
|
const undoUpsert = async function (this: unknown, rows: readonly Record<string, unknown>[]): Promise<Undo> {
|
|
114
145
|
const ids = rows.map((row) => String(row[key]));
|
|
115
146
|
const before = await base.findByKeys.call(this, ids);
|
|
@@ -125,27 +156,27 @@ export function recording<T extends object>(storage: T, entity: string, schema:
|
|
|
125
156
|
};
|
|
126
157
|
};
|
|
127
158
|
|
|
128
|
-
|
|
159
|
+
const upsert = base.upsert;
|
|
160
|
+
if (typeof upsert === 'function') {
|
|
129
161
|
recorded.upsert = async function (input, ...rest) {
|
|
130
162
|
if (declares(schema, 'unique')) refuseAmbiguousUpsert(entity, 'upsert');
|
|
131
163
|
const undo = await undoUpsert.call(this, [input]);
|
|
132
|
-
const row = await
|
|
164
|
+
const row = await upsert.call(this, input, ...rest);
|
|
133
165
|
journal.push(undo);
|
|
134
166
|
return row;
|
|
135
167
|
};
|
|
136
168
|
}
|
|
137
169
|
|
|
138
|
-
|
|
170
|
+
const upsertAll = base.upsertAll;
|
|
171
|
+
if (typeof upsertAll === 'function') {
|
|
139
172
|
recorded.upsertAll = async function (inputs, ...rest) {
|
|
140
173
|
if (declares(schema, 'unique')) refuseAmbiguousUpsert(entity, 'upsertAll');
|
|
141
174
|
const undo = await undoUpsert.call(this, inputs);
|
|
142
|
-
const written = await
|
|
175
|
+
const written = await upsertAll.call(this, inputs, ...rest);
|
|
143
176
|
journal.push(undo);
|
|
144
177
|
return written;
|
|
145
178
|
};
|
|
146
179
|
}
|
|
147
|
-
|
|
148
|
-
return recorded;
|
|
149
180
|
}
|
|
150
181
|
|
|
151
182
|
/** Replay the inverses, most recent first, and report rather than pretend. */
|
package/src/boot/hosted.ts
CHANGED
|
@@ -44,11 +44,12 @@ export async function hostedBy(sources: HostedSources): Promise<ScanResult> {
|
|
|
44
44
|
? await (typeof sources.scan === 'function' ? sources.scan() : sources.scan)
|
|
45
45
|
: undefined;
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
const stated = sources.fronds;
|
|
48
|
+
if (!stated) return scanned ?? { fronds: Fronds.hosting([]), diagnostics: [] };
|
|
48
49
|
|
|
49
50
|
const found = scanned?.fronds ?? [];
|
|
50
|
-
const configured =
|
|
51
|
-
const discovered = found.filter((f) => !
|
|
51
|
+
const configured = stated.map((f) => completed(f, found.find((s) => s.name === f.name)));
|
|
52
|
+
const discovered = found.filter((f) => !stated.some((s) => s.name === f.name));
|
|
52
53
|
|
|
53
54
|
return { fronds: Fronds.hosting([...configured, ...discovered]), diagnostics: scanned?.diagnostics ?? [] };
|
|
54
55
|
}
|