ucn 5.5.1 → 5.5.2
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/.claude/skills/ucn/SKILL.md +35 -9
- package/.claude/skills/ucn/references/commands.md +38 -16
- package/.claude/skills/ucn/references/resolution-rules.md +126 -20
- package/.claude/skills/ucn/references/trust-contract.md +5 -3
- package/README.md +30 -21
- package/cli/index.js +1 -1
- package/core/accessors.js +341 -54
- package/core/account.js +74 -20
- package/core/analysis.js +257 -125
- package/core/ast-analysis.js +12 -3
- package/core/brief.js +8 -4
- package/core/build-worker.js +2 -1
- package/core/cache.js +137 -6
- package/core/call-name-cache.js +134 -0
- package/core/callable-scope.js +21 -0
- package/core/callers.js +4726 -940
- package/core/check.js +22 -5
- package/core/class-identity.js +399 -12
- package/core/constructor-calls.js +831 -0
- package/core/constructor-hierarchy.js +147 -0
- package/core/constructor-protocol.js +196 -0
- package/core/contract-membership.js +346 -140
- package/core/cpp-conversion-constraints.js +123 -0
- package/core/cpp-conversion-syntax.js +216 -0
- package/core/cpp-conversions.js +414 -0
- package/core/cpp-scope.js +245 -44
- package/core/deadcode.js +35 -15
- package/core/decorator-binding.js +137 -3
- package/core/enum-reference-syntax.js +134 -0
- package/core/execute.js +17 -1
- package/core/external-macros.js +42 -10
- package/core/go-packages.js +171 -0
- package/core/graph-build.js +2 -0
- package/core/graph.js +75 -59
- package/core/ignore-rules.js +2 -1
- package/core/imports.js +145 -19
- package/core/index-ir.js +41 -4
- package/core/ir.js +27 -6
- package/core/macro-expansion.js +38 -2
- package/core/out-of-class-members.js +182 -0
- package/core/output/analysis-ext.js +4 -2
- package/core/output/analysis.js +89 -20
- package/core/output/graph.js +12 -3
- package/core/output/lines.js +4 -3
- package/core/output/public.js +1 -0
- package/core/output/reporting.js +6 -1
- package/core/output/shared.js +2 -0
- package/core/output/tracing.js +28 -22
- package/core/output-budget.js +2 -2
- package/core/parallel-build.js +52 -1
- package/core/project.js +217 -112
- package/core/provenance-constructor-base.js +161 -0
- package/core/provenance-facts.js +257 -27
- package/core/provenance-overload.js +458 -54
- package/core/provenance-type.js +487 -0
- package/core/provenance.js +234 -15
- package/core/public-command.js +17 -2
- package/core/query-bodies.js +159 -0
- package/core/reflection.js +255 -2
- package/core/rename-token-syntax.js +111 -0
- package/core/reporting.js +13 -7
- package/core/rust-macro-expansion.js +151 -19
- package/core/rust-modules.js +1 -1
- package/core/rust-names.js +607 -0
- package/core/search.js +70 -17
- package/core/shared.js +98 -1
- package/core/source-memo.js +201 -0
- package/core/text-usage-syntax.js +38 -0
- package/core/tracing.js +7 -1
- package/core/type-denotation.js +124 -37
- package/core/type-references.js +615 -246
- package/core/value-references.js +2777 -0
- package/core/verify.js +1097 -281
- package/languages/adapter.js +28 -4
- package/languages/c-family.js +1118 -164
- package/languages/constructor-bases.js +105 -0
- package/languages/cpp-platform.js +26 -0
- package/languages/cpp-site-syntax.js +83 -0
- package/languages/csharp-scope.js +439 -0
- package/languages/csharp.js +428 -68
- package/languages/go.js +385 -207
- package/languages/html.js +4 -2
- package/languages/index.js +397 -2
- package/languages/java-platform.js +226 -0
- package/languages/java-unicode.js +269 -0
- package/languages/java.js +461 -117
- package/languages/javascript.js +418 -66
- package/languages/lexical-scope.js +190 -23
- package/languages/python.js +404 -54
- package/languages/rust.js +510 -82
- package/languages/utils.js +67 -8
- package/mcp/server.js +8 -2
- package/package.json +2 -2
|
@@ -1722,11 +1722,25 @@ function inReadUnit(ctx, record) {
|
|
|
1722
1722
|
* with the facts the file entry derives from them.
|
|
1723
1723
|
*/
|
|
1724
1724
|
function composeCalls(index, ctx) {
|
|
1725
|
+
const calls = composedCalls(ctx);
|
|
1726
|
+
installComposedCalls(index, ctx.filePath, calls, ctx.original);
|
|
1727
|
+
return calls;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/** The calls a derivation context composes, stable by line (not installed). */
|
|
1731
|
+
function composedCalls(ctx) {
|
|
1725
1732
|
readGroups(ctx, ctx.groups.map((group, i) => i));
|
|
1726
|
-
const fileEntry = index.files.get(ctx.filePath);
|
|
1727
1733
|
const calls = ctx.original.filter(record => !inReadUnit(ctx, record));
|
|
1728
1734
|
for (const group of ctx.groups) for (const record of group.records) calls.push(record);
|
|
1729
1735
|
for (const record of invocationRecords(ctx)) calls.push(record);
|
|
1736
|
+
// Stable by line: records keep document order within a line.
|
|
1737
|
+
calls.sort((a, b) => a.line - b.line);
|
|
1738
|
+
return calls;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
/** Install a file's composed calls in the calls cache, with the facts the file entry derives from them. */
|
|
1742
|
+
function installComposedCalls(index, filePath, calls, original) {
|
|
1743
|
+
const fileEntry = index.files.get(filePath);
|
|
1730
1744
|
// Shapes of generated calls whose value may be lost (audit-async).
|
|
1731
1745
|
const { openCallShape } = require('../languages/rust-value-flow');
|
|
1732
1746
|
const shapes = calls.filter(call => call.macroExpansion && call.name && !call.inMacro && !call.valueConsumed)
|
|
@@ -1734,18 +1748,15 @@ function composeCalls(index, ctx) {
|
|
|
1734
1748
|
if (shapes.length > 0) {
|
|
1735
1749
|
fileEntry.openCalls = [...new Set([...(fileEntry.openCalls || []), ...shapes])].sort(codeUnitCompare);
|
|
1736
1750
|
}
|
|
1737
|
-
|
|
1738
|
-
calls.sort((a, b) => a.line - b.line);
|
|
1739
|
-
const cached = index.callsCache.get(ctx.filePath);
|
|
1751
|
+
const cached = index.callsCache.get(filePath);
|
|
1740
1752
|
if (cached) {
|
|
1741
|
-
if (index.calleeIndex) index._removeFromCalleeIndex(
|
|
1753
|
+
if (index.calleeIndex) index._removeFromCalleeIndex(filePath, original);
|
|
1742
1754
|
cached.calls = calls;
|
|
1743
|
-
if (index.calleeIndex) index._addToCalleeIndex(
|
|
1755
|
+
if (index.calleeIndex) index._addToCalleeIndex(filePath, calls);
|
|
1744
1756
|
} else {
|
|
1745
|
-
index.callsCache.set(
|
|
1757
|
+
index.callsCache.set(filePath, { mtime: fileEntry.mtime, hash: fileEntry.hash, calls });
|
|
1746
1758
|
}
|
|
1747
1759
|
index.callsCacheDirty = true;
|
|
1748
|
-
return calls;
|
|
1749
1760
|
}
|
|
1750
1761
|
|
|
1751
1762
|
const CALL_NODES = new Set(['call_expression', 'macro_invocation', 'struct_expression', 'generic_function']);
|
|
@@ -2156,11 +2167,62 @@ function startRustMacroExpansion(index, pending, options = {}) {
|
|
|
2156
2167
|
const files = invokingFiles(index, pending, inventory, names);
|
|
2157
2168
|
let bytes = 0;
|
|
2158
2169
|
for (const file of files) bytes += index.files.get(file)?.size || 0;
|
|
2159
|
-
|
|
2160
|
-
|
|
2170
|
+
// A sequential build has no parse workers to keep (fix #422): when the
|
|
2171
|
+
// planned expansion is large - a crate whose API macros generate most of
|
|
2172
|
+
// its code has little source to parse - fresh workers expand it.
|
|
2173
|
+
let pool = options.pool?.available.length > 0 ? options.pool : null;
|
|
2174
|
+
let ownPool = null;
|
|
2175
|
+
if (!pool && freshExpansionWorthwhile(index, files, inventory)) {
|
|
2176
|
+
ownPool = require('./parallel-build').spawnExpansionPool(index,
|
|
2177
|
+
freshWorkerCount(index, Math.ceil(files.length / FRESH_FILES_PER_WORKER), files.length));
|
|
2178
|
+
pool = ownPool;
|
|
2179
|
+
}
|
|
2180
|
+
const run = files.length >= PARALLEL_MIN_FILES && (bytes >= PARALLEL_MIN_BYTES || ownPool)
|
|
2181
|
+
? startWorkers(index, files, { ...options, pool, inventory }) : null;
|
|
2182
|
+
if (ownPool) {
|
|
2183
|
+
if (run) {
|
|
2184
|
+
const dispose = run.dispose;
|
|
2185
|
+
run.dispose = () => { dispose(); ownPool.dispose(); };
|
|
2186
|
+
} else {
|
|
2187
|
+
ownPool.dispose();
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2161
2190
|
return { inventory, files, names, results: run };
|
|
2162
2191
|
}
|
|
2163
2192
|
|
|
2193
|
+
// Fresh expansion workers start cold (module load, parser warm-up): they
|
|
2194
|
+
// pay for themselves from this much planned expansion.
|
|
2195
|
+
const FRESH_MIN_SITES = 48;
|
|
2196
|
+
const FRESH_MIN_FILES = 12;
|
|
2197
|
+
const FRESH_FILES_PER_WORKER = 6;
|
|
2198
|
+
|
|
2199
|
+
/**
|
|
2200
|
+
* How many fresh threads to start for `wanted` workers' worth of work over
|
|
2201
|
+
* `files` files: 2 to EXPANSION_WORKERS, never more than an explicit build
|
|
2202
|
+
* worker count (a request for that execution shape) and none when the build
|
|
2203
|
+
* disabled workers; 0 when fewer than two would run.
|
|
2204
|
+
*/
|
|
2205
|
+
function freshWorkerCount(index, wanted, files) {
|
|
2206
|
+
const limit = index.lastBuildWorkerLimit ?? EXPANSION_WORKERS;
|
|
2207
|
+
const count = Math.min(EXPANSION_WORKERS, limit, files, Math.max(2, wanted));
|
|
2208
|
+
return count >= 2 ? count : 0;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
/** Is the planned expansion of a build without kept parse workers worth fresh workers? */
|
|
2212
|
+
function freshExpansionWorthwhile(index, files, inventory) {
|
|
2213
|
+
const env = process.env.UCN_FRESH_EXPANSION;
|
|
2214
|
+
if (env === '0' || freshWorkerCount(index, 2, files.length) === 0) return false;
|
|
2215
|
+
if (env === '1') return true;
|
|
2216
|
+
if (files.length < FRESH_MIN_FILES) return false;
|
|
2217
|
+
let sites = 0;
|
|
2218
|
+
for (const file of files) {
|
|
2219
|
+
for (const call of index.callsCache.get(file)?.calls || []) {
|
|
2220
|
+
if (call.isMacro && inventory.has(call.name)) sites++;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
return sites >= FRESH_MIN_SITES;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2164
2226
|
/**
|
|
2165
2227
|
* Build-time pass, phase 2b (after the import graph resolved `mod`
|
|
2166
2228
|
* declarations): apply the expansions of the planned files, expanding in
|
|
@@ -2235,7 +2297,7 @@ const EXPANSION_WORKERS = 3;
|
|
|
2235
2297
|
* to expand their import facts, impl/trait spans and macro invocation
|
|
2236
2298
|
* records. Also every name a project callable bears.
|
|
2237
2299
|
*/
|
|
2238
|
-
function workerSnapshot(index, workload, inventory) {
|
|
2300
|
+
function workerSnapshot(index, workload, inventory, fullCalls = false) {
|
|
2239
2301
|
const expanding = new Set(workload);
|
|
2240
2302
|
// Invocations resolve at the invoking file, and nested ones also at the
|
|
2241
2303
|
// defining file.
|
|
@@ -2266,7 +2328,10 @@ function workerSnapshot(index, workload, inventory) {
|
|
|
2266
2328
|
record.importBindings = (entry.importBindings || [])
|
|
2267
2329
|
.filter(binding => inventory.has(binding.alias || binding.name));
|
|
2268
2330
|
}
|
|
2269
|
-
if (full) {
|
|
2331
|
+
if (full && fullCalls) {
|
|
2332
|
+
// A calls derivation reads every parsed record of the file.
|
|
2333
|
+
record.calls = index.callsCache.get(file)?.calls || [];
|
|
2334
|
+
} else if (full) {
|
|
2270
2335
|
record.calls = (index.callsCache.get(file)?.calls || [])
|
|
2271
2336
|
.filter(call => call.isMacro && !call.macroExpansion && inventory.has(call.name))
|
|
2272
2337
|
.map(call => ({
|
|
@@ -2337,7 +2402,7 @@ function snapshotIndex(snapshot) {
|
|
|
2337
2402
|
* `signal[queueIndex]`), posting each record to `port`; `signal[workerIndex]`
|
|
2338
2403
|
* is set when this worker is done.
|
|
2339
2404
|
*/
|
|
2340
|
-
function runExpansionQueue({ snapshot, files, signal, workerIndex, queueIndex }, port) {
|
|
2405
|
+
async function runExpansionQueue({ snapshot, files, signal, workerIndex, queueIndex, mode }, port) {
|
|
2341
2406
|
const signalArray = new Int32Array(signal);
|
|
2342
2407
|
try {
|
|
2343
2408
|
const state = createState(snapshotIndex(JSON.parse(Buffer.from(snapshot).toString('utf8'))));
|
|
@@ -2347,11 +2412,15 @@ function runExpansionQueue({ snapshot, files, signal, workerIndex, queueIndex },
|
|
|
2347
2412
|
const file = files[next];
|
|
2348
2413
|
let result;
|
|
2349
2414
|
try {
|
|
2350
|
-
result = { file, record: expandForWorker(state, file) };
|
|
2415
|
+
result = { file, record: mode === 'calls' ? deriveForWorker(state, file) : expandForWorker(state, file) };
|
|
2351
2416
|
} catch (error) {
|
|
2352
2417
|
result = { file, error: error.message };
|
|
2353
2418
|
}
|
|
2354
2419
|
port.postMessage([result]);
|
|
2420
|
+
// Yield between files, as the parse loop does (fix #365): the
|
|
2421
|
+
// native trees of an expansion are freed only when this thread
|
|
2422
|
+
// returns to its event loop (fix #422).
|
|
2423
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
2355
2424
|
}
|
|
2356
2425
|
} finally {
|
|
2357
2426
|
Atomics.store(signalArray, workerIndex, 1);
|
|
@@ -2359,6 +2428,12 @@ function runExpansionQueue({ snapshot, files, signal, workerIndex, queueIndex },
|
|
|
2359
2428
|
}
|
|
2360
2429
|
}
|
|
2361
2430
|
|
|
2431
|
+
/** Worker-side: the whole-file calls derivation of one file (fix #422). */
|
|
2432
|
+
function deriveForWorker(state, filePath) {
|
|
2433
|
+
const ctx = expandFile(state, filePath, 'calls')?.context || null;
|
|
2434
|
+
return ctx ? { calls: composedCalls(ctx) } : { none: true };
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2362
2437
|
/** Worker-side: symbols-mode expansion of one file, as plain records. */
|
|
2363
2438
|
function expandForWorker(state, filePath) {
|
|
2364
2439
|
const rec = expandFile(state, filePath) || { sites: 0, blind: [], used: new Map(), expanded: false };
|
|
@@ -2391,7 +2466,8 @@ function startWorkers(index, files, options = {}) {
|
|
|
2391
2466
|
codeUnitCompare(a, b));
|
|
2392
2467
|
// One serialization shared by every worker (a structured clone per
|
|
2393
2468
|
// worker cost more than the expansion on large workspaces).
|
|
2394
|
-
const encoded = Buffer.from(JSON.stringify(workerSnapshot(index, files, options.inventory
|
|
2469
|
+
const encoded = Buffer.from(JSON.stringify(workerSnapshot(index, files, options.inventory,
|
|
2470
|
+
options.mode === 'calls')), 'utf8');
|
|
2395
2471
|
const snapshot = new SharedArrayBuffer(encoded.length);
|
|
2396
2472
|
encoded.copy(Buffer.from(snapshot));
|
|
2397
2473
|
const sab = new SharedArrayBuffer(4 * (count + 1));
|
|
@@ -2400,7 +2476,8 @@ function startWorkers(index, files, options = {}) {
|
|
|
2400
2476
|
for (let i = 0; i < count; i++) {
|
|
2401
2477
|
const worker = pool.available[i];
|
|
2402
2478
|
ports.push(pool.ports[worker]);
|
|
2403
|
-
pool.post(worker, { snapshot, files: ordered, signal: sab, workerIndex: i, queueIndex: count
|
|
2479
|
+
pool.post(worker, { snapshot, files: ordered, signal: sab, workerIndex: i, queueIndex: count,
|
|
2480
|
+
...(options.mode === 'calls' && { mode: 'calls' }) });
|
|
2404
2481
|
}
|
|
2405
2482
|
// Kept workers this job does not need end now.
|
|
2406
2483
|
for (const worker of pool.available.slice(count)) pool.retire(worker);
|
|
@@ -2451,12 +2528,16 @@ function rustExpandedCalls(index, filePath, rawCalls, { materialize = true } = {
|
|
|
2451
2528
|
rec.callsPending = true;
|
|
2452
2529
|
throw error;
|
|
2453
2530
|
}
|
|
2454
|
-
index
|
|
2531
|
+
finishDerivation(index, filePath, rec);
|
|
2532
|
+
return index.callsCache.get(filePath)?.calls || rawCalls;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
function finishDerivation(index, filePath, rec) {
|
|
2536
|
+
index._rustMacroState?.contexts.delete(filePath);
|
|
2455
2537
|
delete rec.words;
|
|
2456
2538
|
delete rec.groups;
|
|
2457
2539
|
delete rec.expansions;
|
|
2458
2540
|
index.macroExpansionDirty = true;
|
|
2459
|
-
return index.callsCache.get(filePath)?.calls || rawCalls;
|
|
2460
2541
|
}
|
|
2461
2542
|
|
|
2462
2543
|
// Derivation contexts whose trees are kept (fix #405); a context dropped
|
|
@@ -2976,13 +3057,64 @@ function completeRustMacroCalls(index) {
|
|
|
2976
3057
|
|
|
2977
3058
|
/** Derive every pending file's expansion calls (whole-program consumers). */
|
|
2978
3059
|
function materializeRustMacroCalls(index) {
|
|
3060
|
+
const pending = [];
|
|
3061
|
+
for (const [filePath, entry] of index.files) {
|
|
3062
|
+
if (!entry.rustMacroExpansion?.callsPending || !index.callsCache.get(filePath)) continue;
|
|
3063
|
+
// A file a query derived in part keeps its context's records.
|
|
3064
|
+
if (index._rustMacroState?.contexts.has(filePath)) continue;
|
|
3065
|
+
pending.push(filePath);
|
|
3066
|
+
}
|
|
3067
|
+
// Many files to derive at once (an orientation ranking, a deadcode
|
|
3068
|
+
// audit): fresh workers derive them (fix #422), each whole in one pass
|
|
3069
|
+
// exactly as below.
|
|
3070
|
+
const derived = deriveInWorkers(index, pending);
|
|
2979
3071
|
for (const [filePath, entry] of index.files) {
|
|
2980
3072
|
if (!entry.rustMacroExpansion?.callsPending) continue;
|
|
2981
3073
|
const cached = index.callsCache.get(filePath);
|
|
2982
|
-
if (cached)
|
|
3074
|
+
if (!cached) continue;
|
|
3075
|
+
const result = derived?.get(filePath);
|
|
3076
|
+
if (result) installDerived(index, filePath, entry.rustMacroExpansion, cached, result);
|
|
3077
|
+
else rustExpandedCalls(index, filePath, cached.calls);
|
|
2983
3078
|
}
|
|
2984
3079
|
}
|
|
2985
3080
|
|
|
3081
|
+
// Expanded text a materialization derives in fresh workers from, and per
|
|
3082
|
+
// worker (the build recorded each expansion's length): below it, starting
|
|
3083
|
+
// threads costs more than deriving in place.
|
|
3084
|
+
const DERIVE_IN_WORKERS_MIN_BYTES = 192 * 1024;
|
|
3085
|
+
const DERIVE_BYTES_PER_WORKER = 192 * 1024;
|
|
3086
|
+
|
|
3087
|
+
/** Whole-file calls derivations of `files` in fresh workers: Map<file, { calls } | { none }>, or null. */
|
|
3088
|
+
function deriveInWorkers(index, files) {
|
|
3089
|
+
const env = process.env.UCN_FRESH_EXPANSION;
|
|
3090
|
+
if (env === '0' || freshWorkerCount(index, 2, files.length) === 0) return null;
|
|
3091
|
+
let bytes = 0;
|
|
3092
|
+
for (const file of files) {
|
|
3093
|
+
const recorded = index.files.get(file)?.rustMacroExpansion?.expansions || [];
|
|
3094
|
+
for (let i = 2; i < recorded.length; i += 3) if (recorded[i] > 0) bytes += recorded[i];
|
|
3095
|
+
}
|
|
3096
|
+
if (env !== '1' && bytes < DERIVE_IN_WORKERS_MIN_BYTES) return null;
|
|
3097
|
+
const inventory = index._rustMacroState?.inventory || macroInventory(index);
|
|
3098
|
+
if (inventory.size === 0) return null;
|
|
3099
|
+
const count = freshWorkerCount(index, Math.ceil(bytes / DERIVE_BYTES_PER_WORKER), files.length);
|
|
3100
|
+
const pool = require('./parallel-build').spawnExpansionPool(index, count);
|
|
3101
|
+
if (!pool) return null;
|
|
3102
|
+
try {
|
|
3103
|
+
const run = startWorkers(index, files, { pool, inventory, mode: 'calls' });
|
|
3104
|
+
return run ? run.collect() : null;
|
|
3105
|
+
} finally {
|
|
3106
|
+
pool.dispose();
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
function installDerived(index, filePath, rec, cached, result) {
|
|
3111
|
+
const hints = (rec.calleeNames || []).map(name => ({ name, line: 0, calleeHint: true }));
|
|
3112
|
+
if (index.calleeIndex && hints.length > 0) index._removeFromCalleeIndex(filePath, hints);
|
|
3113
|
+
rec.callsPending = false;
|
|
3114
|
+
if (result.calls) installComposedCalls(index, filePath, result.calls, cached.calls);
|
|
3115
|
+
finishDerivation(index, filePath, rec);
|
|
3116
|
+
}
|
|
3117
|
+
|
|
2986
3118
|
/** Expanded and blind invocation sites, for health and deadcode disclosure. */
|
|
2987
3119
|
function rustMacroExpansionSummary(index) {
|
|
2988
3120
|
const summary = { sites: 0, files: 0, blind: { count: 0, fileCount: 0, files: [], sample: [], reasons: {} } };
|
package/core/rust-modules.js
CHANGED
|
@@ -200,4 +200,4 @@ function rustItemAccessible(index, modifiers, file, line, siteFile, siteLine) {
|
|
|
200
200
|
return scope.length <= site.segs.length && scope.every((seg, i) => site.segs[i] === seg);
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
module.exports = { rustPathModule, rustModuleItems, rustModulePathAt, rustItemAccessible };
|
|
203
|
+
module.exports = { rustPathModule, rustModuleItems, rustModulePathAt, rustItemAccessible, moduleTree };
|