yarramate 1.21.0 → 1.22.1
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/fold-tree.d.ts +21 -8
- package/dist/fold-tree.js +118 -23
- package/dist/visual-app/assets/index-CM1MWXT2.js +394 -0
- package/dist/visual-app/index.html +1 -1
- package/dist/visual-app-lib/editor.js +26148 -26109
- package/dist/visual-app-lib/types/fold-tree.d.ts +21 -8
- package/package.json +1 -1
- package/dist/visual-app/assets/index-Ch27Sr5z.js +0 -394
package/dist/fold-tree.d.ts
CHANGED
|
@@ -110,15 +110,28 @@ export declare function nestingTree(edges: readonly FoldEdge[], nesting: readonl
|
|
|
110
110
|
/**
|
|
111
111
|
* The containment tree: what a view nests, plus what a pattern owns.
|
|
112
112
|
*
|
|
113
|
-
* A slot member joins the tree only when all
|
|
114
|
-
* a different way of getting the answer wrong:
|
|
113
|
+
* A slot member joins the tree only when all of these hold, and each condition
|
|
114
|
+
* is a different way of getting the answer wrong:
|
|
115
115
|
*
|
|
116
|
-
* - **
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* the
|
|
120
|
-
*
|
|
121
|
-
*
|
|
116
|
+
* - **Held inside one box.** A member's HOLDERS are every instance whose slots
|
|
117
|
+
* name it. One holder puts the member in that holder. Several put it in their
|
|
118
|
+
* lowest common ancestor, which is the level at which the holders diverge and
|
|
119
|
+
* therefore the innermost box that contains all of them. Holders with no
|
|
120
|
+
* common ancestor leave the member outside, because there is no one box it
|
|
121
|
+
* sits within and a single-parent tree would have to pick.
|
|
122
|
+
*
|
|
123
|
+
* This AMENDS ADR 0143's "Exclusive" rule, which kept every shared subject
|
|
124
|
+
* outside (#473 phase 3, ADR 0145, Nabeel's decision of 2026-09-05). The
|
|
125
|
+
* original reasoning was that two owners force a silent choice; it is only
|
|
126
|
+
* true when the owners sit in different boxes. Where both already sit under
|
|
127
|
+
* one box there is nothing to choose, and the old rule left 14 of the
|
|
128
|
+
* reference Landscape's 30 data objects outside the single application whose
|
|
129
|
+
* own parts were the things binding them.
|
|
130
|
+
* - **`owned` or `unwired`, never `context` alone.** A context slot names
|
|
131
|
+
* something the instance USES and does not contain — the upstream API it
|
|
132
|
+
* calls, the plane it runs on. Folding those would swallow half the landscape
|
|
133
|
+
* into whichever box happened to reference it. At least one binding must be
|
|
134
|
+
* `owned` or `unwired` for the member to fold at all.
|
|
122
135
|
* - **Not a ruling.** See {@link RULING_CORE_KINDS}.
|
|
123
136
|
*
|
|
124
137
|
* A view's own nesting wins where both apply: the view is the more specific
|
package/dist/fold-tree.js
CHANGED
|
@@ -127,6 +127,18 @@ export function nestingTree(edges, nesting, coreKindOf) {
|
|
|
127
127
|
* a straight-line ancestor of a cycle is still validly nested under its own
|
|
128
128
|
* non-cyclic parent. Mutates `parentOf` and returns what it removed.
|
|
129
129
|
*/
|
|
130
|
+
/** Whether `id` sits anywhere inside `ancestor` in the tree built so far. */
|
|
131
|
+
const isDescendantOf = (id, ancestor, parentOf) => {
|
|
132
|
+
const seen = new Set([id]);
|
|
133
|
+
let current = parentOf.get(id);
|
|
134
|
+
while (current !== undefined && !seen.has(current)) {
|
|
135
|
+
if (current === ancestor)
|
|
136
|
+
return true;
|
|
137
|
+
seen.add(current);
|
|
138
|
+
current = parentOf.get(current);
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
};
|
|
130
142
|
function unnestCycles(parentOf) {
|
|
131
143
|
const cycleMembers = new Set();
|
|
132
144
|
for (const start of parentOf.keys()) {
|
|
@@ -149,18 +161,59 @@ function unnestCycles(parentOf) {
|
|
|
149
161
|
parentOf.delete(id);
|
|
150
162
|
return [...cycleMembers];
|
|
151
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* The lowest node that contains every one of `ids`, counting each id as an
|
|
166
|
+
* ancestor of itself, or `undefined` when they do not share one.
|
|
167
|
+
*
|
|
168
|
+
* "Counting each id as an ancestor of itself" is the part that matters: where
|
|
169
|
+
* one holder already sits inside another, the answer is the outer holder rather
|
|
170
|
+
* than something above them both.
|
|
171
|
+
*/
|
|
172
|
+
const lowestCommonAncestor = (ids, parentOf) => {
|
|
173
|
+
const chainOf = (id) => {
|
|
174
|
+
const chain = [];
|
|
175
|
+
const seen = new Set();
|
|
176
|
+
let current = id;
|
|
177
|
+
while (current !== undefined && !seen.has(current)) {
|
|
178
|
+
seen.add(current);
|
|
179
|
+
chain.push(current);
|
|
180
|
+
current = parentOf.get(current);
|
|
181
|
+
}
|
|
182
|
+
return chain;
|
|
183
|
+
};
|
|
184
|
+
const [first, ...rest] = ids;
|
|
185
|
+
if (first === undefined)
|
|
186
|
+
return undefined;
|
|
187
|
+
const others = rest.map((id) => new Set(chainOf(id)));
|
|
188
|
+
// Walking the first chain from the node OUTWARDS makes the first hit the
|
|
189
|
+
// lowest by construction.
|
|
190
|
+
return chainOf(first).find((candidate) => others.every((chain) => chain.has(candidate)));
|
|
191
|
+
};
|
|
152
192
|
/**
|
|
153
193
|
* The containment tree: what a view nests, plus what a pattern owns.
|
|
154
194
|
*
|
|
155
|
-
* A slot member joins the tree only when all
|
|
156
|
-
* a different way of getting the answer wrong:
|
|
195
|
+
* A slot member joins the tree only when all of these hold, and each condition
|
|
196
|
+
* is a different way of getting the answer wrong:
|
|
157
197
|
*
|
|
158
|
-
* - **
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* the
|
|
162
|
-
*
|
|
163
|
-
*
|
|
198
|
+
* - **Held inside one box.** A member's HOLDERS are every instance whose slots
|
|
199
|
+
* name it. One holder puts the member in that holder. Several put it in their
|
|
200
|
+
* lowest common ancestor, which is the level at which the holders diverge and
|
|
201
|
+
* therefore the innermost box that contains all of them. Holders with no
|
|
202
|
+
* common ancestor leave the member outside, because there is no one box it
|
|
203
|
+
* sits within and a single-parent tree would have to pick.
|
|
204
|
+
*
|
|
205
|
+
* This AMENDS ADR 0143's "Exclusive" rule, which kept every shared subject
|
|
206
|
+
* outside (#473 phase 3, ADR 0145, Nabeel's decision of 2026-09-05). The
|
|
207
|
+
* original reasoning was that two owners force a silent choice; it is only
|
|
208
|
+
* true when the owners sit in different boxes. Where both already sit under
|
|
209
|
+
* one box there is nothing to choose, and the old rule left 14 of the
|
|
210
|
+
* reference Landscape's 30 data objects outside the single application whose
|
|
211
|
+
* own parts were the things binding them.
|
|
212
|
+
* - **`owned` or `unwired`, never `context` alone.** A context slot names
|
|
213
|
+
* something the instance USES and does not contain — the upstream API it
|
|
214
|
+
* calls, the plane it runs on. Folding those would swallow half the landscape
|
|
215
|
+
* into whichever box happened to reference it. At least one binding must be
|
|
216
|
+
* `owned` or `unwired` for the member to fold at all.
|
|
164
217
|
* - **Not a ruling.** See {@link RULING_CORE_KINDS}.
|
|
165
218
|
*
|
|
166
219
|
* A view's own nesting wins where both apply: the view is the more specific
|
|
@@ -178,22 +231,64 @@ export function foldTree(input) {
|
|
|
178
231
|
else
|
|
179
232
|
instances.add(membership.instance);
|
|
180
233
|
}
|
|
181
|
-
|
|
234
|
+
// Whether ANY of a member's bindings is one the instance holds it out by. A
|
|
235
|
+
// member bound only through context slots never folds, however many hold it.
|
|
236
|
+
const heldOutSomewhere = new Set();
|
|
182
237
|
for (const membership of input.memberships) {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// A
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
238
|
+
if (membership.wiring !== 'context')
|
|
239
|
+
heldOutSomewhere.add(membership.member);
|
|
240
|
+
}
|
|
241
|
+
const parentOf = new Map(fromNesting.parentOf);
|
|
242
|
+
const candidates = [
|
|
243
|
+
...new Set(input.memberships
|
|
244
|
+
.map(({ member }) => member)
|
|
245
|
+
.filter((member) =>
|
|
246
|
+
// A view's own nesting already placed it, and the view wins.
|
|
247
|
+
!parentOf.has(member) &&
|
|
248
|
+
heldOutSomewhere.has(member) &&
|
|
249
|
+
!RULING_CORE_KINDS.has(coreKindOf(member)) &&
|
|
250
|
+
// A node the input does not carry cannot be drawn inside anything.
|
|
251
|
+
coreKindById.has(member) &&
|
|
252
|
+
// Something that holds itself is not held by anything.
|
|
253
|
+
instancesOf.get(member)?.has(member) !== true)),
|
|
254
|
+
];
|
|
255
|
+
// Resolved in ROUNDS rather than one pass, because a member's holders may
|
|
256
|
+
// themselves be members whose own parents are decided here. Placing a member
|
|
257
|
+
// before its holders are settled would measure the lowest common ancestor
|
|
258
|
+
// against a tree that is still missing the levels that separate them, and the
|
|
259
|
+
// reference has five-deep chains (spec, mapping, call, client, application),
|
|
260
|
+
// so this is exercised rather than theoretical.
|
|
261
|
+
//
|
|
262
|
+
// A member is settled once it is placed or once it is known to stay outside.
|
|
263
|
+
// Whatever a round cannot decide it hands to the next; when a round decides
|
|
264
|
+
// nothing, what is left is a mutual dependency and stays outside, which is the
|
|
265
|
+
// same answer the cycle guard below would reach for it anyway.
|
|
266
|
+
let pending = candidates;
|
|
267
|
+
while (pending.length > 0) {
|
|
268
|
+
const deferred = [];
|
|
269
|
+
let decided = false;
|
|
270
|
+
for (const member of pending) {
|
|
271
|
+
const holders = [...(instancesOf.get(member) ?? [])];
|
|
272
|
+
if (holders.some((holder) => pending.includes(holder) && holder !== member)) {
|
|
273
|
+
deferred.push(member);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
decided = true;
|
|
277
|
+
const parent = holders.length === 1
|
|
278
|
+
? holders[0]
|
|
279
|
+
: lowestCommonAncestor(holders, parentOf);
|
|
280
|
+
if (parent === undefined || parent === member)
|
|
281
|
+
continue;
|
|
282
|
+
// A member that already contains one of its holders cannot also sit
|
|
283
|
+
// inside it. The cycle guard below is the backstop, not the rule.
|
|
284
|
+
if (holders.some((holder) => isDescendantOf(holder, member, parentOf))) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
parentOf.set(member, parent);
|
|
288
|
+
}
|
|
289
|
+
if (!decided)
|
|
290
|
+
break;
|
|
291
|
+
pending = deferred;
|
|
197
292
|
}
|
|
198
293
|
// Slot membership can close a loop the view's nesting alone did not, so the
|
|
199
294
|
// guard runs again over the combined tree rather than trusting the first.
|