@variance-authority/core 0.13.0 → 0.15.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/CHANGELOG.md +458 -0
- package/README.md +13 -10
- package/dist/attribute/component-hash.d.ts +3 -2
- package/dist/attribute/component-hash.js +3 -2
- package/dist/attribute/component-hash.js.map +1 -1
- package/dist/attribute/composition.d.ts +16 -0
- package/dist/attribute/composition.js +4 -6
- package/dist/attribute/composition.js.map +1 -1
- package/dist/attribute/index.d.ts +3 -1
- package/dist/attribute/index.js +2 -1
- package/dist/attribute/index.js.map +1 -1
- package/dist/attribute/region.js +1 -5
- package/dist/attribute/region.js.map +1 -1
- package/dist/attribute/source.d.ts +10 -24
- package/dist/attribute/source.js +4 -38
- package/dist/attribute/source.js.map +1 -1
- package/dist/attribute/subject-pieces.d.ts +37 -0
- package/dist/attribute/subject-pieces.js +122 -0
- package/dist/attribute/subject-pieces.js.map +1 -0
- package/dist/format/capture.d.ts +25 -0
- package/dist/format/capture.js +6 -0
- package/dist/format/capture.js.map +1 -1
- package/dist/format/document.d.ts +2 -2
- package/dist/format/document.js +8 -2
- package/dist/format/document.js.map +1 -1
- package/dist/format/drive.d.ts +191 -0
- package/dist/format/drive.js +343 -0
- package/dist/format/drive.js.map +1 -0
- package/dist/format/environment.d.ts +20 -2
- package/dist/format/environment.js +7 -0
- package/dist/format/environment.js.map +1 -1
- package/dist/format/index.d.ts +6 -3
- package/dist/format/index.js +4 -2
- package/dist/format/index.js.map +1 -1
- package/dist/format/intervention.d.ts +79 -0
- package/dist/format/intervention.js.map +1 -1
- package/dist/format/origin.d.ts +32 -0
- package/dist/format/origin.js +54 -0
- package/dist/format/origin.js.map +1 -0
- package/dist/format/snapshot.d.ts +2 -0
- package/dist/format/snapshot.js.map +1 -1
- package/dist/format/stabilize.d.ts +28 -5
- package/dist/format/stabilize.js +48 -100
- package/dist/format/stabilize.js.map +1 -1
- package/dist/format/waits.d.ts +12 -0
- package/dist/format/waits.js +156 -0
- package/dist/format/waits.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/nearest.d.ts +46 -0
- package/dist/nearest.js +102 -0
- package/dist/nearest.js.map +1 -0
- package/dist/relate/before.d.ts +19 -24
- package/dist/relate/before.js +35 -37
- package/dist/relate/before.js.map +1 -1
- package/dist/relate/beyond.d.ts +59 -0
- package/dist/relate/beyond.js +98 -0
- package/dist/relate/beyond.js.map +1 -0
- package/dist/relate/carried.d.ts +122 -0
- package/dist/relate/carried.js +279 -0
- package/dist/relate/carried.js.map +1 -0
- package/dist/relate/dominate.d.ts +19 -0
- package/dist/relate/dominate.js +77 -0
- package/dist/relate/dominate.js.map +1 -0
- package/dist/relate/index.d.ts +3 -0
- package/dist/relate/index.js +3 -0
- package/dist/relate/index.js.map +1 -1
- package/dist/relate/merkle.js +2 -11
- package/dist/relate/merkle.js.map +1 -1
- package/dist/relate/narrow.d.ts +2 -2
- package/dist/relate/narrow.js +26 -73
- package/dist/relate/narrow.js.map +1 -1
- package/dist/relate/reach.d.ts +1 -1
- package/dist/relate/reach.js +10 -53
- package/dist/relate/reach.js.map +1 -1
- package/dist/relate/records.d.ts +1 -1
- package/dist/relate/records.js.map +1 -1
- package/dist/rules/index.d.ts +3 -3
- package/dist/rules/index.js +3 -3
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/normalize/cascade.d.ts +12 -0
- package/dist/rules/normalize/cascade.js +34 -16
- package/dist/rules/normalize/cascade.js.map +1 -1
- package/dist/rules/normalize/fixture.d.ts +1 -0
- package/dist/rules/normalize/fixture.js +1 -0
- package/dist/rules/normalize/fixture.js.map +1 -1
- package/dist/rules/normalize/index.d.ts +2 -2
- package/dist/rules/normalize/index.js +9 -2
- package/dist/rules/normalize/index.js.map +1 -1
- package/dist/rules/normalize/value.d.ts +13 -2
- package/dist/rules/normalize/value.js +42 -8
- package/dist/rules/normalize/value.js.map +1 -1
- package/dist/rules/normalize/wrapper.js +5 -0
- package/dist/rules/normalize/wrapper.js.map +1 -1
- package/dist/rules/ruleset.d.ts +24 -4
- package/dist/rules/ruleset.js +24 -4
- package/dist/rules/ruleset.js.map +1 -1
- package/dist/segment/index.d.ts +20 -4
- package/dist/segment/index.js +28 -7
- package/dist/segment/index.js.map +1 -1
- package/dist/shard/index.d.ts +11 -0
- package/dist/shard/index.js +11 -0
- package/dist/shard/index.js.map +1 -0
- package/dist/shard/placement.d.ts +117 -0
- package/dist/shard/placement.js +170 -0
- package/dist/shard/placement.js.map +1 -0
- package/package.json +8 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one walk against the arrows: what a change carries from the files it
|
|
3
|
+
* started in to every file that loads one of them, and on.
|
|
4
|
+
*
|
|
5
|
+
* Every question about what a change reached is this walk with a different
|
|
6
|
+
* step. A plain closure carries the whole file everywhere. A narrowed one
|
|
7
|
+
* carries the exports that moved, and an importer that binds none of them is
|
|
8
|
+
* not entered. Selection carries names to a reader and stops behind the
|
|
9
|
+
* importer the record answers. The walk is the same each time — a worklist of
|
|
10
|
+
* facts `(file, name)`, each fact entered once — so it is written once, here,
|
|
11
|
+
* and each caller says only what one importer does with what reached it.
|
|
12
|
+
* Handed the forward adjacency instead, the same walk runs along the arrows,
|
|
13
|
+
* which is how `dependenciesOf` closes over what a file loads. One plain
|
|
14
|
+
* closure is written again: `Graph::search` in sense's native journey graph,
|
|
15
|
+
* because Rust cannot call this.
|
|
16
|
+
*
|
|
17
|
+
* ## What is carried
|
|
18
|
+
*
|
|
19
|
+
* A {@link Carry} is either {@link WHOLE} — the file moved as it loads, or
|
|
20
|
+
* nobody can say which of its names did — or a map from the names that moved
|
|
21
|
+
* to whatever the caller carries with each. An empty map is nothing, and
|
|
22
|
+
* nothing arrives nowhere: the importer is not entered.
|
|
23
|
+
*
|
|
24
|
+
* ## The kernel owns
|
|
25
|
+
*
|
|
26
|
+
* - **the worklist** — first in, first out, so a node is first entered from a
|
|
27
|
+
* node on a shortest path and the trail through `via` is a shortest one;
|
|
28
|
+
* - **what was entered** — the names each node holds only grow, a name is
|
|
29
|
+
* handed to a node once, and a node that holds the whole file takes nothing
|
|
30
|
+
* more. A name is held as a string, whichever file it arrived from: a caller
|
|
31
|
+
* that needs two files' `default` apart names them apart. The walk ends,
|
|
32
|
+
* whatever the steps return, after at most one entry per node and name, and
|
|
33
|
+
* one more when a node turns whole;
|
|
34
|
+
* - **cycles** — a cycle, a self-import included, is a revisit like any other;
|
|
35
|
+
* - **the step that cannot answer** — `undefined` from a step is
|
|
36
|
+
* {@link WHOLE}. A narrower walk has to be earned by an answer, so an
|
|
37
|
+
* unknown can only cost a wider one.
|
|
38
|
+
*/
|
|
39
|
+
import { type Adjacency, type EdgeKind, type NodeId } from './graph.js';
|
|
40
|
+
/** The whole file: every name it exposes, and what it does as it loads. */
|
|
41
|
+
export declare const WHOLE: unique symbol;
|
|
42
|
+
export type Whole = typeof WHOLE;
|
|
43
|
+
/** What reaches a node: the whole file, or the names that moved, each with the caller's payload. */
|
|
44
|
+
export type Carry<T> = ReadonlyMap<string, T> | Whole;
|
|
45
|
+
/** Nothing: the importer is not entered, and nothing is handed on from it. */
|
|
46
|
+
export declare const NOTHING: Carry<never>;
|
|
47
|
+
/** What a node holds so far: the whole file, or the names handed to it. Live — read it, never keep it. */
|
|
48
|
+
export type Held = ReadonlySet<string> | Whole;
|
|
49
|
+
/**
|
|
50
|
+
* What one importer does with what reached it. A step left out passes what it
|
|
51
|
+
* was given on unchanged.
|
|
52
|
+
*/
|
|
53
|
+
export interface CarriedStep<T> {
|
|
54
|
+
/**
|
|
55
|
+
* What crosses one edge: `carried` left `from`, and `importer` loads `from`
|
|
56
|
+
* through an edge of `kind` (an index into `EDGE_KINDS`). `held` is
|
|
57
|
+
* everything `from` holds when it is expanded, which `carried` is part of:
|
|
58
|
+
* the whole file when `carried` is, and a seed's starting names even when
|
|
59
|
+
* `seedsHold` is `false`.
|
|
60
|
+
* {@link NOTHING} keeps the edge from entering `importer`; `undefined` is
|
|
61
|
+
* the whole file. Not asked of an edge into a node that already holds the
|
|
62
|
+
* whole file, since nothing it returns could add to it.
|
|
63
|
+
*/
|
|
64
|
+
arrive?(importer: NodeId, from: NodeId, kind: number, carried: Carry<T>, held: Held): Carry<T> | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* What `importer` hands on, given what arrived that it did not already hold
|
|
67
|
+
* — never empty. Asked once per new arrival, as the walk expands it, in the
|
|
68
|
+
* order the walk reached them, and never of a seed as it starts. Every
|
|
69
|
+
* arrival is asked, so a step that answers {@link NOTHING} for one importer
|
|
70
|
+
* still sees each arrival that reached it. {@link NOTHING} stops the walk
|
|
71
|
+
* behind it; `undefined` hands on the whole file.
|
|
72
|
+
*/
|
|
73
|
+
hand?(importer: NodeId, arrived: Carry<T>): Carry<T> | undefined;
|
|
74
|
+
}
|
|
75
|
+
export interface CarriedOptions {
|
|
76
|
+
/** Edge kinds to walk. `RUNTIME_EDGES` when absent: every kind but `type`. */
|
|
77
|
+
readonly through?: Iterable<EdgeKind>;
|
|
78
|
+
/** Nodes the walk never enters, seeds included. */
|
|
79
|
+
readonly avoid?: Iterable<NodeId>;
|
|
80
|
+
/**
|
|
81
|
+
* Marks to write into instead of a fresh array; `1` where a node was entered.
|
|
82
|
+
* A node already marked when the walk starts counts as holding the whole
|
|
83
|
+
* file, so it is neither entered nor handed anything.
|
|
84
|
+
*/
|
|
85
|
+
readonly mask?: Uint8Array;
|
|
86
|
+
/**
|
|
87
|
+
* Where to record the node each one was first entered from. Not recorded
|
|
88
|
+
* when absent. A seed's entry is left as it was, `-1` by convention.
|
|
89
|
+
*/
|
|
90
|
+
readonly via?: Int32Array;
|
|
91
|
+
/**
|
|
92
|
+
* Whether a seed holds what it starts with. When `false` it holds nothing,
|
|
93
|
+
* so what a cycle carries back to it is new, and handed to it: a file can
|
|
94
|
+
* read its own names back through an importer.
|
|
95
|
+
*/
|
|
96
|
+
readonly seedsHold?: boolean;
|
|
97
|
+
}
|
|
98
|
+
export interface Reached {
|
|
99
|
+
/** `1` where a node was entered, including the seeds. */
|
|
100
|
+
readonly mask: Uint8Array;
|
|
101
|
+
/** The entered nodes, in the order they were first entered. */
|
|
102
|
+
readonly entered: readonly NodeId[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Walk `edges` from `seeds`, each with what its change carries, and hand each
|
|
106
|
+
* node it enters what reaches it through `step`. `edges` is the reverse
|
|
107
|
+
* adjacency for what a change reaches, the forward one for what a file loads.
|
|
108
|
+
* A seed listed more than once starts with everything its listings carry.
|
|
109
|
+
*/
|
|
110
|
+
export declare function carried<T>(edges: Adjacency, seeds: Iterable<readonly [NodeId, Carry<T>]>, step?: CarriedStep<T>, options?: CarriedOptions): Reached;
|
|
111
|
+
/**
|
|
112
|
+
* The marked nodes in id order. The walk's own order depends on seed order;
|
|
113
|
+
* this one does not, so a report built from it is byte-stable whatever order
|
|
114
|
+
* the caller listed its changed files in.
|
|
115
|
+
*/
|
|
116
|
+
export declare function inIdOrder(mask: Uint8Array): NodeId[];
|
|
117
|
+
/**
|
|
118
|
+
* A kind filter as a byte lookup, so the inner loop tests a byte; `fallback`
|
|
119
|
+
* when no kinds are named.
|
|
120
|
+
*/
|
|
121
|
+
export declare function allowedKinds(through: Iterable<EdgeKind> | undefined, fallback?: readonly EdgeKind[]): Uint8Array;
|
|
122
|
+
//# sourceMappingURL=carried.d.ts.map
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// compass: variance-authority.reach.relations
|
|
2
|
+
/**
|
|
3
|
+
* The one walk against the arrows: what a change carries from the files it
|
|
4
|
+
* started in to every file that loads one of them, and on.
|
|
5
|
+
*
|
|
6
|
+
* Every question about what a change reached is this walk with a different
|
|
7
|
+
* step. A plain closure carries the whole file everywhere. A narrowed one
|
|
8
|
+
* carries the exports that moved, and an importer that binds none of them is
|
|
9
|
+
* not entered. Selection carries names to a reader and stops behind the
|
|
10
|
+
* importer the record answers. The walk is the same each time — a worklist of
|
|
11
|
+
* facts `(file, name)`, each fact entered once — so it is written once, here,
|
|
12
|
+
* and each caller says only what one importer does with what reached it.
|
|
13
|
+
* Handed the forward adjacency instead, the same walk runs along the arrows,
|
|
14
|
+
* which is how `dependenciesOf` closes over what a file loads. One plain
|
|
15
|
+
* closure is written again: `Graph::search` in sense's native journey graph,
|
|
16
|
+
* because Rust cannot call this.
|
|
17
|
+
*
|
|
18
|
+
* ## What is carried
|
|
19
|
+
*
|
|
20
|
+
* A {@link Carry} is either {@link WHOLE} — the file moved as it loads, or
|
|
21
|
+
* nobody can say which of its names did — or a map from the names that moved
|
|
22
|
+
* to whatever the caller carries with each. An empty map is nothing, and
|
|
23
|
+
* nothing arrives nowhere: the importer is not entered.
|
|
24
|
+
*
|
|
25
|
+
* ## The kernel owns
|
|
26
|
+
*
|
|
27
|
+
* - **the worklist** — first in, first out, so a node is first entered from a
|
|
28
|
+
* node on a shortest path and the trail through `via` is a shortest one;
|
|
29
|
+
* - **what was entered** — the names each node holds only grow, a name is
|
|
30
|
+
* handed to a node once, and a node that holds the whole file takes nothing
|
|
31
|
+
* more. A name is held as a string, whichever file it arrived from: a caller
|
|
32
|
+
* that needs two files' `default` apart names them apart. The walk ends,
|
|
33
|
+
* whatever the steps return, after at most one entry per node and name, and
|
|
34
|
+
* one more when a node turns whole;
|
|
35
|
+
* - **cycles** — a cycle, a self-import included, is a revisit like any other;
|
|
36
|
+
* - **the step that cannot answer** — `undefined` from a step is
|
|
37
|
+
* {@link WHOLE}. A narrower walk has to be earned by an answer, so an
|
|
38
|
+
* unknown can only cost a wider one.
|
|
39
|
+
*/
|
|
40
|
+
import { EDGE_KINDS, RUNTIME_EDGES } from './graph.js';
|
|
41
|
+
/** The whole file: every name it exposes, and what it does as it loads. */
|
|
42
|
+
export const WHOLE = Symbol('whole');
|
|
43
|
+
/** Nothing: the importer is not entered, and nothing is handed on from it. */
|
|
44
|
+
export const NOTHING = new Map();
|
|
45
|
+
/**
|
|
46
|
+
* Walk `edges` from `seeds`, each with what its change carries, and hand each
|
|
47
|
+
* node it enters what reaches it through `step`. `edges` is the reverse
|
|
48
|
+
* adjacency for what a change reaches, the forward one for what a file loads.
|
|
49
|
+
* A seed listed more than once starts with everything its listings carry.
|
|
50
|
+
*/
|
|
51
|
+
export function carried(edges, seeds, step = {}, options = {}) {
|
|
52
|
+
const { offset, target, kind } = edges;
|
|
53
|
+
const nodes = offset.length - 1;
|
|
54
|
+
const mask = options.mask ?? new Uint8Array(nodes);
|
|
55
|
+
const { via } = options;
|
|
56
|
+
const allowed = options.through === undefined ? RUNTIME : allowedKinds(options.through);
|
|
57
|
+
let avoided;
|
|
58
|
+
if (options.avoid !== undefined) {
|
|
59
|
+
// A typed array ignores a write out of its range, so an id the graph does
|
|
60
|
+
// not hold avoids nothing.
|
|
61
|
+
for (const id of options.avoid) {
|
|
62
|
+
avoided ??= new Uint8Array(nodes);
|
|
63
|
+
avoided[id] = 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// An entered node absent here holds the whole file.
|
|
67
|
+
let partial;
|
|
68
|
+
// The worklist, one number an entry: the node itself when it carries the
|
|
69
|
+
// whole file, `~k` when it carries names, held at `k` aside. Each entry is
|
|
70
|
+
// what arrived, and is handed on as it is expanded: the same entries in the
|
|
71
|
+
// same order as asking on arrival, with the edge loop left free of the step.
|
|
72
|
+
// While every entry is a node's first, the worklist is the list of entered
|
|
73
|
+
// nodes too, so a walk pushes one number a node and nothing else. The first
|
|
74
|
+
// entry that is not a node's first sets `entered` apart, and from then on it
|
|
75
|
+
// is kept on its own.
|
|
76
|
+
const queued = [];
|
|
77
|
+
let aside;
|
|
78
|
+
let entered;
|
|
79
|
+
const { arrive, hand } = step;
|
|
80
|
+
const holdsNothing = options.seedsHold === false;
|
|
81
|
+
for (const [seed, carry] of seeds) {
|
|
82
|
+
if (seed < 0 || seed >= nodes || avoided?.[seed] === 1)
|
|
83
|
+
continue;
|
|
84
|
+
let start = carry;
|
|
85
|
+
if (mask[seed] !== 1) {
|
|
86
|
+
mask[seed] = 1;
|
|
87
|
+
entered?.push(seed);
|
|
88
|
+
if (holdsNothing)
|
|
89
|
+
(partial ??= new Map()).set(seed, new Set());
|
|
90
|
+
else if (carry !== WHOLE)
|
|
91
|
+
(partial ??= new Map()).set(seed, namesOf(carry));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Marked before the walk, which holds the whole file, or listed again.
|
|
95
|
+
const holds = partial?.get(seed);
|
|
96
|
+
if (holds === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
// A seed that holds nothing of what it starts with starts every listing.
|
|
99
|
+
if (!holdsNothing) {
|
|
100
|
+
if (carry === WHOLE)
|
|
101
|
+
partial.delete(seed);
|
|
102
|
+
else
|
|
103
|
+
start = addedTo(holds, carry);
|
|
104
|
+
}
|
|
105
|
+
if (start !== WHOLE && start.size === 0)
|
|
106
|
+
continue;
|
|
107
|
+
entered ??= nodesOf(queued, aside, []);
|
|
108
|
+
}
|
|
109
|
+
queued.push(start === WHOLE ? seed : named((aside ??= { nodes: [], carries: [] }), seed, start));
|
|
110
|
+
}
|
|
111
|
+
const starts = queued.length;
|
|
112
|
+
// With no step on the edge and every seed whole, nothing is held by name
|
|
113
|
+
// until a hand answers with names, and the walk runs in a loop of its own
|
|
114
|
+
// until then: a closure and a cut walk are this loop, unslowed by the walks
|
|
115
|
+
// that narrow.
|
|
116
|
+
let from = 0;
|
|
117
|
+
let handed;
|
|
118
|
+
if (arrive === undefined && partial === undefined && aside === undefined) {
|
|
119
|
+
const stop = wholeFrom(edges, queued, starts, step, mask, via, allowed, avoided);
|
|
120
|
+
if (stop === undefined)
|
|
121
|
+
return { mask, entered: queued };
|
|
122
|
+
({ head: from, carry: handed } = stop);
|
|
123
|
+
}
|
|
124
|
+
for (let head = from; head < queued.length; head += 1) {
|
|
125
|
+
const entry = queued[head];
|
|
126
|
+
const node = entry >= 0 ? entry : aside.nodes[~entry];
|
|
127
|
+
let carry = entry >= 0 ? WHOLE : aside.carries[~entry];
|
|
128
|
+
if (handed !== undefined && head === from)
|
|
129
|
+
carry = handed;
|
|
130
|
+
else if (hand !== undefined && head >= starts)
|
|
131
|
+
carry = step.hand(node, carry) ?? WHOLE;
|
|
132
|
+
if (carry !== WHOLE && carry.size === 0)
|
|
133
|
+
continue;
|
|
134
|
+
// What it holds, which what it carries is part of: a seed that holds
|
|
135
|
+
// nothing of what it starts with still starts with it.
|
|
136
|
+
const holds = carry === WHOLE ? undefined : partial?.get(node);
|
|
137
|
+
let held = holds ?? WHOLE;
|
|
138
|
+
if (holdsNothing && head < starts && holds !== undefined && carry !== WHOLE) {
|
|
139
|
+
const both = namesOf(carry);
|
|
140
|
+
for (const name of holds)
|
|
141
|
+
both.add(name);
|
|
142
|
+
held = both;
|
|
143
|
+
}
|
|
144
|
+
const end = offset[node + 1];
|
|
145
|
+
for (let edge = offset[node]; edge < end; edge += 1) {
|
|
146
|
+
const edgeKind = kind[edge];
|
|
147
|
+
if (allowed[edgeKind] !== 1)
|
|
148
|
+
continue;
|
|
149
|
+
const next = target[edge];
|
|
150
|
+
if (avoided?.[next] === 1)
|
|
151
|
+
continue;
|
|
152
|
+
// Entered and absent from `partial`: it holds the whole file already.
|
|
153
|
+
const seen = mask[next] === 1;
|
|
154
|
+
if (seen && (partial === undefined || !partial.has(next)))
|
|
155
|
+
continue;
|
|
156
|
+
const out = arrive === undefined ? carry : (step.arrive(next, node, edgeKind, carry, held) ?? WHOLE);
|
|
157
|
+
if (out !== WHOLE && out.size === 0)
|
|
158
|
+
continue;
|
|
159
|
+
let arrived = out;
|
|
160
|
+
if (!seen) {
|
|
161
|
+
mask[next] = 1;
|
|
162
|
+
if (via !== undefined)
|
|
163
|
+
via[next] = node;
|
|
164
|
+
entered?.push(next);
|
|
165
|
+
if (out !== WHOLE)
|
|
166
|
+
(partial ??= new Map()).set(next, namesOf(out));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
if (out === WHOLE)
|
|
170
|
+
partial.delete(next);
|
|
171
|
+
else {
|
|
172
|
+
arrived = addedTo(partial.get(next), out);
|
|
173
|
+
if (arrived.size === 0)
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
entered ??= nodesOf(queued, aside, []);
|
|
177
|
+
}
|
|
178
|
+
queued.push(arrived === WHOLE ? next : named((aside ??= { nodes: [], carries: [] }), next, arrived));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Nothing carried by name, nothing to decode: every entry is a node.
|
|
182
|
+
return { mask, entered: entered ?? (aside === undefined ? queued : nodesOf(queued, aside, queued)) };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The walk while every entry carries the whole file: an entered node takes
|
|
186
|
+
* nothing more, so the mask is the whole record of what was entered. Returns
|
|
187
|
+
* where a hand first answered with names, or `undefined` when the walk ended
|
|
188
|
+
* whole.
|
|
189
|
+
*/
|
|
190
|
+
function wholeFrom(edges, queued, starts, step, mask, via, allowed, avoided) {
|
|
191
|
+
const { offset, target, kind } = edges;
|
|
192
|
+
const asks = step.hand !== undefined;
|
|
193
|
+
for (let head = 0; head < queued.length; head += 1) {
|
|
194
|
+
const node = queued[head];
|
|
195
|
+
if (asks && head >= starts) {
|
|
196
|
+
const carry = step.hand(node, WHOLE);
|
|
197
|
+
if (carry !== undefined && carry !== WHOLE) {
|
|
198
|
+
if (carry.size === 0)
|
|
199
|
+
continue;
|
|
200
|
+
return { head, carry };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const end = offset[node + 1];
|
|
204
|
+
for (let edge = offset[node]; edge < end; edge += 1) {
|
|
205
|
+
if (allowed[kind[edge]] !== 1)
|
|
206
|
+
continue;
|
|
207
|
+
const next = target[edge];
|
|
208
|
+
if (mask[next] === 1 || avoided?.[next] === 1)
|
|
209
|
+
continue;
|
|
210
|
+
mask[next] = 1;
|
|
211
|
+
if (via !== undefined)
|
|
212
|
+
via[next] = node;
|
|
213
|
+
queued.push(next);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
/** The worklist entry for `node` carrying the names in `carry`. */
|
|
219
|
+
function named(aside, node, carry) {
|
|
220
|
+
aside.carries.push(carry);
|
|
221
|
+
return ~(aside.nodes.push(node) - 1);
|
|
222
|
+
}
|
|
223
|
+
/** The nodes of `entries`, written into `into`, which may be `entries` itself. */
|
|
224
|
+
function nodesOf(entries, aside, into) {
|
|
225
|
+
for (let at = 0; at < entries.length; at += 1) {
|
|
226
|
+
const entry = entries[at];
|
|
227
|
+
into[at] = entry >= 0 ? entry : aside.nodes[~entry];
|
|
228
|
+
}
|
|
229
|
+
return into;
|
|
230
|
+
}
|
|
231
|
+
/** What `carry` adds to `holds`, which takes it in. */
|
|
232
|
+
function addedTo(holds, carry) {
|
|
233
|
+
const added = new Map();
|
|
234
|
+
for (const [name, payload] of carry) {
|
|
235
|
+
if (holds.has(name))
|
|
236
|
+
continue;
|
|
237
|
+
holds.add(name);
|
|
238
|
+
added.set(name, payload);
|
|
239
|
+
}
|
|
240
|
+
return added;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* The names a carry holds, as a set the walk grows. Added one by one: V8 builds
|
|
244
|
+
* a `Set` from a map's key iterator several times slower than this loop.
|
|
245
|
+
*/
|
|
246
|
+
function namesOf(carry) {
|
|
247
|
+
const names = new Set();
|
|
248
|
+
for (const name of carry.keys())
|
|
249
|
+
names.add(name);
|
|
250
|
+
return names;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The marked nodes in id order. The walk's own order depends on seed order;
|
|
254
|
+
* this one does not, so a report built from it is byte-stable whatever order
|
|
255
|
+
* the caller listed its changed files in.
|
|
256
|
+
*/
|
|
257
|
+
export function inIdOrder(mask) {
|
|
258
|
+
const visited = [];
|
|
259
|
+
for (let id = 0; id < mask.length; id += 1)
|
|
260
|
+
if (mask[id] === 1)
|
|
261
|
+
visited.push(id);
|
|
262
|
+
return visited;
|
|
263
|
+
}
|
|
264
|
+
/** The default filter, built once: selection walks once per changed file. */
|
|
265
|
+
const RUNTIME = allowedKinds(undefined);
|
|
266
|
+
/**
|
|
267
|
+
* A kind filter as a byte lookup, so the inner loop tests a byte; `fallback`
|
|
268
|
+
* when no kinds are named.
|
|
269
|
+
*/
|
|
270
|
+
export function allowedKinds(through, fallback = RUNTIME_EDGES) {
|
|
271
|
+
const allowed = new Uint8Array(EDGE_KINDS.length);
|
|
272
|
+
for (const name of through ?? fallback) {
|
|
273
|
+
const at = EDGE_KINDS.indexOf(name);
|
|
274
|
+
if (at !== -1)
|
|
275
|
+
allowed[at] = 1;
|
|
276
|
+
}
|
|
277
|
+
return allowed;
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=carried.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"carried.js","sourceRoot":"","sources":["../../src/relate/carried.ts"],"names":[],"mappings":"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","sourcesContent":["// compass: variance-authority.reach.relations\n/**\n * The one walk against the arrows: what a change carries from the files it\n * started in to every file that loads one of them, and on.\n *\n * Every question about what a change reached is this walk with a different\n * step. A plain closure carries the whole file everywhere. A narrowed one\n * carries the exports that moved, and an importer that binds none of them is\n * not entered. Selection carries names to a reader and stops behind the\n * importer the record answers. The walk is the same each time — a worklist of\n * facts `(file, name)`, each fact entered once — so it is written once, here,\n * and each caller says only what one importer does with what reached it.\n * Handed the forward adjacency instead, the same walk runs along the arrows,\n * which is how `dependenciesOf` closes over what a file loads. One plain\n * closure is written again: `Graph::search` in sense's native journey graph,\n * because Rust cannot call this.\n *\n * ## What is carried\n *\n * A {@link Carry} is either {@link WHOLE} — the file moved as it loads, or\n * nobody can say which of its names did — or a map from the names that moved\n * to whatever the caller carries with each. An empty map is nothing, and\n * nothing arrives nowhere: the importer is not entered.\n *\n * ## The kernel owns\n *\n * - **the worklist** — first in, first out, so a node is first entered from a\n * node on a shortest path and the trail through `via` is a shortest one;\n * - **what was entered** — the names each node holds only grow, a name is\n * handed to a node once, and a node that holds the whole file takes nothing\n * more. A name is held as a string, whichever file it arrived from: a caller\n * that needs two files' `default` apart names them apart. The walk ends,\n * whatever the steps return, after at most one entry per node and name, and\n * one more when a node turns whole;\n * - **cycles** — a cycle, a self-import included, is a revisit like any other;\n * - **the step that cannot answer** — `undefined` from a step is\n * {@link WHOLE}. A narrower walk has to be earned by an answer, so an\n * unknown can only cost a wider one.\n */\n\nimport { EDGE_KINDS, RUNTIME_EDGES, type Adjacency, type EdgeKind, type NodeId } from './graph.js';\n\n/** The whole file: every name it exposes, and what it does as it loads. */\nexport const WHOLE: unique symbol = Symbol('whole');\nexport type Whole = typeof WHOLE;\n\n/** What reaches a node: the whole file, or the names that moved, each with the caller's payload. */\nexport type Carry<T> = ReadonlyMap<string, T> | Whole;\n\n/** Nothing: the importer is not entered, and nothing is handed on from it. */\nexport const NOTHING: Carry<never> = new Map<string, never>();\n\n/** What a node holds so far: the whole file, or the names handed to it. Live — read it, never keep it. */\nexport type Held = ReadonlySet<string> | Whole;\n\n/**\n * What one importer does with what reached it. A step left out passes what it\n * was given on unchanged.\n */\nexport interface CarriedStep<T> {\n /**\n * What crosses one edge: `carried` left `from`, and `importer` loads `from`\n * through an edge of `kind` (an index into `EDGE_KINDS`). `held` is\n * everything `from` holds when it is expanded, which `carried` is part of:\n * the whole file when `carried` is, and a seed's starting names even when\n * `seedsHold` is `false`.\n * {@link NOTHING} keeps the edge from entering `importer`; `undefined` is\n * the whole file. Not asked of an edge into a node that already holds the\n * whole file, since nothing it returns could add to it.\n */\n arrive?(importer: NodeId, from: NodeId, kind: number, carried: Carry<T>, held: Held): Carry<T> | undefined;\n /**\n * What `importer` hands on, given what arrived that it did not already hold\n * — never empty. Asked once per new arrival, as the walk expands it, in the\n * order the walk reached them, and never of a seed as it starts. Every\n * arrival is asked, so a step that answers {@link NOTHING} for one importer\n * still sees each arrival that reached it. {@link NOTHING} stops the walk\n * behind it; `undefined` hands on the whole file.\n */\n hand?(importer: NodeId, arrived: Carry<T>): Carry<T> | undefined;\n}\n\nexport interface CarriedOptions {\n /** Edge kinds to walk. `RUNTIME_EDGES` when absent: every kind but `type`. */\n readonly through?: Iterable<EdgeKind>;\n /** Nodes the walk never enters, seeds included. */\n readonly avoid?: Iterable<NodeId>;\n /**\n * Marks to write into instead of a fresh array; `1` where a node was entered.\n * A node already marked when the walk starts counts as holding the whole\n * file, so it is neither entered nor handed anything.\n */\n readonly mask?: Uint8Array;\n /**\n * Where to record the node each one was first entered from. Not recorded\n * when absent. A seed's entry is left as it was, `-1` by convention.\n */\n readonly via?: Int32Array;\n /**\n * Whether a seed holds what it starts with. When `false` it holds nothing,\n * so what a cycle carries back to it is new, and handed to it: a file can\n * read its own names back through an importer.\n */\n readonly seedsHold?: boolean;\n}\n\nexport interface Reached {\n /** `1` where a node was entered, including the seeds. */\n readonly mask: Uint8Array;\n /** The entered nodes, in the order they were first entered. */\n readonly entered: readonly NodeId[];\n}\n\n/**\n * Walk `edges` from `seeds`, each with what its change carries, and hand each\n * node it enters what reaches it through `step`. `edges` is the reverse\n * adjacency for what a change reaches, the forward one for what a file loads.\n * A seed listed more than once starts with everything its listings carry.\n */\nexport function carried<T>(\n edges: Adjacency,\n seeds: Iterable<readonly [NodeId, Carry<T>]>,\n step: CarriedStep<T> = {},\n options: CarriedOptions = {},\n): Reached {\n const { offset, target, kind } = edges;\n const nodes = offset.length - 1;\n const mask = options.mask ?? new Uint8Array(nodes);\n const { via } = options;\n const allowed = options.through === undefined ? RUNTIME : allowedKinds(options.through);\n let avoided: Uint8Array | undefined;\n if (options.avoid !== undefined) {\n // A typed array ignores a write out of its range, so an id the graph does\n // not hold avoids nothing.\n for (const id of options.avoid) {\n avoided ??= new Uint8Array(nodes);\n avoided[id] = 1;\n }\n }\n\n // An entered node absent here holds the whole file.\n let partial: Map<NodeId, Set<string>> | undefined;\n // The worklist, one number an entry: the node itself when it carries the\n // whole file, `~k` when it carries names, held at `k` aside. Each entry is\n // what arrived, and is handed on as it is expanded: the same entries in the\n // same order as asking on arrival, with the edge loop left free of the step.\n // While every entry is a node's first, the worklist is the list of entered\n // nodes too, so a walk pushes one number a node and nothing else. The first\n // entry that is not a node's first sets `entered` apart, and from then on it\n // is kept on its own.\n const queued: number[] = [];\n let aside: Aside<T> | undefined;\n let entered: NodeId[] | undefined;\n const { arrive, hand } = step;\n\n const holdsNothing = options.seedsHold === false;\n for (const [seed, carry] of seeds) {\n if (seed < 0 || seed >= nodes || avoided?.[seed] === 1) continue;\n let start: Carry<T> = carry;\n if (mask[seed] !== 1) {\n mask[seed] = 1;\n entered?.push(seed);\n if (holdsNothing) (partial ??= new Map()).set(seed, new Set());\n else if (carry !== WHOLE) (partial ??= new Map()).set(seed, namesOf(carry));\n } else {\n // Marked before the walk, which holds the whole file, or listed again.\n const holds = partial?.get(seed);\n if (holds === undefined) continue;\n // A seed that holds nothing of what it starts with starts every listing.\n if (!holdsNothing) {\n if (carry === WHOLE) partial!.delete(seed);\n else start = addedTo(holds, carry);\n }\n if (start !== WHOLE && start.size === 0) continue;\n entered ??= nodesOf(queued, aside, []);\n }\n queued.push(start === WHOLE ? seed : named((aside ??= { nodes: [], carries: [] }), seed, start));\n }\n const starts = queued.length;\n\n // With no step on the edge and every seed whole, nothing is held by name\n // until a hand answers with names, and the walk runs in a loop of its own\n // until then: a closure and a cut walk are this loop, unslowed by the walks\n // that narrow.\n let from = 0;\n let handed: ReadonlyMap<string, T> | undefined;\n if (arrive === undefined && partial === undefined && aside === undefined) {\n const stop = wholeFrom(edges, queued, starts, step, mask, via, allowed, avoided);\n if (stop === undefined) return { mask, entered: queued };\n ({ head: from, carry: handed } = stop);\n }\n\n for (let head = from; head < queued.length; head += 1) {\n const entry = queued[head]!;\n const node = entry >= 0 ? entry : aside!.nodes[~entry]!;\n let carry: Carry<T> = entry >= 0 ? WHOLE : aside!.carries[~entry]!;\n if (handed !== undefined && head === from) carry = handed;\n else if (hand !== undefined && head >= starts) carry = step.hand!(node, carry) ?? WHOLE;\n if (carry !== WHOLE && carry.size === 0) continue;\n // What it holds, which what it carries is part of: a seed that holds\n // nothing of what it starts with still starts with it.\n const holds = carry === WHOLE ? undefined : partial?.get(node);\n let held: Held = holds ?? WHOLE;\n if (holdsNothing && head < starts && holds !== undefined && carry !== WHOLE) {\n const both = namesOf(carry);\n for (const name of holds) both.add(name);\n held = both;\n }\n const end = offset[node + 1]!;\n\n for (let edge = offset[node]!; edge < end; edge += 1) {\n const edgeKind = kind[edge]!;\n if (allowed[edgeKind] !== 1) continue;\n const next = target[edge]!;\n if (avoided?.[next] === 1) continue;\n // Entered and absent from `partial`: it holds the whole file already.\n const seen = mask[next] === 1;\n if (seen && (partial === undefined || !partial.has(next))) continue;\n\n const out = arrive === undefined ? carry : (step.arrive!(next, node, edgeKind, carry, held) ?? WHOLE);\n if (out !== WHOLE && out.size === 0) continue;\n let arrived = out;\n if (!seen) {\n mask[next] = 1;\n if (via !== undefined) via[next] = node;\n entered?.push(next);\n if (out !== WHOLE) (partial ??= new Map()).set(next, namesOf(out));\n } else {\n if (out === WHOLE) partial!.delete(next);\n else {\n arrived = addedTo(partial!.get(next)!, out);\n if (arrived.size === 0) continue;\n }\n entered ??= nodesOf(queued, aside, []);\n }\n queued.push(arrived === WHOLE ? next : named((aside ??= { nodes: [], carries: [] }), next, arrived));\n }\n }\n\n // Nothing carried by name, nothing to decode: every entry is a node.\n return { mask, entered: entered ?? (aside === undefined ? queued : nodesOf(queued, aside, queued)) };\n}\n\n/** Where a whole walk met a hand that answered with names, and the names. */\ninterface Handed<T> {\n readonly head: number;\n readonly carry: ReadonlyMap<string, T>;\n}\n\n/**\n * The walk while every entry carries the whole file: an entered node takes\n * nothing more, so the mask is the whole record of what was entered. Returns\n * where a hand first answered with names, or `undefined` when the walk ended\n * whole.\n */\nfunction wholeFrom<T>(\n edges: Adjacency,\n queued: NodeId[],\n starts: number,\n step: CarriedStep<T>,\n mask: Uint8Array,\n via: Int32Array | undefined,\n allowed: Uint8Array,\n avoided: Uint8Array | undefined,\n): Handed<T> | undefined {\n const { offset, target, kind } = edges;\n const asks = step.hand !== undefined;\n for (let head = 0; head < queued.length; head += 1) {\n const node = queued[head]!;\n if (asks && head >= starts) {\n const carry = step.hand!(node, WHOLE);\n if (carry !== undefined && carry !== WHOLE) {\n if (carry.size === 0) continue;\n return { head, carry };\n }\n }\n const end = offset[node + 1]!;\n for (let edge = offset[node]!; edge < end; edge += 1) {\n if (allowed[kind[edge]!] !== 1) continue;\n const next = target[edge]!;\n if (mask[next] === 1 || avoided?.[next] === 1) continue;\n mask[next] = 1;\n if (via !== undefined) via[next] = node;\n queued.push(next);\n }\n }\n return undefined;\n}\n\n/** What the worklist carries by name, out of line so a whole entry is one number. */\ninterface Aside<T> {\n readonly nodes: NodeId[];\n readonly carries: Array<ReadonlyMap<string, T>>;\n}\n\n/** The worklist entry for `node` carrying the names in `carry`. */\nfunction named<T>(aside: Aside<T>, node: NodeId, carry: ReadonlyMap<string, T>): number {\n aside.carries.push(carry);\n return ~(aside.nodes.push(node) - 1);\n}\n\n/** The nodes of `entries`, written into `into`, which may be `entries` itself. */\nfunction nodesOf<T>(entries: readonly number[], aside: Aside<T> | undefined, into: NodeId[]): NodeId[] {\n for (let at = 0; at < entries.length; at += 1) {\n const entry = entries[at]!;\n into[at] = entry >= 0 ? entry : aside!.nodes[~entry]!;\n }\n return into;\n}\n\n/** What `carry` adds to `holds`, which takes it in. */\nfunction addedTo<T>(holds: Set<string>, carry: ReadonlyMap<string, T>): ReadonlyMap<string, T> {\n const added = new Map<string, T>();\n for (const [name, payload] of carry) {\n if (holds.has(name)) continue;\n holds.add(name);\n added.set(name, payload);\n }\n return added;\n}\n\n/**\n * The names a carry holds, as a set the walk grows. Added one by one: V8 builds\n * a `Set` from a map's key iterator several times slower than this loop.\n */\nfunction namesOf<T>(carry: ReadonlyMap<string, T>): Set<string> {\n const names = new Set<string>();\n for (const name of carry.keys()) names.add(name);\n return names;\n}\n\n/**\n * The marked nodes in id order. The walk's own order depends on seed order;\n * this one does not, so a report built from it is byte-stable whatever order\n * the caller listed its changed files in.\n */\nexport function inIdOrder(mask: Uint8Array): NodeId[] {\n const visited: NodeId[] = [];\n for (let id = 0; id < mask.length; id += 1) if (mask[id] === 1) visited.push(id);\n return visited;\n}\n\n/** The default filter, built once: selection walks once per changed file. */\nconst RUNTIME = allowedKinds(undefined);\n\n/**\n * A kind filter as a byte lookup, so the inner loop tests a byte; `fallback`\n * when no kinds are named.\n */\nexport function allowedKinds(\n through: Iterable<EdgeKind> | undefined,\n fallback: readonly EdgeKind[] = RUNTIME_EDGES,\n): Uint8Array {\n const allowed = new Uint8Array(EDGE_KINDS.length);\n for (const name of through ?? fallback) {\n const at = EDGE_KINDS.indexOf(name);\n if (at !== -1) allowed[at] = 1;\n }\n return allowed;\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dominance over a graph with one root.
|
|
3
|
+
*
|
|
4
|
+
* D dominates N when every path from the root to N passes through D; N's
|
|
5
|
+
* immediate dominator is the nearest such D, and the immediate dominators form
|
|
6
|
+
* a tree. Cooper, Harvey and Kennedy's iterative algorithm ("A Simple, Fast
|
|
7
|
+
* Dominance Algorithm", 2001): intersect the predecessors' dominators in
|
|
8
|
+
* reverse postorder until nothing changes. It holds on cycles, and on the graphs
|
|
9
|
+
* an import walk produces it settles in two or three passes.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Each node the root reaches, mapped to its immediate dominator. The root and
|
|
13
|
+
* every node it does not reach are absent.
|
|
14
|
+
*
|
|
15
|
+
* `successors` is the graph as the caller sees it: a walk that cuts an edge
|
|
16
|
+
* leaves the edge out here.
|
|
17
|
+
*/
|
|
18
|
+
export declare function dominatorsOf(root: string, successors: (node: string) => Iterable<string>): ReadonlyMap<string, string>;
|
|
19
|
+
//# sourceMappingURL=dominate.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// compass: variance-authority.reach
|
|
2
|
+
/**
|
|
3
|
+
* Dominance over a graph with one root.
|
|
4
|
+
*
|
|
5
|
+
* D dominates N when every path from the root to N passes through D; N's
|
|
6
|
+
* immediate dominator is the nearest such D, and the immediate dominators form
|
|
7
|
+
* a tree. Cooper, Harvey and Kennedy's iterative algorithm ("A Simple, Fast
|
|
8
|
+
* Dominance Algorithm", 2001): intersect the predecessors' dominators in
|
|
9
|
+
* reverse postorder until nothing changes. It holds on cycles, and on the graphs
|
|
10
|
+
* an import walk produces it settles in two or three passes.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Each node the root reaches, mapped to its immediate dominator. The root and
|
|
14
|
+
* every node it does not reach are absent.
|
|
15
|
+
*
|
|
16
|
+
* `successors` is the graph as the caller sees it: a walk that cuts an edge
|
|
17
|
+
* leaves the edge out here.
|
|
18
|
+
*/
|
|
19
|
+
export function dominatorsOf(root, successors) {
|
|
20
|
+
// Postorder by an explicit stack: an import chain is deeper than a call stack likes.
|
|
21
|
+
const order = [];
|
|
22
|
+
const number = new Map();
|
|
23
|
+
const predecessors = new Map();
|
|
24
|
+
const seen = new Set([root]);
|
|
25
|
+
const stack = [{ node: root, next: successors(root)[Symbol.iterator]() }];
|
|
26
|
+
while (stack.length > 0) {
|
|
27
|
+
const top = stack[stack.length - 1];
|
|
28
|
+
const step = top.next.next();
|
|
29
|
+
if (step.done === true) {
|
|
30
|
+
stack.pop();
|
|
31
|
+
number.set(top.node, order.length);
|
|
32
|
+
order.push(top.node);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const child = step.value;
|
|
36
|
+
const into = predecessors.get(child);
|
|
37
|
+
if (into === undefined)
|
|
38
|
+
predecessors.set(child, [top.node]);
|
|
39
|
+
else
|
|
40
|
+
into.push(top.node);
|
|
41
|
+
if (seen.has(child))
|
|
42
|
+
continue;
|
|
43
|
+
seen.add(child);
|
|
44
|
+
stack.push({ node: child, next: successors(child)[Symbol.iterator]() });
|
|
45
|
+
}
|
|
46
|
+
const idom = new Map([[root, root]]);
|
|
47
|
+
const intersect = (left, right) => {
|
|
48
|
+
let a = left;
|
|
49
|
+
let b = right;
|
|
50
|
+
while (a !== b) {
|
|
51
|
+
while (number.get(a) < number.get(b))
|
|
52
|
+
a = idom.get(a);
|
|
53
|
+
while (number.get(b) < number.get(a))
|
|
54
|
+
b = idom.get(b);
|
|
55
|
+
}
|
|
56
|
+
return a;
|
|
57
|
+
};
|
|
58
|
+
for (let changed = true; changed;) {
|
|
59
|
+
changed = false;
|
|
60
|
+
for (let at = order.length - 2; at >= 0; at -= 1) {
|
|
61
|
+
const node = order[at];
|
|
62
|
+
let next;
|
|
63
|
+
for (const from of predecessors.get(node) ?? []) {
|
|
64
|
+
if (!idom.has(from))
|
|
65
|
+
continue;
|
|
66
|
+
next = next === undefined ? from : intersect(from, next);
|
|
67
|
+
}
|
|
68
|
+
if (next !== undefined && idom.get(node) !== next) {
|
|
69
|
+
idom.set(node, next);
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
idom.delete(root);
|
|
75
|
+
return idom;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=dominate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dominate.js","sourceRoot":"","sources":["../../src/relate/dominate.ts"],"names":[],"mappings":"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","sourcesContent":["// compass: variance-authority.reach\n/**\n * Dominance over a graph with one root.\n *\n * D dominates N when every path from the root to N passes through D; N's\n * immediate dominator is the nearest such D, and the immediate dominators form\n * a tree. Cooper, Harvey and Kennedy's iterative algorithm (\"A Simple, Fast\n * Dominance Algorithm\", 2001): intersect the predecessors' dominators in\n * reverse postorder until nothing changes. It holds on cycles, and on the graphs\n * an import walk produces it settles in two or three passes.\n */\n\n/**\n * Each node the root reaches, mapped to its immediate dominator. The root and\n * every node it does not reach are absent.\n *\n * `successors` is the graph as the caller sees it: a walk that cuts an edge\n * leaves the edge out here.\n */\nexport function dominatorsOf(\n root: string,\n successors: (node: string) => Iterable<string>,\n): ReadonlyMap<string, string> {\n // Postorder by an explicit stack: an import chain is deeper than a call stack likes.\n const order: string[] = [];\n const number = new Map<string, number>();\n const predecessors = new Map<string, string[]>();\n const seen = new Set([root]);\n const stack: { node: string; next: Iterator<string> }[] = [{ node: root, next: successors(root)[Symbol.iterator]() }];\n while (stack.length > 0) {\n const top = stack[stack.length - 1]!;\n const step = top.next.next();\n if (step.done === true) {\n stack.pop();\n number.set(top.node, order.length);\n order.push(top.node);\n continue;\n }\n const child = step.value;\n const into = predecessors.get(child);\n if (into === undefined) predecessors.set(child, [top.node]);\n else into.push(top.node);\n if (seen.has(child)) continue;\n seen.add(child);\n stack.push({ node: child, next: successors(child)[Symbol.iterator]() });\n }\n\n const idom = new Map<string, string>([[root, root]]);\n const intersect = (left: string, right: string): string => {\n let a = left;\n let b = right;\n while (a !== b) {\n while (number.get(a)! < number.get(b)!) a = idom.get(a)!;\n while (number.get(b)! < number.get(a)!) b = idom.get(b)!;\n }\n return a;\n };\n for (let changed = true; changed;) {\n changed = false;\n for (let at = order.length - 2; at >= 0; at -= 1) {\n const node = order[at]!;\n let next: string | undefined;\n for (const from of predecessors.get(node) ?? []) {\n if (!idom.has(from)) continue;\n next = next === undefined ? from : intersect(from, next);\n }\n if (next !== undefined && idom.get(node) !== next) {\n idom.set(node, next);\n changed = true;\n }\n }\n }\n idom.delete(root);\n return idom;\n}\n"]}
|
package/dist/relate/index.d.ts
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { EDGE_KINDS, NODE_KINDS, RUNTIME_EDGES, idOf, keyOf, nodeAt, nodesOfKind, relationsOf, type Adjacency, type EdgeKind, type Node, type NodeId, type NodeKind, type Relation, type Relations, } from './graph.js';
|
|
13
13
|
export { beforeReach, changedBefore, within, type BeforeReach, type BeforeReachOptions, } from './before.js';
|
|
14
|
+
export { beyondReach, type BeyondReach, type InstallMoved } from './beyond.js';
|
|
15
|
+
export { NOTHING, WHOLE, carried, type CarriedOptions, type CarriedStep, type Carry, type Held, type Reached, type Whole, } from './carried.js';
|
|
16
|
+
export { dominatorsOf } from './dominate.js';
|
|
14
17
|
export { type EdgeUse, type Uses } from './narrow.js';
|
|
15
18
|
export { dependenciesOf, dependentsOf, trailOf, type Traversal, type TraversalOptions, } from './reach.js';
|
|
16
19
|
export { CLOSURE_EDGES, closureOf, driftedBetween, type Closure, type ClosureInput, type Drift, } from './merkle.js';
|
package/dist/relate/index.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { EDGE_KINDS, NODE_KINDS, RUNTIME_EDGES, idOf, keyOf, nodeAt, nodesOfKind, relationsOf, } from './graph.js';
|
|
13
13
|
export { beforeReach, changedBefore, within, } from './before.js';
|
|
14
|
+
export { beyondReach } from './beyond.js';
|
|
15
|
+
export { NOTHING, WHOLE, carried, } from './carried.js';
|
|
16
|
+
export { dominatorsOf } from './dominate.js';
|
|
14
17
|
export {} from './narrow.js';
|
|
15
18
|
export { dependenciesOf, dependentsOf, trailOf, } from './reach.js';
|
|
16
19
|
export { CLOSURE_EDGES, closureOf, driftedBetween, } from './merkle.js';
|
package/dist/relate/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/relate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,IAAI,EACJ,KAAK,EACL,MAAM,EACN,WAAW,EACX,WAAW,GAQZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,WAAW,EACX,aAAa,EACb,MAAM,GAGP,MAAM,aAAa,CAAC;AAErB,OAAO,EAA2B,MAAM,aAAa,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,YAAY,EACZ,OAAO,GAGR,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,GAIf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,OAAO,EACP,UAAU,EACV,gBAAgB,GAOjB,MAAM,cAAc,CAAC","sourcesContent":["/**\n * `core/relate` — what reaches what.\n *\n * A typed, interned, bidirectional graph and the two traversals over it, plus the\n * file-shaped record the scanner produces and the fold that turns a pile of them\n * into the structure. No I/O, no parser, no resolver: the expensive half belongs\n * to whoever owns the disk, and this is the half that has to be fast and has to\n * be provable without one.\n *\n * See [`docs/selecting.md`](../../../../docs/selecting.md) for what it is for.\n */\n\nexport {\n EDGE_KINDS,\n NODE_KINDS,\n RUNTIME_EDGES,\n idOf,\n keyOf,\n nodeAt,\n nodesOfKind,\n relationsOf,\n type Adjacency,\n type EdgeKind,\n type Node,\n type NodeId,\n type NodeKind,\n type Relation,\n type Relations,\n} from './graph.js';\n\nexport {\n beforeReach,\n changedBefore,\n within,\n type BeforeReach,\n type BeforeReachOptions,\n} from './before.js';\n\nexport { type EdgeUse, type Uses } from './narrow.js';\n\nexport {\n dependenciesOf,\n dependentsOf,\n trailOf,\n type Traversal,\n type TraversalOptions,\n} from './reach.js';\n\nexport {\n CLOSURE_EDGES,\n closureOf,\n driftedBetween,\n type Closure,\n type ClosureInput,\n type Drift,\n} from './merkle.js';\n\nexport {\n explain,\n affectedBy,\n relationsOfFiles,\n type FileEdge,\n type FileRecord,\n type AffectedOptions,\n type PackageEdge,\n type RelationsOptions,\n type Affected,\n} from './records.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/relate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,IAAI,EACJ,KAAK,EACL,MAAM,EACN,WAAW,EACX,WAAW,GAQZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,WAAW,EACX,aAAa,EACb,MAAM,GAGP,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAuC,MAAM,aAAa,CAAC;AAE/E,OAAO,EACL,OAAO,EACP,KAAK,EACL,OAAO,GAOR,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAA2B,MAAM,aAAa,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,YAAY,EACZ,OAAO,GAGR,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,GAIf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,OAAO,EACP,UAAU,EACV,gBAAgB,GAOjB,MAAM,cAAc,CAAC","sourcesContent":["/**\n * `core/relate` — what reaches what.\n *\n * A typed, interned, bidirectional graph and the two traversals over it, plus the\n * file-shaped record the scanner produces and the fold that turns a pile of them\n * into the structure. No I/O, no parser, no resolver: the expensive half belongs\n * to whoever owns the disk, and this is the half that has to be fast and has to\n * be provable without one.\n *\n * See [`docs/selecting.md`](../../../../docs/selecting.md) for what it is for.\n */\n\nexport {\n EDGE_KINDS,\n NODE_KINDS,\n RUNTIME_EDGES,\n idOf,\n keyOf,\n nodeAt,\n nodesOfKind,\n relationsOf,\n type Adjacency,\n type EdgeKind,\n type Node,\n type NodeId,\n type NodeKind,\n type Relation,\n type Relations,\n} from './graph.js';\n\nexport {\n beforeReach,\n changedBefore,\n within,\n type BeforeReach,\n type BeforeReachOptions,\n} from './before.js';\n\nexport { beyondReach, type BeyondReach, type InstallMoved } from './beyond.js';\n\nexport {\n NOTHING,\n WHOLE,\n carried,\n type CarriedOptions,\n type CarriedStep,\n type Carry,\n type Held,\n type Reached,\n type Whole,\n} from './carried.js';\n\nexport { dominatorsOf } from './dominate.js';\n\nexport { type EdgeUse, type Uses } from './narrow.js';\n\nexport {\n dependenciesOf,\n dependentsOf,\n trailOf,\n type Traversal,\n type TraversalOptions,\n} from './reach.js';\n\nexport {\n CLOSURE_EDGES,\n closureOf,\n driftedBetween,\n type Closure,\n type ClosureInput,\n type Drift,\n} from './merkle.js';\n\nexport {\n explain,\n affectedBy,\n relationsOfFiles,\n type FileEdge,\n type FileRecord,\n type AffectedOptions,\n type PackageEdge,\n type RelationsOptions,\n type Affected,\n} from './records.js';\n"]}
|
package/dist/relate/merkle.js
CHANGED
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
* left to a recorded run, which sees the module load whatever named it.
|
|
47
47
|
*/
|
|
48
48
|
import { digestCombine, digestString } from '../format/hash.js';
|
|
49
|
+
import { allowedKinds } from './carried.js';
|
|
49
50
|
import { EDGE_KINDS, NODE_KINDS, keyOf, } from './graph.js';
|
|
50
51
|
/**
|
|
51
52
|
* Hash every node over its own content and its whole dependency closure.
|
|
@@ -56,7 +57,7 @@ import { EDGE_KINDS, NODE_KINDS, keyOf, } from './graph.js';
|
|
|
56
57
|
*/
|
|
57
58
|
export function closureOf(input) {
|
|
58
59
|
const { relations, content } = input;
|
|
59
|
-
const allowed = allowedKinds(input.through);
|
|
60
|
+
const allowed = allowedKinds(input.through, CLOSURE_EDGES);
|
|
60
61
|
const count = relations.names.length;
|
|
61
62
|
const { component, order } = condense(relations, allowed);
|
|
62
63
|
const digests = new Map();
|
|
@@ -243,16 +244,6 @@ export const CLOSURE_EDGES = [
|
|
|
243
244
|
'declared-in',
|
|
244
245
|
'depends',
|
|
245
246
|
];
|
|
246
|
-
/** Edge kinds as a lookup, so the inner loop tests a byte. */
|
|
247
|
-
function allowedKinds(through) {
|
|
248
|
-
const allowed = new Uint8Array(EDGE_KINDS.length);
|
|
249
|
-
for (const kind of through ?? CLOSURE_EDGES) {
|
|
250
|
-
const at = EDGE_KINDS.indexOf(kind);
|
|
251
|
-
if (at !== -1)
|
|
252
|
-
allowed[at] = 1;
|
|
253
|
-
}
|
|
254
|
-
return allowed;
|
|
255
|
-
}
|
|
256
247
|
function byCodeUnit(a, b) {
|
|
257
248
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
258
249
|
}
|