@yahoo/uds-create-config 5.58.0 → 5.59.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/entities/system/LinkedSystem.js +3 -3
- package/dist/entities/system/link-plan.d.ts +12 -1
- package/dist/entities/system/link-plan.js +77 -3
- package/dist/framework/Config.d.ts +19 -12
- package/dist/framework/Config.js +109 -21
- package/dist/framework/source-integrity.d.ts +13 -4
- package/dist/framework/source-integrity.js +40 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -303,8 +303,8 @@ const LinkedSystemDefinition = defineEntity({
|
|
|
303
303
|
* Draw entities from a linked system: the pin's `link` declaration grows to cover them and the
|
|
304
304
|
* build mints a pointer row per entity, in this one op. A row of this config's own at a covered
|
|
305
305
|
* path is deleted so theirs shows through; a row held through another pin becomes an extension
|
|
306
|
-
* through this one when `onLinked` is `'take'
|
|
307
|
-
* is written.
|
|
306
|
+
* through this one when `onLinked` is `'take'`, and the other pin's `options` entry there goes with
|
|
307
|
+
* it. `link-plan` says what that replaces before anything is written.
|
|
308
308
|
*/
|
|
309
309
|
link: {
|
|
310
310
|
input: linkInput,
|
|
@@ -332,7 +332,7 @@ const LinkedSystemDefinition = defineEntity({
|
|
|
332
332
|
input: linkInput,
|
|
333
333
|
readOnly: true,
|
|
334
334
|
label: "Plan a link",
|
|
335
|
-
description: "What `link` with the same input replaces — rows of yours deleted so the source shows through, every path another linked system provides (`linked`, turned into extensions when `onLinked` says take), members left alone because they are already here from the same origin, paths another linked system declares that the link reaches as a different entity (`settled`), whose row stays, and why the config would refuse the link (`blocked`), a collision it would leave among them. Writes nothing.",
|
|
335
|
+
description: "What `link` with the same input replaces — rows of yours deleted so the source shows through, every path another linked system provides (`linked`, turned into extensions when `onLinked` says take), the `options` entries another linked system holds at what a take moves, which it removes since they would apply to nothing (`dropped`), members left alone because they are already here from the same origin, paths another linked system declares that the link reaches as a different entity (`settled`), whose row stays, and why the config would refuse the link (`blocked`), a collision it would leave among them. Writes nothing.",
|
|
336
336
|
scope: "item",
|
|
337
337
|
title: (entity) => `Plan a link from ${entity}`,
|
|
338
338
|
example: linkExample,
|
|
@@ -22,6 +22,13 @@ interface LinkedElsewhere extends EntityAddress {
|
|
|
22
22
|
/** The slug of the pin it comes through. */
|
|
23
23
|
readonly from: string;
|
|
24
24
|
}
|
|
25
|
+
/** An entry in another pin's `options` that a take leaves applying to nothing, and removes. */
|
|
26
|
+
interface DroppedDirective extends EntityAddress {
|
|
27
|
+
/** The slug of the pin whose entry it was. */
|
|
28
|
+
readonly from: string;
|
|
29
|
+
/** The accessor the entry sits under in that pin's `options`. */
|
|
30
|
+
readonly accessor: string;
|
|
31
|
+
}
|
|
25
32
|
/** One row a link mints. `named` when a path the call named covers it; otherwise it comes along
|
|
26
33
|
* because something linked points at it. */
|
|
27
34
|
interface LinkArrival extends EntityAddress {
|
|
@@ -40,6 +47,10 @@ interface LinkPlan {
|
|
|
40
47
|
readonly linked: readonly LinkedElsewhere[];
|
|
41
48
|
/** Rows held through another pin, turned into extensions through this one. */
|
|
42
49
|
readonly extended: readonly EntityAddress[];
|
|
50
|
+
/** The other pins' `options` entries at what the take moves, removed with it: they adjust only the
|
|
51
|
+
* rows their pin derives, and the row there follows this one now. Not carried onto the taken row,
|
|
52
|
+
* since an entry corrects what that pin brings rather than stating a value of this config's own. */
|
|
53
|
+
readonly dropped: readonly DroppedDirective[];
|
|
43
54
|
/** Members already here from the source's own origin, left alone and kept out of the declaration. */
|
|
44
55
|
readonly shared: readonly EntityAddress[];
|
|
45
56
|
/** Paths another pin offers that a `hybrid` link reaches as a different entity. The other pin's
|
|
@@ -193,4 +204,4 @@ declare function sourceRowStates({
|
|
|
193
204
|
readonly slug: string;
|
|
194
205
|
}): SourceRowState[];
|
|
195
206
|
//#endregion
|
|
196
|
-
export { BreakOutcome, BreakPlan, BreakPlanInput, BreakPlanRow, BreakReach, LinkArrival, LinkPaths, LinkPlan, LinkPlanInput, LinkedElsewhere, OnLinked, SourceRowState, UnlinkPlan, UnlinkPlanInput, planBreak, planLink, planUnlink, sourceRowStates };
|
|
207
|
+
export { BreakOutcome, BreakPlan, BreakPlanInput, BreakPlanRow, BreakReach, DroppedDirective, LinkArrival, LinkPaths, LinkPlan, LinkPlanInput, LinkedElsewhere, OnLinked, SourceRowState, UnlinkPlan, UnlinkPlanInput, planBreak, planLink, planUnlink, sourceRowStates };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { collectRefs, isMintedRow, ref, sourceOf, splitRef } from "../../framework/utils/refs.js";
|
|
2
2
|
import { isConfigRejection, rejectData } from "../../framework/rejection.js";
|
|
3
3
|
import { blockedBy } from "../../framework/plan.js";
|
|
4
|
-
import { excludedCondition } from "../../framework/source-integrity.js";
|
|
4
|
+
import { danglingDirectives, excludedCondition } from "../../framework/source-integrity.js";
|
|
5
|
+
import { sameOrigin } from "../../framework/Config.js";
|
|
5
6
|
import { PIN_KIND, covers, declarationOf, entriesFor, excludePaths, excludes, includePaths, narrowDeclaration } from "./link-declaration.js";
|
|
6
7
|
import { planCopy, sourceEntries } from "./copy-plan.js";
|
|
7
8
|
//#region src/entities/system/link-plan.ts
|
|
@@ -40,7 +41,7 @@ function sharedWithOrigin({ config, source, slug, kind, path }) {
|
|
|
40
41
|
kind,
|
|
41
42
|
path
|
|
42
43
|
});
|
|
43
|
-
if (!theirs || !mine || theirs
|
|
44
|
+
if (!theirs || !mine || !sameOrigin(theirs, mine)) return void 0;
|
|
44
45
|
return theirs.pin.displayName ?? theirs.key;
|
|
45
46
|
}
|
|
46
47
|
/** Another pin whose declaration covers the path and whose source has it: the pin the path would
|
|
@@ -237,11 +238,20 @@ function planLink({ config, input }) {
|
|
|
237
238
|
}
|
|
238
239
|
declared = Object.keys(next).length === 0 ? current : next;
|
|
239
240
|
}
|
|
240
|
-
const
|
|
241
|
+
const planned = [
|
|
241
242
|
...takes,
|
|
242
243
|
...declares,
|
|
243
244
|
...shadows
|
|
244
245
|
];
|
|
246
|
+
const { dropped, patches: drops } = takes.length > 0 ? directivesTakenFrom({
|
|
247
|
+
config,
|
|
248
|
+
slug,
|
|
249
|
+
patches: planned
|
|
250
|
+
}) : {
|
|
251
|
+
dropped: [],
|
|
252
|
+
patches: []
|
|
253
|
+
};
|
|
254
|
+
const patches = [...planned, ...drops];
|
|
245
255
|
return {
|
|
246
256
|
declared,
|
|
247
257
|
...outcome({
|
|
@@ -254,10 +264,74 @@ function planLink({ config, input }) {
|
|
|
254
264
|
addresses,
|
|
255
265
|
linked,
|
|
256
266
|
extended,
|
|
267
|
+
dropped,
|
|
257
268
|
shared,
|
|
258
269
|
patches
|
|
259
270
|
};
|
|
260
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* The other pins' `options` entries a take leaves applying to nothing, and the pin edits that remove
|
|
274
|
+
* them.
|
|
275
|
+
*
|
|
276
|
+
* An entry adjusts the rows its pin derives. A take writes this config's own row at the path, one that
|
|
277
|
+
* follows `slug`, so the other pin's entry stays in its `options` reading as though it still applied,
|
|
278
|
+
* and the build refuses it ({@link danglingDirectives}). Read off the config the take leaves, so a
|
|
279
|
+
* folder whose members all moved counts the way the build will count it, and only what the take
|
|
280
|
+
* itself left behind: an entry that already applied to nothing is the build's to report, not this
|
|
281
|
+
* take's to remove.
|
|
282
|
+
*
|
|
283
|
+
* Nothing when the take is refused; `outcome` says why.
|
|
284
|
+
*/
|
|
285
|
+
function directivesTakenFrom({ config, slug, patches }) {
|
|
286
|
+
let after;
|
|
287
|
+
try {
|
|
288
|
+
after = config.applyAll(patches).withDerivedRows();
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (!isConfigRejection(error)) throw error;
|
|
291
|
+
return {
|
|
292
|
+
dropped: [],
|
|
293
|
+
patches: []
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const key = (at) => `${at.slug}\u0000${at.accessor}\u0000${at.path}`;
|
|
297
|
+
const already = new Set(danglingDirectives(config).map(key));
|
|
298
|
+
const dropped = [];
|
|
299
|
+
for (const at of danglingDirectives(after)) {
|
|
300
|
+
if (at.missing !== "row" || at.heldBy !== slug || already.has(key(at))) continue;
|
|
301
|
+
dropped.push({
|
|
302
|
+
kind: config.accessors()[at.accessor]?.kind ?? at.accessor,
|
|
303
|
+
path: at.path,
|
|
304
|
+
from: at.slug,
|
|
305
|
+
accessor: at.accessor
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
const edits = [];
|
|
309
|
+
for (const pin of new Set(dropped.map((at) => at.from))) {
|
|
310
|
+
const options = config.storedBody("linked-system", pin).options ?? {};
|
|
311
|
+
const data = {};
|
|
312
|
+
let emptied = 0;
|
|
313
|
+
for (const [accessor, entries] of Object.entries(options)) {
|
|
314
|
+
const going = dropped.filter((at) => at.from === pin && at.accessor === accessor).map((at) => at.path);
|
|
315
|
+
if (going.length === 0) continue;
|
|
316
|
+
if (Object.keys(entries ?? {}).filter((path) => !going.includes(path)).length > 0) {
|
|
317
|
+
for (const path of going) data[`options.${accessor}.${path}`] = null;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
data[`options.${accessor}`] = null;
|
|
321
|
+
emptied += 1;
|
|
322
|
+
}
|
|
323
|
+
edits.push({
|
|
324
|
+
kind: PIN_KIND,
|
|
325
|
+
operation: "update",
|
|
326
|
+
path: pin,
|
|
327
|
+
data: emptied === Object.keys(options).length ? { options: null } : data
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
dropped,
|
|
332
|
+
patches: edits
|
|
333
|
+
};
|
|
334
|
+
}
|
|
261
335
|
/** What a plan reports of a refusal: its message, and the collisions when that's what it names. */
|
|
262
336
|
function refusal(error) {
|
|
263
337
|
const collisions = error.issues.flatMap((issue) => issue.collisions ?? []);
|
|
@@ -1972,10 +1972,7 @@ declare class Config {
|
|
|
1972
1972
|
* kind's `sourceOrigin`, so the two pins may name different versions. `undefined` for a source of
|
|
1973
1973
|
* one hop, or one whose system no pin here reaches.
|
|
1974
1974
|
*
|
|
1975
|
-
* The two pins match
|
|
1976
|
-
* the whole key: a pin by path is stamped with its sibling's id when it's pushed, so the Bridge's
|
|
1977
|
-
* `yos` reads `configPath` and `systemId` where this config's pin to the same system names only the
|
|
1978
|
-
* id — and a `configPath` is relative to the config holding it, so it can't be compared across two.
|
|
1975
|
+
* The two pins match by {@link sameOriginFields}.
|
|
1979
1976
|
*/
|
|
1980
1977
|
private directRoute;
|
|
1981
1978
|
/**
|
|
@@ -2569,11 +2566,15 @@ declare class Config {
|
|
|
2569
2566
|
* to a system linked to a foundation answers with the foundation. A row a materialized artifact
|
|
2570
2567
|
* flattened to a literal has no pointer left, so the pin whose `link` declaration covers the path
|
|
2571
2568
|
* stands in, provided the row still reads as the source's ({@link matchesSource}); one the
|
|
2572
|
-
* publisher changed is its own
|
|
2573
|
-
*
|
|
2569
|
+
* publisher changed is its own, and so is a linked row this config lays a value of its own over: the
|
|
2570
|
+
* Bridge's `spectrum/blue/0` points at Yahoo OS's with the Bridge's colours on it, which is the
|
|
2571
|
+
* Bridge's entity, not a second route to Yahoo OS's. Two answers that are {@link sameOrigin} are one
|
|
2572
|
+
* entity reached by two routes, whatever versions the pins name: they compare the pin kind's
|
|
2573
|
+
* `sourceOrigin`, not its identity.
|
|
2574
2574
|
*
|
|
2575
2575
|
* `pin` is the pin entity at the hop where the answer was found, for a surface that names the
|
|
2576
|
-
* origin the way it names any source.
|
|
2576
|
+
* origin the way it names any source. `key` is its origin fields joined, for a surface with no
|
|
2577
|
+
* name to show.
|
|
2577
2578
|
*/
|
|
2578
2579
|
originOf({
|
|
2579
2580
|
kind,
|
|
@@ -2581,10 +2582,7 @@ declare class Config {
|
|
|
2581
2582
|
}: {
|
|
2582
2583
|
kind: string;
|
|
2583
2584
|
path: string;
|
|
2584
|
-
}):
|
|
2585
|
-
key: string;
|
|
2586
|
-
pin: Entity;
|
|
2587
|
-
} | undefined;
|
|
2585
|
+
}): Origin | undefined;
|
|
2588
2586
|
/**
|
|
2589
2587
|
* The pin whose `link` declaration covers `kind:path` while the row there still reads as that
|
|
2590
2588
|
* source's: how a row a materialized artifact flattened to a literal is traced back to the pin it
|
|
@@ -2899,5 +2897,14 @@ declare class Config {
|
|
|
2899
2897
|
* links from the provided sources. A declared `linkedKind` with no provided source throws. */
|
|
2900
2898
|
protected hydrateFrom(stored: StoredConfig, options?: HydrationOptions): this;
|
|
2901
2899
|
}
|
|
2900
|
+
/** Where an entity originates ({@link Config.originOf}): the pin at the hop that answered, and its origin fields. */
|
|
2901
|
+
interface Origin {
|
|
2902
|
+
readonly key: string;
|
|
2903
|
+
/** The pin kind's `sourceOrigin` fields as the pin reads them, `''` where unset. */
|
|
2904
|
+
readonly fields: readonly string[];
|
|
2905
|
+
readonly pin: Entity;
|
|
2906
|
+
}
|
|
2907
|
+
/** Whether two {@link Config.originOf} answers are one origin, by {@link sameOriginFields}. */
|
|
2908
|
+
declare function sameOrigin(a: Origin, b: Origin): boolean;
|
|
2902
2909
|
//#endregion
|
|
2903
|
-
export { AppliedOp, AttachedSources, BatchReceipt, Config, CssGrammar, Edition, HydrationOptions, ListOptions, LiveConfig, Patch, RecordedSource, SERIALIZED_CONFIG_VERSION, SerializedConfig, SettledSource, SkippedOp, SourceCollision, SourcesBySlug, StoredConfig, SubPatch, TreeNode };
|
|
2910
|
+
export { AppliedOp, AttachedSources, BatchReceipt, Config, CssGrammar, Edition, HydrationOptions, ListOptions, LiveConfig, Origin, Patch, RecordedSource, SERIALIZED_CONFIG_VERSION, SerializedConfig, SettledSource, SkippedOp, SourceCollision, SourcesBySlug, StoredConfig, SubPatch, TreeNode, sameOrigin };
|
package/dist/framework/Config.js
CHANGED
|
@@ -3992,10 +3992,7 @@ var Config = class Config {
|
|
|
3992
3992
|
* kind's `sourceOrigin`, so the two pins may name different versions. `undefined` for a source of
|
|
3993
3993
|
* one hop, or one whose system no pin here reaches.
|
|
3994
3994
|
*
|
|
3995
|
-
* The two pins match
|
|
3996
|
-
* the whole key: a pin by path is stamped with its sibling's id when it's pushed, so the Bridge's
|
|
3997
|
-
* `yos` reads `configPath` and `systemId` where this config's pin to the same system names only the
|
|
3998
|
-
* id — and a `configPath` is relative to the config holding it, so it can't be compared across two.
|
|
3995
|
+
* The two pins match by {@link sameOriginFields}.
|
|
3999
3996
|
*/
|
|
4000
3997
|
directRoute(source) {
|
|
4001
3998
|
const hops = sourceHops(source);
|
|
@@ -4009,8 +4006,7 @@ var Config = class Config {
|
|
|
4009
4006
|
if (theirs === void 0) continue;
|
|
4010
4007
|
const direct = this.sourceSlugs().find((slug) => {
|
|
4011
4008
|
const mine = this.sourceFields(sourceKind, slug, "sourceOrigin");
|
|
4012
|
-
|
|
4013
|
-
return shared.length > 0 && shared.every((field) => theirs[field] === mine?.[field]);
|
|
4009
|
+
return mine !== void 0 && sameOriginFields(theirs, mine);
|
|
4014
4010
|
});
|
|
4015
4011
|
if (direct !== void 0) return direct;
|
|
4016
4012
|
}
|
|
@@ -5142,6 +5138,40 @@ var Config = class Config {
|
|
|
5142
5138
|
const folders = [];
|
|
5143
5139
|
/** Folders this pass leaves as they stand — already held, over rows it still claims. */
|
|
5144
5140
|
const standing = /* @__PURE__ */ new Set();
|
|
5141
|
+
const taken = [];
|
|
5142
|
+
for (const kind of this.ownedKinds()) for (const [path, stored] of this.state.snapshot.slice(kind).items) {
|
|
5143
|
+
const body = stored.toJSON();
|
|
5144
|
+
if (!isExtendedRef(body) || isMintedRow(body)) continue;
|
|
5145
|
+
if (pathOf(body) !== path) continue;
|
|
5146
|
+
const slug = sourceOf(body);
|
|
5147
|
+
if (slug !== void 0) taken.push({
|
|
5148
|
+
kind,
|
|
5149
|
+
path,
|
|
5150
|
+
slug
|
|
5151
|
+
});
|
|
5152
|
+
}
|
|
5153
|
+
/** Every folder a pin has a member under this pass, claimed or taken, as `slug kind path`. */
|
|
5154
|
+
const occupied = /* @__PURE__ */ new Set();
|
|
5155
|
+
const occupy = (slug, kind, path) => {
|
|
5156
|
+
const segments = path.split("/");
|
|
5157
|
+
for (let depth = 1; depth < segments.length; depth++) occupied.add(`${slug}\u0000${kind}\u0000${segments.slice(0, depth).join("/")}`);
|
|
5158
|
+
};
|
|
5159
|
+
for (const [target, slug] of claimedFrom) occupy(slug, kindOf(target), pathOf(target));
|
|
5160
|
+
for (const { kind, path, slug } of taken) occupy(slug, kind, path);
|
|
5161
|
+
/**
|
|
5162
|
+
* Whether a folder already here stays as it is. One this config authored does, and so does one
|
|
5163
|
+
* minted through a pin that still has a member under it. One minted through a pin that has none
|
|
5164
|
+
* left there goes to the pin its members follow now: after a take, a `colorMode` whose modes all
|
|
5165
|
+
* follow Yahoo OS kept pointing at the Bridge's, its fields read from a system none of it follows.
|
|
5166
|
+
*/
|
|
5167
|
+
const stands = (kind, path) => {
|
|
5168
|
+
const held = this.state.snapshot.group(kind, path)?.toJSON();
|
|
5169
|
+
if (held === void 0) return false;
|
|
5170
|
+
if (!isMintedRow(held)) return true;
|
|
5171
|
+
const pointer = held.ref;
|
|
5172
|
+
const through = isRef(pointer) ? sourceOf(pointer) : void 0;
|
|
5173
|
+
return through !== void 0 && occupied.has(`${through}\u0000${kind}\u0000${path}`);
|
|
5174
|
+
};
|
|
5145
5175
|
for (const [slug, paths] of offeredBy) {
|
|
5146
5176
|
const source = this.sourceFor(slug);
|
|
5147
5177
|
if (!source) continue;
|
|
@@ -5153,7 +5183,7 @@ var Config = class Config {
|
|
|
5153
5183
|
for (const group of source.groups(kind)) {
|
|
5154
5184
|
const prefix = `${group.path}/`;
|
|
5155
5185
|
if (!under.some((path) => path.startsWith(prefix))) continue;
|
|
5156
|
-
if (
|
|
5186
|
+
if (stands(kind, group.path)) {
|
|
5157
5187
|
standing.add(`group:${kind}:${group.path}`);
|
|
5158
5188
|
continue;
|
|
5159
5189
|
}
|
|
@@ -5166,6 +5196,27 @@ var Config = class Config {
|
|
|
5166
5196
|
}
|
|
5167
5197
|
}
|
|
5168
5198
|
}
|
|
5199
|
+
const derivedFolders = new Set(folders.map(({ kind, path }) => `${kind}:${path}`));
|
|
5200
|
+
for (const { kind, path, slug } of taken) {
|
|
5201
|
+
const source = this.sourceFor(slug);
|
|
5202
|
+
if (!source || !this.entityOf(kind)) continue;
|
|
5203
|
+
for (const group of source.groups(kind)) {
|
|
5204
|
+
if (!path.startsWith(`${group.path}/`)) continue;
|
|
5205
|
+
const target = `${kind}:${group.path}`;
|
|
5206
|
+
if (derivedFolders.has(target) || standing.has(`group:${target}`)) continue;
|
|
5207
|
+
if (stands(kind, group.path)) {
|
|
5208
|
+
standing.add(`group:${target}`);
|
|
5209
|
+
continue;
|
|
5210
|
+
}
|
|
5211
|
+
derivedFolders.add(target);
|
|
5212
|
+
folders.push({
|
|
5213
|
+
kind,
|
|
5214
|
+
path: group.path,
|
|
5215
|
+
slug
|
|
5216
|
+
});
|
|
5217
|
+
claimedFrom.set(target, slug);
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5169
5220
|
for (const [target, slug] of claimedFrom) {
|
|
5170
5221
|
if (folders.some(({ kind, path }) => `${kind}:${path}` === target)) continue;
|
|
5171
5222
|
changes.push(derivedItemChange(kindOf(target), pathOf(target), ref(kindOf(target), pathOf(target), slug).__ref, overlayBody({
|
|
@@ -5328,7 +5379,22 @@ var Config = class Config {
|
|
|
5328
5379
|
const source = this.sourceFor(slug);
|
|
5329
5380
|
if (!source) return [];
|
|
5330
5381
|
const members = /* @__PURE__ */ new Set();
|
|
5331
|
-
|
|
5382
|
+
/**
|
|
5383
|
+
* Every entity or folder visited, and whether a linked row reached it.
|
|
5384
|
+
*
|
|
5385
|
+
* A taken row is visited twice — first as an authored row, then as one its pin adopts — and only a
|
|
5386
|
+
* linked visit lands the items a body points at. Remembering only THAT a target was visited let the
|
|
5387
|
+
* unlinked pass claim the style properties and token folders it found, so the linked pass skipped
|
|
5388
|
+
* them and a mode their tokens vary under never got a row. A target reached unlinked is visited
|
|
5389
|
+
* again when a linked row reaches it; one reached linked is done.
|
|
5390
|
+
*/
|
|
5391
|
+
const reached = /* @__PURE__ */ new Map();
|
|
5392
|
+
const reaching = (target, linked) => {
|
|
5393
|
+
const prior = reached.get(target);
|
|
5394
|
+
if (prior === true || prior === false && !linked) return false;
|
|
5395
|
+
reached.set(target, linked);
|
|
5396
|
+
return true;
|
|
5397
|
+
};
|
|
5332
5398
|
/**
|
|
5333
5399
|
* A style bag's KEYS name style properties, and a borrowed bag's name the SOURCE's.
|
|
5334
5400
|
*
|
|
@@ -5346,11 +5412,11 @@ var Config = class Config {
|
|
|
5346
5412
|
if (!sites) return;
|
|
5347
5413
|
for (const { bag } of sites(body)) for (const prop of Object.keys(bag)) {
|
|
5348
5414
|
const target = `style-property:${prop}`;
|
|
5349
|
-
if (members.has(target)) continue;
|
|
5350
5415
|
const local = this.storedBody("style-property", prop);
|
|
5351
5416
|
if (local && !isMintedRow(local)) continue;
|
|
5352
5417
|
const declared = source.resolve("style-property", prop);
|
|
5353
5418
|
if (!declared) continue;
|
|
5419
|
+
if (!reaching(target, linked)) continue;
|
|
5354
5420
|
members.add(target);
|
|
5355
5421
|
visitEntity("style-property", declared.toJSON(), true, linked);
|
|
5356
5422
|
}
|
|
@@ -5381,19 +5447,19 @@ var Config = class Config {
|
|
|
5381
5447
|
const target = `${kind}:${path}`;
|
|
5382
5448
|
const item = source.resolve(kind, path);
|
|
5383
5449
|
if (item) {
|
|
5384
|
-
if (!linked ||
|
|
5450
|
+
if (!linked || !reaching(target, true)) continue;
|
|
5385
5451
|
members.add(target);
|
|
5386
5452
|
visitEntity(kind, item.toJSON(), true, true);
|
|
5387
5453
|
continue;
|
|
5388
5454
|
}
|
|
5389
|
-
if (
|
|
5390
|
-
walked.add(target);
|
|
5455
|
+
if (!reaching(target, linked)) continue;
|
|
5391
5456
|
if (!source.groups(kind).some((node) => node.path === path)) continue;
|
|
5392
5457
|
const prefix = `${path}/`;
|
|
5393
5458
|
for (const member of source.list(kind)) {
|
|
5394
5459
|
if (!member.path.startsWith(prefix)) continue;
|
|
5395
|
-
|
|
5396
|
-
|
|
5460
|
+
const at = `${kind}:${member.path}`;
|
|
5461
|
+
members.add(at);
|
|
5462
|
+
if (reaching(at, linked)) visitEntity(kind, member.toJSON(), true, linked);
|
|
5397
5463
|
}
|
|
5398
5464
|
}
|
|
5399
5465
|
};
|
|
@@ -5519,15 +5585,21 @@ var Config = class Config {
|
|
|
5519
5585
|
* to a system linked to a foundation answers with the foundation. A row a materialized artifact
|
|
5520
5586
|
* flattened to a literal has no pointer left, so the pin whose `link` declaration covers the path
|
|
5521
5587
|
* stands in, provided the row still reads as the source's ({@link matchesSource}); one the
|
|
5522
|
-
* publisher changed is its own
|
|
5523
|
-
*
|
|
5588
|
+
* publisher changed is its own, and so is a linked row this config lays a value of its own over: the
|
|
5589
|
+
* Bridge's `spectrum/blue/0` points at Yahoo OS's with the Bridge's colours on it, which is the
|
|
5590
|
+
* Bridge's entity, not a second route to Yahoo OS's. Two answers that are {@link sameOrigin} are one
|
|
5591
|
+
* entity reached by two routes, whatever versions the pins name: they compare the pin kind's
|
|
5592
|
+
* `sourceOrigin`, not its identity.
|
|
5524
5593
|
*
|
|
5525
5594
|
* `pin` is the pin entity at the hop where the answer was found, for a surface that names the
|
|
5526
|
-
* origin the way it names any source.
|
|
5595
|
+
* origin the way it names any source. `key` is its origin fields joined, for a surface with no
|
|
5596
|
+
* name to show.
|
|
5527
5597
|
*/
|
|
5528
5598
|
originOf({ kind, path }) {
|
|
5529
5599
|
const held = this.resolve(kind, path);
|
|
5530
5600
|
if (!held) return void 0;
|
|
5601
|
+
const stored = this.storedBody(kind, path);
|
|
5602
|
+
if (isExtendedRef(stored) && Object.keys(stored.extend).length > 0) return;
|
|
5531
5603
|
let through = held.borrowedFrom;
|
|
5532
5604
|
if (through === void 0 && held.copiedThrough !== void 0) {
|
|
5533
5605
|
if (this.copyDiffers({
|
|
@@ -5543,11 +5615,12 @@ var Config = class Config {
|
|
|
5543
5615
|
});
|
|
5544
5616
|
if (upstream) return upstream;
|
|
5545
5617
|
for (const sourceKind of this.sourceKinds()) {
|
|
5546
|
-
const
|
|
5618
|
+
const fields = this.sourceFields(sourceKind, through, "sourceOrigin");
|
|
5547
5619
|
const pin = this.resolve(sourceKind, through);
|
|
5548
|
-
if (
|
|
5620
|
+
if (fields === void 0 || !pin) continue;
|
|
5549
5621
|
return {
|
|
5550
|
-
key,
|
|
5622
|
+
key: fields.join("\0"),
|
|
5623
|
+
fields,
|
|
5551
5624
|
pin
|
|
5552
5625
|
};
|
|
5553
5626
|
}
|
|
@@ -6325,6 +6398,21 @@ function extendBody(base, delta) {
|
|
|
6325
6398
|
/** The paths each config is reading through a re-pointed link, for {@link Config.laidLinkedBody} to
|
|
6326
6399
|
* find a loop by. Keyed on the config's state, which every read of one config shares. */
|
|
6327
6400
|
const redirecting = /* @__PURE__ */ new WeakMap();
|
|
6401
|
+
/** Whether two {@link Config.originOf} answers are one origin, by {@link sameOriginFields}. */
|
|
6402
|
+
function sameOrigin(a, b) {
|
|
6403
|
+
return sameOriginFields(a.fields, b.fields);
|
|
6404
|
+
}
|
|
6405
|
+
/**
|
|
6406
|
+
* Whether two pins' `sourceOrigin` fields name one system: they agree on every field both set, and
|
|
6407
|
+
* share at least one. Not the whole key: a pin by path is stamped with its sibling's id when it's
|
|
6408
|
+
* pushed, so the Bridge's `yos` reads `configPath` and `systemId` where a pin to the same system by id
|
|
6409
|
+
* names only the id — and a `configPath` is relative to the config holding it, so it can't be compared
|
|
6410
|
+
* across two.
|
|
6411
|
+
*/
|
|
6412
|
+
function sameOriginFields(theirs, mine) {
|
|
6413
|
+
const shared = theirs.flatMap((value, field) => value !== "" && mine[field] ? [field] : []);
|
|
6414
|
+
return shared.length > 0 && shared.every((field) => theirs[field] === mine[field]);
|
|
6415
|
+
}
|
|
6328
6416
|
/**
|
|
6329
6417
|
* The entity an `extend` re-points its path to, and the rest of it: a link's `override` written as a
|
|
6330
6418
|
* ref lands in the stored `extend` as its `__ref`, beside whatever the `extend` verb adds.
|
|
@@ -6539,4 +6627,4 @@ function loadGroupChange(meta, kind, path, raw, phase = "author") {
|
|
|
6539
6627
|
};
|
|
6540
6628
|
}
|
|
6541
6629
|
//#endregion
|
|
6542
|
-
export { Config, SERIALIZED_CONFIG_VERSION };
|
|
6630
|
+
export { Config, SERIALIZED_CONFIG_VERSION, sameOrigin };
|
|
@@ -239,12 +239,17 @@ interface DanglingDirective {
|
|
|
239
239
|
/** The accessor as written — this config may have no kind under it. */
|
|
240
240
|
accessor: string;
|
|
241
241
|
path: string;
|
|
242
|
-
/**
|
|
243
|
-
|
|
242
|
+
/**
|
|
243
|
+
* `accessor` when no kind is registered under that name, `path` when the source has no such entity,
|
|
244
|
+
* `row` when the source has it but the row here isn't one the pin derives.
|
|
245
|
+
*/
|
|
246
|
+
missing: 'accessor' | 'path' | 'row';
|
|
247
|
+
/** With `row`: the pin the row there follows instead, absent when it is this config's own. */
|
|
248
|
+
heldBy?: string;
|
|
244
249
|
}
|
|
245
250
|
/**
|
|
246
|
-
* Every directive aimed at nothing — an accessor this config has no kind for,
|
|
247
|
-
* does not have.
|
|
251
|
+
* Every directive aimed at nothing — an accessor this config has no kind for, a path the source
|
|
252
|
+
* does not have, or a path where the row here isn't one the pin derives.
|
|
248
253
|
*
|
|
249
254
|
* The one mistake the other checks structurally cannot see. Each of them answers for a path it is
|
|
250
255
|
* already holding, so it asks "does the declaration mention this one" — and a name nobody has is
|
|
@@ -254,6 +259,10 @@ interface DanglingDirective {
|
|
|
254
259
|
* Also what notices the source dropping a path. A directive written against an entity a later version
|
|
255
260
|
* removed keeps parsing and keeps doing nothing, which is the same failure the declared verb exists to
|
|
256
261
|
* catch one level down.
|
|
262
|
+
*
|
|
263
|
+
* And what notices a path leaving the pin. A row taken through another pin (`linked-system/link` with
|
|
264
|
+
* `onLinked: 'take'`), or authored here, stands where the pin's row stood, and the entry the pin
|
|
265
|
+
* still carries for it applies to nothing.
|
|
257
266
|
*/
|
|
258
267
|
declare function danglingDirectives(config: Config): DanglingDirective[];
|
|
259
268
|
/** A borrowed token whose condition is missing or has a different category here. */
|
|
@@ -489,8 +489,25 @@ function unadoptedDirectives(config) {
|
|
|
489
489
|
return found;
|
|
490
490
|
}
|
|
491
491
|
/**
|
|
492
|
-
*
|
|
493
|
-
*
|
|
492
|
+
* Who holds the row at a path a directive names, when it isn't the directive's own pin: the slug of
|
|
493
|
+
* the pin the row follows instead, `null` for a row of this config's own, `undefined` when the pin's
|
|
494
|
+
* row is there or nothing is.
|
|
495
|
+
*
|
|
496
|
+
* A pin's `options` lay over the rows its declaration derives, and nothing else. A row taken through
|
|
497
|
+
* another pin, or authored here, stands at the path in its place, so the entry applies to nothing.
|
|
498
|
+
*/
|
|
499
|
+
function heldElsewhere(config, { slug, kind, path }) {
|
|
500
|
+
const item = config.storedBody(kind, path);
|
|
501
|
+
const group = item === void 0 ? config.groupMeta(kind, path) : void 0;
|
|
502
|
+
if (item === void 0 && group === void 0) return void 0;
|
|
503
|
+
const pointer = item ?? group?.ref;
|
|
504
|
+
const through = isRef(pointer) ? sourceOf(pointer) : void 0;
|
|
505
|
+
if (through === slug) return void 0;
|
|
506
|
+
return through ?? null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Every directive aimed at nothing — an accessor this config has no kind for, a path the source
|
|
510
|
+
* does not have, or a path where the row here isn't one the pin derives.
|
|
494
511
|
*
|
|
495
512
|
* The one mistake the other checks structurally cannot see. Each of them answers for a path it is
|
|
496
513
|
* already holding, so it asks "does the declaration mention this one" — and a name nobody has is
|
|
@@ -500,6 +517,10 @@ function unadoptedDirectives(config) {
|
|
|
500
517
|
* Also what notices the source dropping a path. A directive written against an entity a later version
|
|
501
518
|
* removed keeps parsing and keeps doing nothing, which is the same failure the declared verb exists to
|
|
502
519
|
* catch one level down.
|
|
520
|
+
*
|
|
521
|
+
* And what notices a path leaving the pin. A row taken through another pin (`linked-system/link` with
|
|
522
|
+
* `onLinked: 'take'`), or authored here, stands where the pin's row stood, and the entry the pin
|
|
523
|
+
* still carries for it applies to nothing.
|
|
503
524
|
*/
|
|
504
525
|
function danglingDirectives(config) {
|
|
505
526
|
const found = [];
|
|
@@ -516,7 +537,23 @@ function danglingDirectives(config) {
|
|
|
516
537
|
});
|
|
517
538
|
continue;
|
|
518
539
|
}
|
|
519
|
-
if (source.resolve(kind, path) || source.members(kind, path).length > 0)
|
|
540
|
+
if (source.resolve(kind, path) || source.members(kind, path).length > 0) {
|
|
541
|
+
if (config.sourceOverlay(slug, kind, path)?.hidden === true) continue;
|
|
542
|
+
const heldBy = heldElsewhere(config, {
|
|
543
|
+
slug,
|
|
544
|
+
kind,
|
|
545
|
+
path
|
|
546
|
+
});
|
|
547
|
+
if (heldBy === void 0) continue;
|
|
548
|
+
found.push({
|
|
549
|
+
slug,
|
|
550
|
+
accessor,
|
|
551
|
+
path,
|
|
552
|
+
missing: "row",
|
|
553
|
+
...heldBy === null ? {} : { heldBy }
|
|
554
|
+
});
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
520
557
|
found.push({
|
|
521
558
|
slug,
|
|
522
559
|
accessor,
|