@cplieger/deadset-ts 4.0.1 → 5.1.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/README.md +4 -4
- package/dist/bin/component-mapper.js +5 -0
- package/dist/bin/mapper-connection.js +101 -0
- package/dist/bin/node-host.js +24 -2
- package/dist/src/analysis.js +301 -41
- package/dist/src/component-files.js +612 -0
- package/dist/src/component-layer.js +132 -0
- package/dist/src/config.js +9 -2
- package/dist/src/configuration-files.js +290 -0
- package/dist/src/conformance-record.js +3 -9
- package/dist/src/contextual-uses.js +240 -0
- package/dist/src/convention-rows.js +389 -0
- package/dist/src/conventions.js +565 -0
- package/dist/src/dependencies.js +36 -3
- package/dist/src/destructuring.js +6 -6
- package/dist/src/discover.js +10 -3
- package/dist/src/emit-map.js +72 -0
- package/dist/src/entry-point-gaps.js +81 -35
- package/dist/src/entry-points.js +50 -32
- package/dist/src/exempt-classes.js +6 -1
- package/dist/src/exempt.js +32 -20
- package/dist/src/findings/completion.js +46 -32
- package/dist/src/findings/emitters.js +22 -1
- package/dist/src/findings/interfaces.js +6 -2
- package/dist/src/findings/reads-and-writes.js +53 -9
- package/dist/src/findings/unused-declarations.js +47 -3
- package/dist/src/generated-file.js +24 -0
- package/dist/src/graph.js +13 -5
- package/dist/src/interface-satisfaction.js +2 -68
- package/dist/src/inventory.js +8 -3
- package/dist/src/kinds.js +6 -5
- package/dist/src/manifest.js +37 -11
- package/dist/src/matrix.js +21 -13
- package/dist/src/partial.js +3 -1
- package/dist/src/pnpm-workspace.js +120 -0
- package/dist/src/position.js +17 -2
- package/dist/src/print.js +2 -0
- package/dist/src/query.js +15 -0
- package/dist/src/references.js +404 -36
- package/dist/src/report.js +26 -6
- package/dist/src/reporters/annotations.js +10 -3
- package/dist/src/reporters/template.js +3 -0
- package/dist/src/resolve.js +37 -11
- package/dist/src/roots.js +37 -8
- package/dist/src/run.js +13 -3
- package/dist/src/schema.js +2 -0
- package/dist/src/session.js +141 -34
- package/dist/src/setup-failure.js +23 -0
- package/dist/src/source-files.js +4 -61
- package/dist/src/stores.js +3 -0
- package/dist/src/sweep.js +4 -2
- package/dist/src/template-field.js +38 -6
- package/dist/src/test-support.js +59 -0
- package/dist/src/type-errors.js +252 -0
- package/dist/src/type-query-alias.js +92 -0
- package/dist/src/value-flow.js +74 -0
- package/dist/src/verbs/analyze.js +24 -4
- package/dist/src/verbs/explain.js +4 -1
- package/dist/src/verbs/print-projects.js +17 -56
- package/dist/src/verbs/print-retained.js +7 -2
- package/dist/src/verbs/print-roots.js +7 -2
- package/dist/src/version-range.js +255 -0
- package/dist/src/version.js +1 -1
- package/dist/src/workspace-manifests.js +68 -0
- package/dist/src/workspace-programs.js +342 -0
- package/dist/src/workspace-resolution.js +307 -0
- package/dist/src/workspace-run.js +75 -0
- package/dist/src/workspace.js +358 -0
- package/jsr.json +1 -1
- package/package.json +3 -2
- package/src/analysis.ts +475 -72
- package/src/component-files.ts +718 -0
- package/src/component-layer.ts +160 -0
- package/src/config.ts +11 -2
- package/src/configuration-files.ts +370 -0
- package/src/conformance-record.ts +3 -10
- package/src/contextual-uses.ts +354 -0
- package/src/convention-rows.ts +448 -0
- package/src/conventions.ts +788 -0
- package/src/dependencies.ts +49 -2
- package/src/destructuring.ts +16 -6
- package/src/discover.ts +10 -6
- package/src/emit-map.ts +93 -0
- package/src/entry-point-gaps.ts +92 -38
- package/src/entry-points.ts +79 -36
- package/src/exempt-classes.ts +6 -1
- package/src/exempt.ts +69 -20
- package/src/file-facts.ts +2 -0
- package/src/findings/completion.ts +55 -36
- package/src/findings/emitters.ts +32 -1
- package/src/findings/interfaces.ts +9 -2
- package/src/findings/reads-and-writes.ts +70 -9
- package/src/findings/unused-declarations.ts +60 -3
- package/src/generated-file.ts +27 -0
- package/src/graph.ts +15 -4
- package/src/host.ts +23 -0
- package/src/index.ts +1 -1
- package/src/interface-satisfaction.ts +1 -92
- package/src/inventory.ts +17 -3
- package/src/kinds.ts +7 -5
- package/src/manifest.ts +47 -14
- package/src/matrix.ts +43 -15
- package/src/partial.ts +6 -1
- package/src/pnpm-workspace.ts +135 -0
- package/src/position.ts +23 -2
- package/src/print.ts +2 -0
- package/src/query.ts +26 -0
- package/src/references.ts +472 -40
- package/src/report.ts +56 -6
- package/src/reporters/annotations.ts +12 -3
- package/src/reporters/template.ts +3 -0
- package/src/resolve.ts +65 -11
- package/src/roots.ts +70 -9
- package/src/run.ts +15 -5
- package/src/schema.ts +2 -0
- package/src/session.ts +218 -37
- package/src/setup-failure.ts +39 -0
- package/src/source-files.ts +4 -66
- package/src/stores.ts +9 -0
- package/src/sweep.ts +9 -2
- package/src/template-field.ts +42 -7
- package/src/test-support.ts +77 -0
- package/src/type-errors.ts +340 -0
- package/src/type-query-alias.ts +133 -0
- package/src/value-flow.ts +104 -0
- package/src/verbs/analyze.ts +38 -4
- package/src/verbs/explain.ts +4 -1
- package/src/verbs/print-projects.ts +17 -69
- package/src/verbs/print-retained.ts +7 -2
- package/src/verbs/print-roots.ts +7 -2
- package/src/verbs/verb.ts +6 -3
- package/src/version-range.ts +309 -0
- package/src/version.ts +1 -1
- package/src/workspace-manifests.ts +100 -0
- package/src/workspace-programs.ts +486 -0
- package/src/workspace-resolution.ts +393 -0
- package/src/workspace-run.ts +148 -0
- package/src/workspace.ts +463 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The uses contextual types make of the target's members, which no name node resolves to.
|
|
3
|
+
* An object-literal property writes the member of its name on each constituent of the
|
|
4
|
+
* literal's contextual type that declares one. A value reaching a position typed by another
|
|
5
|
+
* object type reads each of its members that type declares, through array elements and
|
|
6
|
+
* function parameters and results too; a class instance there is the
|
|
7
|
+
* interface-satisfaction class's evidence instead.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
isComputedPropertyName,
|
|
12
|
+
isGetAccessorDeclaration,
|
|
13
|
+
isIdentifier,
|
|
14
|
+
isMethodDeclaration,
|
|
15
|
+
isNoSubstitutionTemplateLiteral,
|
|
16
|
+
isNumericLiteral,
|
|
17
|
+
isObjectLiteralExpression,
|
|
18
|
+
isPropertyAssignment,
|
|
19
|
+
isSetAccessorDeclaration,
|
|
20
|
+
isShorthandPropertyAssignment,
|
|
21
|
+
isStringLiteral,
|
|
22
|
+
type Expression,
|
|
23
|
+
type Node,
|
|
24
|
+
type ObjectLiteralExpression,
|
|
25
|
+
} from "@typescript/native/unstable/ast";
|
|
26
|
+
import { SignatureKind, type Type } from "@typescript/native/unstable/sync";
|
|
27
|
+
import type { AliasChains } from "./alias-chain.ts";
|
|
28
|
+
import type { Inventory, SymbolKind } from "./inventory.ts";
|
|
29
|
+
import { isAnswered, type PropertyTables } from "./query.ts";
|
|
30
|
+
import type { ProjectView } from "./session.ts";
|
|
31
|
+
|
|
32
|
+
/** The member kinds a contextual type writes and reads: data members and the members of object types. */
|
|
33
|
+
const MEMBER_KINDS: ReadonlySet<SymbolKind> = new Set(["type-member", "class-member"]);
|
|
34
|
+
|
|
35
|
+
/** One object literal a walk found, and the declaration it is written inside. */
|
|
36
|
+
export interface LiteralAt {
|
|
37
|
+
readonly literal: ObjectLiteralExpression;
|
|
38
|
+
readonly from: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** One value handed to a position typed by its context, and the declaration it is written inside. */
|
|
42
|
+
export interface FlowAt {
|
|
43
|
+
readonly value: Expression;
|
|
44
|
+
readonly from: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One use the contextual types make: the node it is written at and the members it names. */
|
|
48
|
+
export interface ContextualUse {
|
|
49
|
+
readonly at: Node;
|
|
50
|
+
readonly from: string;
|
|
51
|
+
readonly ids: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** What the contextual types of one project's literals and values use, and what asking cost. */
|
|
55
|
+
export interface ContextualUses {
|
|
56
|
+
/** Each literal property that writes a member, by the property's node. */
|
|
57
|
+
readonly writes: readonly ContextualUse[];
|
|
58
|
+
/** Each value whose members a position typed by another object type reads. */
|
|
59
|
+
readonly reads: readonly ContextualUse[];
|
|
60
|
+
/** The batched lookups: contextual types and value types, one per capped run. */
|
|
61
|
+
readonly batches: number;
|
|
62
|
+
/** The per-type lookups: each distinct type's constituents, properties and declaring symbol. */
|
|
63
|
+
readonly lookups: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The name one literal property is written under, where the name is literal text. */
|
|
67
|
+
function propertyName(property: Node): { node: Node; text: string } | undefined {
|
|
68
|
+
if (
|
|
69
|
+
!isPropertyAssignment(property) &&
|
|
70
|
+
!isShorthandPropertyAssignment(property) &&
|
|
71
|
+
!isMethodDeclaration(property) &&
|
|
72
|
+
!isGetAccessorDeclaration(property) &&
|
|
73
|
+
!isSetAccessorDeclaration(property)
|
|
74
|
+
) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const name = property.name;
|
|
78
|
+
if (isIdentifier(name) || isStringLiteral(name) || isNumericLiteral(name)) {
|
|
79
|
+
return { node: name, text: name.text };
|
|
80
|
+
}
|
|
81
|
+
if (isComputedPropertyName(name)) {
|
|
82
|
+
const key = name.expression;
|
|
83
|
+
if (isStringLiteral(key) || isNumericLiteral(key) || isNoSubstitutionTemplateLiteral(key)) {
|
|
84
|
+
return { node: name, text: key.text };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The parameter and result types of one call signature, each where the checker answered. */
|
|
91
|
+
interface Called {
|
|
92
|
+
readonly parameters: readonly (Type | undefined)[];
|
|
93
|
+
readonly result: Type | undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** One object type a value is checked as, and the position's object types it is checked against. */
|
|
97
|
+
interface Checked {
|
|
98
|
+
readonly source: Type;
|
|
99
|
+
readonly targets: readonly Type[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** How many element and signature steps the structural walk takes below a value's own type. */
|
|
103
|
+
const MAX_DEPTH = 3;
|
|
104
|
+
|
|
105
|
+
/** One member of an object type, by the inventory's declarations and the checker's declaration handles. */
|
|
106
|
+
interface Member {
|
|
107
|
+
readonly ids: readonly string[];
|
|
108
|
+
readonly handles: ReadonlySet<string>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The contextual uses of one project's literals and values, asked in batches: one
|
|
113
|
+
* contextual type per literal and per value whose type carries a member of the target,
|
|
114
|
+
* one type per value, and each distinct type's properties once.
|
|
115
|
+
*/
|
|
116
|
+
export function contextualUses<Brand>(
|
|
117
|
+
project: ProjectView<Brand>,
|
|
118
|
+
held: Inventory,
|
|
119
|
+
chains: AliasChains,
|
|
120
|
+
literals: readonly LiteralAt[],
|
|
121
|
+
flows: readonly FlowAt[],
|
|
122
|
+
cap: number,
|
|
123
|
+
properties: PropertyTables,
|
|
124
|
+
): ContextualUses {
|
|
125
|
+
const queries = project.queries;
|
|
126
|
+
const kinds = new Map(held.symbols.map((symbol) => [symbol.id, symbol.kind]));
|
|
127
|
+
let batches = 0;
|
|
128
|
+
let lookups = 0;
|
|
129
|
+
const batchesOf = (count: number): number => Math.ceil(count / cap);
|
|
130
|
+
|
|
131
|
+
const parts = new Map<number, readonly Type[]>();
|
|
132
|
+
/** The object types one type is: itself, or its constituents at any depth. */
|
|
133
|
+
const partsOf = (type: Type): readonly Type[] => {
|
|
134
|
+
const known = parts.get(type.id);
|
|
135
|
+
if (known !== undefined) {
|
|
136
|
+
return known;
|
|
137
|
+
}
|
|
138
|
+
let found: readonly Type[] = [];
|
|
139
|
+
if (type.isUnionType() || type.isIntersectionType()) {
|
|
140
|
+
lookups += 1;
|
|
141
|
+
const constituents = queries.constituents(type);
|
|
142
|
+
found = isAnswered(constituents) ? constituents.flatMap(partsOf) : [];
|
|
143
|
+
} else if (type.isObjectType()) {
|
|
144
|
+
found = [type];
|
|
145
|
+
}
|
|
146
|
+
parts.set(type.id, found);
|
|
147
|
+
return found;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const tables = new Map<number, ReadonlyMap<string, Member>>();
|
|
151
|
+
/** The members one object type has, by name. */
|
|
152
|
+
const membersOf = (type: Type): ReadonlyMap<string, Member> => {
|
|
153
|
+
const known = tables.get(type.id);
|
|
154
|
+
if (known !== undefined) {
|
|
155
|
+
return known;
|
|
156
|
+
}
|
|
157
|
+
const table = new Map<string, Member>();
|
|
158
|
+
const read = properties.of(type);
|
|
159
|
+
lookups += read.asked ? 1 : 0;
|
|
160
|
+
for (const property of isAnswered(read.properties) ? read.properties : []) {
|
|
161
|
+
table.set(property.name, {
|
|
162
|
+
ids: chains
|
|
163
|
+
.declarationsOf(property)
|
|
164
|
+
.filter((id) => MEMBER_KINDS.has(kinds.get(id) ?? "file")),
|
|
165
|
+
handles: new Set(
|
|
166
|
+
property.declarations.map((handle) => `${handle.path}#${String(handle.index)}`),
|
|
167
|
+
),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
tables.set(type.id, table);
|
|
171
|
+
return table;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const writes: ContextualUse[] = [];
|
|
175
|
+
batches += batchesOf(literals.length);
|
|
176
|
+
const contexts = queries.contextualTypes(
|
|
177
|
+
literals.map((one) => one.literal),
|
|
178
|
+
cap,
|
|
179
|
+
);
|
|
180
|
+
literals.forEach(({ literal, from }, index) => {
|
|
181
|
+
const context = contexts[index];
|
|
182
|
+
if (context === undefined || !isAnswered(context)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const tablesOf = partsOf(context).map(membersOf);
|
|
186
|
+
for (const property of literal.properties) {
|
|
187
|
+
const name = propertyName(property);
|
|
188
|
+
if (name === undefined) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const ids = [...new Set(tablesOf.flatMap((table) => table.get(name.text)?.ids ?? []))];
|
|
192
|
+
if (ids.length > 0) {
|
|
193
|
+
writes.push({ at: name.node, from, ids });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const carried = flows.filter((flow) => !isObjectLiteralExpression(flow.value));
|
|
199
|
+
batches += batchesOf(carried.length);
|
|
200
|
+
const types = queries.typesAt(
|
|
201
|
+
carried.map((flow) => flow.value),
|
|
202
|
+
cap,
|
|
203
|
+
);
|
|
204
|
+
/** The members a position may read structurally: those of object types, not of classes. */
|
|
205
|
+
const readable = (member: Member): readonly string[] =>
|
|
206
|
+
member.ids.filter((id) => kinds.get(id) === "type-member");
|
|
207
|
+
const carries = (part: Type): boolean =>
|
|
208
|
+
[...membersOf(part).values()].some((member) => readable(member).length > 0);
|
|
209
|
+
|
|
210
|
+
const elements = new Map<number, Type | undefined>();
|
|
211
|
+
/** The element type of an array type, read once per type. */
|
|
212
|
+
const elementOf = (type: Type): Type | undefined => {
|
|
213
|
+
if (elements.has(type.id)) {
|
|
214
|
+
return elements.get(type.id);
|
|
215
|
+
}
|
|
216
|
+
lookups += 1;
|
|
217
|
+
const isArray = queries.isArray(type);
|
|
218
|
+
let element: Type | undefined;
|
|
219
|
+
if (isAnswered(isArray) && isArray) {
|
|
220
|
+
lookups += 1;
|
|
221
|
+
const held = queries.typeArguments(type);
|
|
222
|
+
element = isAnswered(held) ? held[0] : undefined;
|
|
223
|
+
}
|
|
224
|
+
elements.set(type.id, element);
|
|
225
|
+
return element;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const calls = new Map<number, Called | undefined>();
|
|
229
|
+
/** The parameter and return types of a type's one call signature, read once per type. */
|
|
230
|
+
const callOf = (type: Type): Called | undefined => {
|
|
231
|
+
if (calls.has(type.id)) {
|
|
232
|
+
return calls.get(type.id);
|
|
233
|
+
}
|
|
234
|
+
lookups += 1;
|
|
235
|
+
const signatures = queries.signaturesOf(type, SignatureKind.Call);
|
|
236
|
+
let called: Called | undefined;
|
|
237
|
+
const [signature, ...more] = isAnswered(signatures) ? signatures : [];
|
|
238
|
+
if (signature !== undefined && more.length === 0) {
|
|
239
|
+
const parameters = signature.parameters.map((_parameter, index) =>
|
|
240
|
+
queries.parameterType(signature, index),
|
|
241
|
+
);
|
|
242
|
+
const result = queries.returnTypeOf(signature);
|
|
243
|
+
lookups += parameters.length + 1;
|
|
244
|
+
called = {
|
|
245
|
+
parameters: parameters.map((one) => (isAnswered(one) ? one : undefined)),
|
|
246
|
+
result: isAnswered(result) ? result : undefined,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
calls.set(type.id, called);
|
|
250
|
+
return called;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Each object type a value of `source` is checked as against the object types of
|
|
255
|
+
* `target`: the value's own, and through array elements and through a function's
|
|
256
|
+
* parameters, checked the other way, and its result. A source that is one of the
|
|
257
|
+
* target's own object types matches it as it stands.
|
|
258
|
+
*/
|
|
259
|
+
const compared = (
|
|
260
|
+
source: Type | undefined,
|
|
261
|
+
target: Type | undefined,
|
|
262
|
+
depth: number,
|
|
263
|
+
into: Checked[],
|
|
264
|
+
seen: Set<string>,
|
|
265
|
+
): void => {
|
|
266
|
+
const key = `${String(source?.id)}>${String(target?.id)}`;
|
|
267
|
+
if (source === undefined || target === undefined || source.id === target.id) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (depth > MAX_DEPTH || seen.has(key)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
seen.add(key);
|
|
274
|
+
const targets = partsOf(target);
|
|
275
|
+
const sources = partsOf(source).filter((one) => !targets.some((part) => part.id === one.id));
|
|
276
|
+
for (const one of sources) {
|
|
277
|
+
if (carries(one)) {
|
|
278
|
+
into.push({ source: one, targets });
|
|
279
|
+
}
|
|
280
|
+
const element = elementOf(one);
|
|
281
|
+
const called = element === undefined ? callOf(one) : undefined;
|
|
282
|
+
for (const part of targets) {
|
|
283
|
+
if (element !== undefined) {
|
|
284
|
+
compared(element, elementOf(part), depth + 1, into, seen);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const other = called === undefined ? undefined : callOf(part);
|
|
288
|
+
if (called === undefined || other === undefined) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
other.parameters.forEach((parameter, index) => {
|
|
292
|
+
compared(parameter, called.parameters[index], depth + 1, into, seen);
|
|
293
|
+
});
|
|
294
|
+
compared(called.result, other.result, depth + 1, into, seen);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/** Whether a value of one type may carry a member a position reads, at any depth the walk takes. */
|
|
300
|
+
const mayCarry = (type: Type, depth: number): boolean =>
|
|
301
|
+
depth <= MAX_DEPTH &&
|
|
302
|
+
partsOf(type).some((part) => {
|
|
303
|
+
if (carries(part)) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
const element = elementOf(part);
|
|
307
|
+
if (element !== undefined) {
|
|
308
|
+
return mayCarry(element, depth + 1);
|
|
309
|
+
}
|
|
310
|
+
return callOf(part) !== undefined;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const sourced = carried.flatMap((flow, index) => {
|
|
314
|
+
const type = types[index];
|
|
315
|
+
return type === undefined || !isAnswered(type) || !mayCarry(type, 0) ? [] : [{ flow, type }];
|
|
316
|
+
});
|
|
317
|
+
batches += batchesOf(sourced.length);
|
|
318
|
+
const positions = queries.contextualTypes(
|
|
319
|
+
sourced.map((one) => one.flow.value),
|
|
320
|
+
cap,
|
|
321
|
+
);
|
|
322
|
+
const reads: ContextualUse[] = [];
|
|
323
|
+
sourced.forEach(({ flow, type }, index) => {
|
|
324
|
+
const position = positions[index];
|
|
325
|
+
if (position === undefined || !isAnswered(position)) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const checked: Checked[] = [];
|
|
329
|
+
compared(type, position, 0, checked, new Set());
|
|
330
|
+
const ids = new Set<string>();
|
|
331
|
+
for (const { source, targets } of checked) {
|
|
332
|
+
const tables = targets.map(membersOf);
|
|
333
|
+
for (const [name, member] of membersOf(source)) {
|
|
334
|
+
const read = tables.some((table) => {
|
|
335
|
+
const declared = table.get(name);
|
|
336
|
+
return (
|
|
337
|
+
declared !== undefined &&
|
|
338
|
+
declared.handles.size > 0 &&
|
|
339
|
+
![...declared.handles].some((handle) => member.handles.has(handle))
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
if (read) {
|
|
343
|
+
for (const id of readable(member)) {
|
|
344
|
+
ids.add(id);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (ids.size > 0) {
|
|
350
|
+
reads.push({ at: flow.value, from: flow.from, ids: [...ids] });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
return { writes, reads, batches, lookups };
|
|
354
|
+
}
|