@hey-api/codegen-core 0.5.4 → 0.6.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/dist/index.cjs DELETED
@@ -1,1759 +0,0 @@
1
-
2
- //#region rolldown:runtime
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
- key = keys[i];
13
- if (!__hasOwnProp.call(to, key) && key !== except) {
14
- __defProp(to, key, {
15
- get: ((k) => from[k]).bind(null, key),
16
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
- });
18
- }
19
- }
20
- }
21
- return to;
22
- };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
- value: mod,
25
- enumerable: true
26
- }) : target, mod));
27
-
28
- //#endregion
29
- let node_path = require("node:path");
30
- node_path = __toESM(node_path);
31
- let ansi_colors = require("ansi-colors");
32
- ansi_colors = __toESM(ansi_colors);
33
- let color_support = require("color-support");
34
- color_support = __toESM(color_support);
35
-
36
- //#region src/brands.ts
37
- const fileBrand = "heyapi.file";
38
- const nodeBrand = "heyapi.node";
39
- const symbolBrand = "heyapi.symbol";
40
-
41
- //#endregion
42
- //#region src/log.ts
43
- ansi_colors.default.enabled = (0, color_support.default)().hasBasic;
44
- const DEBUG_NAMESPACE = "heyapi";
45
- const NO_WARNINGS = /^(1|true|yes|on)$/i.test(process.env.HEYAPI_DISABLE_WARNINGS ?? "");
46
- const DebugGroups = {
47
- analyzer: ansi_colors.default.greenBright,
48
- dsl: ansi_colors.default.cyanBright,
49
- file: ansi_colors.default.yellowBright,
50
- registry: ansi_colors.default.blueBright,
51
- symbol: ansi_colors.default.magentaBright
52
- };
53
- const WarnGroups = { deprecated: ansi_colors.default.magentaBright };
54
- let cachedDebugGroups;
55
- function getDebugGroups() {
56
- if (cachedDebugGroups) return cachedDebugGroups;
57
- const value = process.env.DEBUG;
58
- cachedDebugGroups = new Set(value ? value.split(",").map((x) => x.trim().toLowerCase()) : []);
59
- return cachedDebugGroups;
60
- }
61
- /**
62
- * Tracks which deprecations have been shown to avoid spam.
63
- */
64
- const shownDeprecations = /* @__PURE__ */ new Set();
65
- function debug(message, group) {
66
- const groups = getDebugGroups();
67
- if (!(groups.has("*") || groups.has(`${DEBUG_NAMESPACE}:*`) || groups.has(`${DEBUG_NAMESPACE}:${group}`) || groups.has(group))) return;
68
- const prefix = (DebugGroups[group] ?? ansi_colors.default.whiteBright)(`${DEBUG_NAMESPACE}:${group}`);
69
- console.debug(`${prefix} ${message}`);
70
- }
71
- function warn(message, group) {
72
- if (NO_WARNINGS) return;
73
- const color = WarnGroups[group] ?? ansi_colors.default.yellowBright;
74
- console.warn(color(`${message}`));
75
- }
76
- function warnDeprecated({ context, field, replacement }) {
77
- const key = context ? `${context}:${field}:${JSON.stringify(replacement)}` : `${field}:${JSON.stringify(replacement)}`;
78
- if (shownDeprecations.has(key)) return;
79
- shownDeprecations.add(key);
80
- let message = `\`${field}\` is deprecated.`;
81
- if (replacement) {
82
- const reps = typeof replacement === "function" ? replacement(field) : replacement;
83
- const repString = (reps instanceof Array ? reps : [reps]).map((r) => `\`${r}\``).join(" or ");
84
- message += ` Use ${repString} instead.`;
85
- }
86
- warn(`${context ? `[${context}] ` : ""}${message}`, "deprecated");
87
- }
88
- const log = {
89
- debug,
90
- warn,
91
- warnDeprecated
92
- };
93
-
94
- //#endregion
95
- //#region src/files/file.ts
96
- var File = class {
97
- /**
98
- * Exports from this file.
99
- */
100
- _exports = [];
101
- /**
102
- * File extension (e.g. `.ts`).
103
- */
104
- _extension;
105
- /**
106
- * Actual emitted file path, including extension and directories.
107
- */
108
- _finalPath;
109
- /**
110
- * Imports to this file.
111
- */
112
- _imports = [];
113
- /**
114
- * Language of the file.
115
- */
116
- _language;
117
- /**
118
- * Logical, extension-free path used for planning and routing.
119
- */
120
- _logicalFilePath;
121
- /**
122
- * Base name of the file (without extension).
123
- */
124
- _name;
125
- /**
126
- * Syntax nodes contained in this file.
127
- */
128
- _nodes = [];
129
- /**
130
- * Renderer assigned to this file.
131
- */
132
- _renderer;
133
- /** Brand used for identifying files. */
134
- "~brand" = fileBrand;
135
- /** All names defined in this file, including local scopes. */
136
- allNames = /* @__PURE__ */ new Map();
137
- /** Whether this file is external to the project. */
138
- external;
139
- /** Unique identifier for the file. */
140
- id;
141
- /** The project this file belongs to. */
142
- project;
143
- /** Names declared at the top level of the file. */
144
- topLevelNames = /* @__PURE__ */ new Map();
145
- constructor(input, id, project) {
146
- this.external = input.external ?? false;
147
- this.id = id;
148
- if (input.language !== void 0) this._language = input.language;
149
- this._logicalFilePath = input.logicalFilePath.split(node_path.default.sep).join("/");
150
- if (input.name !== void 0) this._name = input.name;
151
- this.project = project;
152
- }
153
- /**
154
- * Exports from this file.
155
- */
156
- get exports() {
157
- return [...this._exports];
158
- }
159
- /**
160
- * Read-only accessor for the file extension.
161
- */
162
- get extension() {
163
- if (this.external) return;
164
- if (this._extension) return this._extension;
165
- const language = this.language;
166
- const extension = language ? this.project.extensions[language] : void 0;
167
- if (extension && extension[0]) return extension[0];
168
- }
169
- /**
170
- * Read-only accessor for the final emitted path.
171
- *
172
- * If undefined, the file has not yet been assigned a final path
173
- * or is external to the project and should not be emitted.
174
- */
175
- get finalPath() {
176
- if (this._finalPath) return this._finalPath;
177
- return [...this._logicalFilePath ? this._logicalFilePath.split("/").slice(0, -1) : [], `${this.name}${this.extension ?? ""}`].join("/");
178
- }
179
- /**
180
- * Imports to this file.
181
- */
182
- get imports() {
183
- return [...this._imports];
184
- }
185
- /**
186
- * Language of the file; inferred from nodes or fallback if not set explicitly.
187
- */
188
- get language() {
189
- if (this._language) return this._language;
190
- if (this._nodes[0]) return this._nodes[0].language;
191
- }
192
- /**
193
- * Logical, extension-free path used for planning and routing.
194
- */
195
- get logicalFilePath() {
196
- return this._logicalFilePath;
197
- }
198
- /**
199
- * Base name of the file (without extension).
200
- *
201
- * If no name was set explicitly, it is inferred from the logical file path.
202
- */
203
- get name() {
204
- if (this._name) return this._name;
205
- const name = this._logicalFilePath.split("/").pop();
206
- if (name) return name;
207
- const message = `File ${this.toString()} has no name`;
208
- log.debug(message, "file");
209
- throw new Error(message);
210
- }
211
- /**
212
- * Syntax nodes contained in this file.
213
- */
214
- get nodes() {
215
- return [...this._nodes];
216
- }
217
- /**
218
- * Renderer assigned to this file.
219
- */
220
- get renderer() {
221
- return this._renderer;
222
- }
223
- /**
224
- * Add an export group to the file.
225
- */
226
- addExport(group) {
227
- this._exports.push(group);
228
- }
229
- /**
230
- * Add an import group to the file.
231
- */
232
- addImport(group) {
233
- this._imports.push(group);
234
- }
235
- /**
236
- * Add a syntax node to the file.
237
- */
238
- addNode(node) {
239
- this._nodes.push(node);
240
- node.file = this;
241
- }
242
- /**
243
- * Sets the file extension.
244
- */
245
- setExtension(extension) {
246
- this._extension = extension;
247
- }
248
- /**
249
- * Sets the final emitted path of the file.
250
- */
251
- setFinalPath(path$4) {
252
- this._finalPath = path$4;
253
- }
254
- /**
255
- * Sets the language of the file.
256
- */
257
- setLanguage(lang) {
258
- this._language = lang;
259
- }
260
- /**
261
- * Sets the name of the file.
262
- */
263
- setName(name) {
264
- this._name = name;
265
- }
266
- /**
267
- * Sets the renderer assigned to this file.
268
- */
269
- setRenderer(renderer) {
270
- this._renderer = renderer;
271
- }
272
- /**
273
- * Returns a debug‑friendly string representation identifying the file.
274
- */
275
- toString() {
276
- return `[File ${this._logicalFilePath}#${this.id}]`;
277
- }
278
- };
279
-
280
- //#endregion
281
- //#region src/guards.ts
282
- function isBrand(value, brand) {
283
- if (!value || typeof value !== "object") return false;
284
- return value["~brand"] === brand;
285
- }
286
- function isNode(value) {
287
- if (!value || typeof value !== "object") return false;
288
- return isBrand(value, nodeBrand);
289
- }
290
- function isNodeRef(value) {
291
- return isBrand(value["~ref"], nodeBrand);
292
- }
293
- function isSymbol(value) {
294
- return isBrand(value, symbolBrand);
295
- }
296
- function isSymbolRef(value) {
297
- return isBrand(value["~ref"], symbolBrand);
298
- }
299
-
300
- //#endregion
301
- //#region src/languages/extensions.ts
302
- const defaultExtensions = {
303
- c: [".c"],
304
- "c#": [".cs"],
305
- "c++": [".cpp", ".hpp"],
306
- css: [".css"],
307
- dart: [".dart"],
308
- go: [".go"],
309
- haskell: [".hs"],
310
- html: [".html"],
311
- java: [".java"],
312
- javascript: [".js", ".jsx"],
313
- json: [".json"],
314
- kotlin: [".kt"],
315
- lua: [".lua"],
316
- markdown: [".md"],
317
- matlab: [".m"],
318
- perl: [".pl"],
319
- php: [".php"],
320
- python: [".py"],
321
- r: [".r"],
322
- ruby: [".rb"],
323
- rust: [".rs"],
324
- scala: [".scala"],
325
- shell: [".sh"],
326
- sql: [".sql"],
327
- swift: [".swift"],
328
- typescript: [".ts", ".tsx"],
329
- yaml: [".yaml", ".yml"]
330
- };
331
-
332
- //#endregion
333
- //#region src/planner/resolvers.ts
334
- const simpleNameConflictResolver = ({ attempt, baseName }) => attempt === 0 ? baseName : `${baseName}${attempt + 1}`;
335
- const underscoreNameConflictResolver = ({ attempt, baseName }) => attempt === 0 ? baseName : `${baseName}_${attempt + 1}`;
336
-
337
- //#endregion
338
- //#region src/languages/resolvers.ts
339
- const defaultNameConflictResolvers = {
340
- php: underscoreNameConflictResolver,
341
- python: underscoreNameConflictResolver,
342
- ruby: underscoreNameConflictResolver
343
- };
344
-
345
- //#endregion
346
- //#region src/logger.ts
347
- let loggerCounter = 0;
348
- const nameToId = (name) => `${name}-${loggerCounter++}`;
349
- const idEnd = (id) => `${id}-end`;
350
- const idLength = (id) => `${id}-length`;
351
- const idStart = (id) => `${id}-start`;
352
- const getSeverity = (duration, percentage) => {
353
- if (duration > 200) return {
354
- color: ansi_colors.default.red,
355
- type: "duration"
356
- };
357
- if (percentage > 30) return {
358
- color: ansi_colors.default.red,
359
- type: "percentage"
360
- };
361
- if (duration > 50) return {
362
- color: ansi_colors.default.yellow,
363
- type: "duration"
364
- };
365
- if (percentage > 10) return {
366
- color: ansi_colors.default.yellow,
367
- type: "percentage"
368
- };
369
- };
370
- var Logger = class {
371
- events = [];
372
- end(result) {
373
- let event;
374
- let events = this.events;
375
- for (const index of result.position) {
376
- event = events[index];
377
- if (event?.events) events = event.events;
378
- }
379
- if (event && !event.end) event.end = performance.mark(idEnd(event.id));
380
- }
381
- /**
382
- * Recursively end all unended events in the event tree.
383
- * This ensures all events have end marks before measuring.
384
- */
385
- endAllEvents(events) {
386
- for (const event of events) {
387
- if (!event.end) event.end = performance.mark(idEnd(event.id));
388
- if (event.events.length > 0) this.endAllEvents(event.events);
389
- }
390
- }
391
- report(print = true) {
392
- const firstEvent = this.events[0];
393
- if (!firstEvent) return;
394
- this.endAllEvents(this.events);
395
- const lastEvent = this.events[this.events.length - 1];
396
- const name = "root";
397
- const id = nameToId(name);
398
- try {
399
- const measure = performance.measure(idLength(id), idStart(firstEvent.id), idEnd(lastEvent.id));
400
- if (print) this.reportEvent({
401
- end: lastEvent.end,
402
- events: this.events,
403
- id,
404
- indent: 0,
405
- measure,
406
- name,
407
- start: firstEvent.start
408
- });
409
- return measure;
410
- } catch {
411
- return;
412
- }
413
- }
414
- reportEvent({ indent, ...parent }) {
415
- const color = !indent ? ansi_colors.default.cyan : ansi_colors.default.gray;
416
- const lastIndex = parent.events.length - 1;
417
- parent.events.forEach((event, index) => {
418
- try {
419
- const measure = performance.measure(idLength(event.id), idStart(event.id), idEnd(event.id));
420
- const duration = Math.ceil(measure.duration * 100) / 100;
421
- const percentage = Math.ceil(measure.duration / parent.measure.duration * 100 * 100) / 100;
422
- const severity = indent ? getSeverity(duration, percentage) : void 0;
423
- let durationLabel = `${duration.toFixed(2).padStart(8)}ms`;
424
- if (severity?.type === "duration") durationLabel = severity.color(durationLabel);
425
- const branch = index === lastIndex ? "└─ " : "├─ ";
426
- const prefix = !indent ? "" : "│ ".repeat(indent - 1) + branch;
427
- const maxLength = 38 - prefix.length;
428
- const percentageBranch = !indent ? "" : "↳ ";
429
- let percentageLabel = `${indent ? " ".repeat(indent - 1) + percentageBranch : ""}${percentage.toFixed(2)}%`;
430
- if (severity?.type === "percentage") percentageLabel = severity.color(percentageLabel);
431
- const jobPrefix = ansi_colors.default.gray("[root] ");
432
- console.log(`${jobPrefix}${ansi_colors.default.gray(prefix)}${color(`${event.name.padEnd(maxLength)} ${durationLabel} (${percentageLabel})`)}`);
433
- this.reportEvent({
434
- ...event,
435
- indent: indent + 1,
436
- measure
437
- });
438
- } catch {}
439
- });
440
- }
441
- start(id) {
442
- return performance.mark(idStart(id));
443
- }
444
- storeEvent({ result, ...event }) {
445
- const lastEventIndex = event.events.length - 1;
446
- const lastEvent = event.events[lastEventIndex];
447
- if (lastEvent && !lastEvent.end) {
448
- result.position = [...result.position, lastEventIndex];
449
- this.storeEvent({
450
- ...event,
451
- events: lastEvent.events,
452
- result
453
- });
454
- return;
455
- }
456
- const length = event.events.push({
457
- ...event,
458
- events: []
459
- });
460
- result.position = [...result.position, length - 1];
461
- }
462
- timeEvent(name) {
463
- const id = nameToId(name);
464
- const start = this.start(id);
465
- const event = {
466
- events: this.events,
467
- id,
468
- name,
469
- start
470
- };
471
- const result = { position: [] };
472
- this.storeEvent({
473
- ...event,
474
- result
475
- });
476
- return {
477
- mark: start,
478
- timeEnd: () => this.end(result)
479
- };
480
- }
481
- };
482
-
483
- //#endregion
484
- //#region src/files/registry.ts
485
- var FileRegistry = class {
486
- _id = 0;
487
- _values = /* @__PURE__ */ new Map();
488
- project;
489
- constructor(project) {
490
- this.project = project;
491
- }
492
- get(args) {
493
- return this._values.get(this.createFileKey(args));
494
- }
495
- isRegistered(args) {
496
- return this._values.has(this.createFileKey(args));
497
- }
498
- get nextId() {
499
- return this._id++;
500
- }
501
- register(file) {
502
- const key = this.createFileKey(file);
503
- let result = this._values.get(key);
504
- if (result) {
505
- if (file.name) result.setName(file.name);
506
- } else result = new File(file, this.nextId, this.project);
507
- this._values.set(key, result);
508
- return result;
509
- }
510
- *registered() {
511
- for (const file of this._values.values()) yield file;
512
- }
513
- createFileKey(args) {
514
- const logicalPath = args.logicalFilePath.split(node_path.default.sep).join("/");
515
- return `${args.external ? "ext:" : ""}${logicalPath}${args.language ? `:${args.language}` : ""}`;
516
- }
517
- };
518
-
519
- //#endregion
520
- //#region src/refs/refs.ts
521
- /**
522
- * Wraps a single value in a Ref object.
523
- *
524
- * If the value is already a Ref, returns it as-is (idempotent).
525
- *
526
- * @example
527
- * ```ts
528
- * const r = ref(123); // { '~ref': 123 }
529
- * console.log(r['~ref']); // 123
530
- *
531
- * const r2 = ref(r); // { '~ref': 123 } (not double-wrapped)
532
- * ```
533
- */
534
- const ref = (value) => {
535
- if (isRef(value)) return value;
536
- return { "~ref": value };
537
- };
538
- /**
539
- * Converts a plain object to an object of Refs (deep, per property).
540
- *
541
- * @example
542
- * ```ts
543
- * const obj = { a: 1, b: "x" };
544
- * const refs = refs(obj); // { a: { '~ref': 1 }, b: { '~ref': "x" } }
545
- * ```
546
- */
547
- const refs = (obj) => {
548
- const result = {};
549
- for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = ref(obj[key]);
550
- return result;
551
- };
552
- /**
553
- * Unwraps a single Ref object to its value.
554
- *
555
- * @example
556
- * ```ts
557
- * const r = { '~ref': 42 };
558
- * const n = fromRef(r); // 42
559
- * console.log(n); // 42
560
- * ```
561
- */
562
- const fromRef = (ref$1) => ref$1?.["~ref"];
563
- /**
564
- * Converts an object of Refs back to a plain object (unwraps all refs).
565
- *
566
- * @example
567
- * ```ts
568
- * const refs = { a: { '~ref': 1 }, b: { '~ref': "x" } };
569
- * const plain = fromRefs(refs); // { a: 1, b: "x" }
570
- * ```
571
- */
572
- const fromRefs = (obj) => {
573
- const result = {};
574
- for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = fromRef(obj[key]);
575
- return result;
576
- };
577
- /**
578
- * Checks whether a value is a Ref object.
579
- *
580
- * @param value Value to check
581
- * @returns True if the value is a Ref object.
582
- */
583
- const isRef = (value) => typeof value === "object" && value !== null && "~ref" in value;
584
-
585
- //#endregion
586
- //#region src/nodes/registry.ts
587
- var NodeRegistry = class {
588
- list = [];
589
- add(node) {
590
- return this.list.push(ref(node)) - 1;
591
- }
592
- *all() {
593
- for (const r of this.list) {
594
- const node = fromRef(r);
595
- if (node) yield node;
596
- }
597
- }
598
- remove(index) {
599
- this.list[index] = ref(null);
600
- }
601
- update(index, node) {
602
- this.list[index] = ref(node);
603
- }
604
- };
605
-
606
- //#endregion
607
- //#region src/project/namespace.ts
608
- const kindRank = {
609
- class: 3,
610
- enum: 4,
611
- function: 5,
612
- interface: 1,
613
- namespace: 0,
614
- type: 2,
615
- var: 6
616
- };
617
- /**
618
- * Returns true if two declarations of given kinds
619
- * are allowed to share the same identifier in TypeScript.
620
- */
621
- function canShareName(a, b) {
622
- if (kindRank[a] > kindRank[b]) [a, b] = [b, a];
623
- switch (a) {
624
- case "interface": return b === "class" || b === "interface";
625
- case "namespace": return b === "class" || b === "enum" || b === "function" || b === "namespace";
626
- case "type": return b === "function" || b === "var";
627
- default: return false;
628
- }
629
- }
630
-
631
- //#endregion
632
- //#region src/planner/scope.ts
633
- const createScope = (args = {}) => ({
634
- children: [],
635
- localNames: args.localNames || /* @__PURE__ */ new Map(),
636
- parent: args.parent,
637
- symbols: []
638
- });
639
-
640
- //#endregion
641
- //#region src/planner/analyzer.ts
642
- var AnalysisContext = class {
643
- /**
644
- * Stack of parent nodes during analysis.
645
- *
646
- * The top of the stack is the current semantic container.
647
- */
648
- _parentStack = [];
649
- scope;
650
- scopes = createScope();
651
- symbol;
652
- constructor(node) {
653
- this._parentStack.push(node);
654
- this.scope = this.scopes;
655
- this.symbol = node.symbol;
656
- }
657
- /**
658
- * Get the current semantic parent (top of stack).
659
- */
660
- get currentParent() {
661
- return this._parentStack[this._parentStack.length - 1];
662
- }
663
- /**
664
- * Register a child node under the current parent.
665
- */
666
- addChild(child, relationship = "container") {
667
- const parent = this.currentParent;
668
- if (!parent) return;
669
- if (!parent.structuralChildren) parent.structuralChildren = /* @__PURE__ */ new Map();
670
- parent.structuralChildren.set(child, relationship);
671
- if (!child.structuralParents) child.structuralParents = /* @__PURE__ */ new Map();
672
- child.structuralParents.set(parent, relationship);
673
- }
674
- addDependency(symbol) {
675
- if (this.symbol !== fromRef(symbol)) this.scope.symbols.push(symbol);
676
- }
677
- analyze(input) {
678
- const value = isRef(input) ? input : ref(input);
679
- if (isSymbolRef(value)) {
680
- const symbol = fromRef(value);
681
- if (symbol.node && this.currentParent !== symbol.node) this.addChild(symbol.node, "reference");
682
- this.addDependency(value);
683
- } else if (isNodeRef(value)) {
684
- const node = fromRef(value);
685
- this.addChild(node, "container");
686
- this.pushParent(node);
687
- node.analyze(this);
688
- this.popParent();
689
- }
690
- }
691
- localNames(scope) {
692
- const names = /* @__PURE__ */ new Map();
693
- for (const [name, kinds] of scope.localNames) names.set(name, new Set(kinds));
694
- if (scope.parent) {
695
- const parentNames = this.localNames(scope.parent);
696
- for (const [name, kinds] of parentNames) if (!names.has(name)) names.set(name, kinds);
697
- else {
698
- const existingKinds = names.get(name);
699
- for (const kind of kinds) existingKinds.add(kind);
700
- }
701
- }
702
- return names;
703
- }
704
- /**
705
- * Pop the current semantic parent.
706
- * Call this when exiting a container node.
707
- */
708
- popParent() {
709
- this._parentStack.pop();
710
- }
711
- popScope() {
712
- this.scope = this.scope.parent ?? this.scope;
713
- }
714
- /**
715
- * Push a node as the current semantic parent.
716
- */
717
- pushParent(node) {
718
- this._parentStack.push(node);
719
- }
720
- pushScope() {
721
- const scope = createScope({ parent: this.scope });
722
- this.scope.children.push(scope);
723
- this.scope = scope;
724
- }
725
- walkScopes(callback, scope = this.scopes) {
726
- this.scope = scope;
727
- for (const symbol of scope.symbols) callback(symbol, scope);
728
- for (const child of scope.children) {
729
- scope = child;
730
- this.walkScopes(callback, scope);
731
- }
732
- this.scope = this.scopes;
733
- }
734
- };
735
- var Analyzer = class {
736
- nodeCache = /* @__PURE__ */ new WeakMap();
737
- analyzeNode(node) {
738
- const cached = this.nodeCache.get(node);
739
- if (cached) return cached;
740
- node.root = true;
741
- const ctx = new AnalysisContext(node);
742
- node.analyze(ctx);
743
- this.nodeCache.set(node, ctx);
744
- return ctx;
745
- }
746
- analyze(nodes, callback) {
747
- for (const node of nodes) {
748
- const ctx = this.analyzeNode(node);
749
- callback?.(ctx, node);
750
- }
751
- }
752
- };
753
-
754
- //#endregion
755
- //#region src/planner/planner.ts
756
- const isTypeOnlyKind = (kind) => kind === "type" || kind === "interface";
757
- var Planner = class {
758
- analyzer = new Analyzer();
759
- cacheResolvedNames = /* @__PURE__ */ new Set();
760
- project;
761
- constructor(project) {
762
- this.project = project;
763
- }
764
- /**
765
- * Executes the planning phase for the project.
766
- */
767
- plan(meta) {
768
- this.cacheResolvedNames.clear();
769
- this.allocateFiles();
770
- this.assignLocalNames();
771
- this.resolveFilePaths(meta);
772
- this.planExports();
773
- this.planImports();
774
- }
775
- /**
776
- * Creates and assigns a file to every node, re-export,
777
- * and external dependency.
778
- */
779
- allocateFiles() {
780
- this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
781
- const symbol = node.symbol;
782
- if (!symbol) return;
783
- const file = this.project.files.register({
784
- external: false,
785
- language: node.language,
786
- logicalFilePath: symbol.getFilePath?.(symbol) || this.project.defaultFileName
787
- });
788
- file.addNode(node);
789
- symbol.setFile(file);
790
- for (const exportFrom of symbol.exportFrom) this.project.files.register({
791
- external: false,
792
- language: file.language,
793
- logicalFilePath: exportFrom
794
- });
795
- ctx.walkScopes((dependency) => {
796
- const dep = fromRef(dependency);
797
- if (dep.external && dep.isCanonical && !dep.file) {
798
- const file$1 = this.project.files.register({
799
- external: true,
800
- language: dep.node?.language,
801
- logicalFilePath: dep.external
802
- });
803
- dep.setFile(file$1);
804
- }
805
- });
806
- });
807
- }
808
- /**
809
- * Assigns final names to all symbols.
810
- *
811
- * First assigns top-level (file-scoped) symbol names, then local symbols.
812
- */
813
- assignLocalNames() {
814
- this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
815
- const symbol = node.symbol;
816
- if (!symbol) return;
817
- this.assignTopLevelName({
818
- ctx,
819
- node,
820
- symbol
821
- });
822
- });
823
- this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
824
- const file = node.file;
825
- if (!file) return;
826
- ctx.walkScopes((dependency) => {
827
- const dep = fromRef(dependency);
828
- if (dep.file) return;
829
- this.assignLocalName({
830
- ctx,
831
- file,
832
- scopesToUpdate: [createScope({ localNames: file.allNames })],
833
- symbol: dep
834
- });
835
- });
836
- });
837
- }
838
- /**
839
- * Resolves and sets final file paths for all non-external files. Attaches renderers.
840
- *
841
- * Uses the project's fileName function if provided, otherwise uses the file's current name.
842
- *
843
- * Resolves final paths relative to the project's root directory.
844
- */
845
- resolveFilePaths(meta) {
846
- for (const file of this.project.files.registered()) {
847
- if (file.external) {
848
- file.setFinalPath(file.logicalFilePath);
849
- continue;
850
- }
851
- const finalName = this.project.fileName?.(file.name) || file.name;
852
- file.setName(finalName);
853
- const finalPath = file.finalPath;
854
- if (finalPath) file.setFinalPath(node_path.default.resolve(this.project.root, finalPath));
855
- const ctx = {
856
- file,
857
- meta,
858
- project: this.project
859
- };
860
- const renderer = this.project.renderers.find((r) => r.supports(ctx));
861
- if (renderer) file.setRenderer(renderer);
862
- }
863
- }
864
- /**
865
- * Plans exports by analyzing all exported symbols.
866
- *
867
- * Registers re-export targets as files and creates new exported symbols for them.
868
- *
869
- * Assigns names to re-exported symbols and collects re-export metadata,
870
- * distinguishing type-only exports based on symbol kinds.
871
- */
872
- planExports() {
873
- const seenByFile = /* @__PURE__ */ new Map();
874
- const sourceFile = /* @__PURE__ */ new Map();
875
- this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
876
- if (!node.exported) return;
877
- const symbol = node.symbol;
878
- if (!symbol) return;
879
- const file = node.file;
880
- if (!file) return;
881
- for (const exportFrom of symbol.exportFrom) {
882
- const target = this.project.files.register({
883
- external: false,
884
- language: node.language,
885
- logicalFilePath: exportFrom
886
- });
887
- if (target.id === file.id) continue;
888
- let fileMap = seenByFile.get(target);
889
- if (!fileMap) {
890
- fileMap = /* @__PURE__ */ new Map();
891
- seenByFile.set(target, fileMap);
892
- }
893
- const exp = this.project.symbols.register({
894
- exported: true,
895
- external: symbol.external,
896
- importKind: symbol.importKind,
897
- kind: symbol.kind,
898
- name: symbol.finalName
899
- });
900
- exp.setFile(target);
901
- sourceFile.set(exp.id, file);
902
- this.assignTopLevelName({
903
- ctx,
904
- symbol: exp
905
- });
906
- let entry = fileMap.get(exp.finalName);
907
- if (!entry) {
908
- entry = {
909
- kinds: /* @__PURE__ */ new Set(),
910
- symbol: exp
911
- };
912
- fileMap.set(exp.finalName, entry);
913
- }
914
- entry.kinds.add(exp.kind);
915
- }
916
- });
917
- for (const [file, fileMap] of seenByFile) {
918
- const exports$1 = /* @__PURE__ */ new Map();
919
- for (const [, entry] of fileMap) {
920
- const source = sourceFile.get(entry.symbol.id);
921
- let exp = exports$1.get(source);
922
- if (!exp) exp = {
923
- canExportAll: true,
924
- exports: [],
925
- from: source,
926
- isTypeOnly: true
927
- };
928
- const isTypeOnly = [...entry.kinds].every((kind) => isTypeOnlyKind(kind));
929
- const exportedName = entry.symbol.finalName;
930
- exp.exports.push({
931
- exportedName,
932
- isTypeOnly,
933
- kind: entry.symbol.importKind,
934
- sourceName: entry.symbol.name
935
- });
936
- if (entry.symbol.name !== entry.symbol.finalName) exp.canExportAll = false;
937
- if (!isTypeOnly) exp.isTypeOnly = false;
938
- exports$1.set(source, exp);
939
- }
940
- for (const [, exp] of exports$1) file.addExport(exp);
941
- }
942
- }
943
- /**
944
- * Plans imports by analyzing symbol dependencies across files.
945
- *
946
- * For external dependencies, assigns top-level names.
947
- *
948
- * Creates or reuses import symbols for dependencies from other files,
949
- * assigning names and updating import metadata including type-only flags.
950
- */
951
- planImports() {
952
- const seenByFile = /* @__PURE__ */ new Map();
953
- this.analyzer.analyze(this.project.nodes.all(), (ctx) => {
954
- const symbol = ctx.symbol;
955
- if (!symbol) return;
956
- const file = symbol.file;
957
- if (!file) return;
958
- let fileMap = seenByFile.get(file);
959
- if (!fileMap) {
960
- fileMap = /* @__PURE__ */ new Map();
961
- seenByFile.set(file, fileMap);
962
- }
963
- ctx.walkScopes((dependency) => {
964
- const dep = fromRef(dependency);
965
- if (!dep.file || dep.file.id === file.id) return;
966
- if (dep.external) this.assignTopLevelName({
967
- ctx,
968
- symbol: dep
969
- });
970
- const fromFileId = dep.file.id;
971
- const importedName = dep.finalName;
972
- const isTypeOnly = isTypeOnlyKind(dep.kind);
973
- const key = `${fromFileId}|${importedName}|${dep.importKind}|${isTypeOnly}`;
974
- let entry = fileMap.get(key);
975
- if (!entry) {
976
- const imp = this.project.symbols.register({
977
- exported: dep.exported,
978
- external: dep.external,
979
- importKind: dep.importKind,
980
- kind: dep.kind,
981
- name: dep.finalName
982
- });
983
- imp.setFile(file);
984
- this.assignTopLevelName({
985
- ctx,
986
- scope: createScope({ localNames: imp.file.allNames }),
987
- symbol: imp
988
- });
989
- entry = {
990
- dep,
991
- kinds: /* @__PURE__ */ new Set(),
992
- symbol: imp
993
- };
994
- fileMap.set(key, entry);
995
- entry.kinds.add(imp.kind);
996
- }
997
- dependency["~ref"] = entry.symbol;
998
- });
999
- });
1000
- for (const [file, fileMap] of seenByFile) {
1001
- const imports = /* @__PURE__ */ new Map();
1002
- for (const [, entry] of fileMap) {
1003
- const source = entry.dep.file;
1004
- let imp = imports.get(source);
1005
- if (!imp) imp = {
1006
- from: source,
1007
- imports: [],
1008
- isTypeOnly: true,
1009
- kind: "named"
1010
- };
1011
- const isTypeOnly = [...entry.kinds].every((kind) => isTypeOnlyKind(kind));
1012
- if (entry.symbol.importKind === "namespace") {
1013
- imp.imports = [];
1014
- imp.kind = "namespace";
1015
- imp.localName = entry.symbol.finalName;
1016
- } else if (entry.symbol.importKind === "default") {
1017
- imp.kind = "default";
1018
- imp.localName = entry.symbol.finalName;
1019
- } else imp.imports.push({
1020
- isTypeOnly,
1021
- localName: entry.symbol.finalName,
1022
- sourceName: entry.dep.finalName
1023
- });
1024
- if (!isTypeOnly) imp.isTypeOnly = false;
1025
- imports.set(source, imp);
1026
- }
1027
- for (const [, imp] of imports) file.addImport(imp);
1028
- }
1029
- }
1030
- /**
1031
- * Assigns the final name to a top-level (file-scoped) symbol.
1032
- *
1033
- * Uses the symbol's file top-level names as the default scope,
1034
- * and updates all relevant name scopes including the file's allNames and local scopes.
1035
- *
1036
- * Supports optional overrides for the naming scope and scopes to update.
1037
- */
1038
- assignTopLevelName(args) {
1039
- if (!args.symbol.file) return;
1040
- this.assignSymbolName({
1041
- ...args,
1042
- file: args.symbol.file,
1043
- scope: args?.scope ?? createScope({ localNames: args.symbol.file.topLevelNames }),
1044
- scopesToUpdate: [
1045
- createScope({ localNames: args.symbol.file.allNames }),
1046
- args.ctx.scopes,
1047
- ...args?.scopesToUpdate ?? []
1048
- ]
1049
- });
1050
- }
1051
- /**
1052
- * Assigns the final name to a non-top-level (local) symbol.
1053
- *
1054
- * Uses the provided scope or derives it from the current analysis context's local names.
1055
- *
1056
- * Updates all provided name scopes accordingly.
1057
- */
1058
- assignLocalName(args) {
1059
- this.assignSymbolName({
1060
- ...args,
1061
- scope: args.scope ?? args.ctx.scope
1062
- });
1063
- }
1064
- /**
1065
- * Assigns the final name to a symbol within the provided name scope.
1066
- *
1067
- * Resolves name conflicts until a unique name is found.
1068
- *
1069
- * Updates all specified name scopes with the assigned final name.
1070
- */
1071
- assignSymbolName(args) {
1072
- const { ctx, file, node, scope, scopesToUpdate, symbol } = args;
1073
- if (this.cacheResolvedNames.has(symbol.id)) return;
1074
- const baseName = symbol.name;
1075
- let finalName = node?.nameSanitizer?.(baseName) ?? symbol.node?.nameSanitizer?.(baseName) ?? baseName;
1076
- let attempt = 1;
1077
- const localNames = ctx.localNames(scope);
1078
- while (true) {
1079
- if ([...localNames.get(finalName) ?? []].every((kind) => canShareName(symbol.kind, kind))) break;
1080
- const language = node?.language || symbol.node?.language || file.language;
1081
- const resolvedName = ((language ? this.project.nameConflictResolvers[language] : void 0) ?? this.project.defaultNameConflictResolver)({
1082
- attempt,
1083
- baseName
1084
- });
1085
- if (!resolvedName) throw new Error(`Unresolvable name conflict: ${symbol.toString()}`);
1086
- finalName = node?.nameSanitizer?.(resolvedName) ?? symbol.node?.nameSanitizer?.(resolvedName) ?? resolvedName;
1087
- attempt = attempt + 1;
1088
- }
1089
- symbol.setFinalName(finalName);
1090
- this.cacheResolvedNames.add(symbol.id);
1091
- const updateScopes = [scope, ...scopesToUpdate];
1092
- for (const scope$1 of updateScopes) this.updateScope(symbol, scope$1);
1093
- }
1094
- /**
1095
- * Updates the provided name scope with the symbol's final name and kind.
1096
- *
1097
- * Ensures the name scope tracks all kinds associated with a given name.
1098
- */
1099
- updateScope(symbol, scope) {
1100
- const name = symbol.finalName;
1101
- const cache = scope.localNames.get(name) ?? /* @__PURE__ */ new Set();
1102
- cache.add(symbol.kind);
1103
- scope.localNames.set(name, cache);
1104
- }
1105
- };
1106
-
1107
- //#endregion
1108
- //#region src/symbols/symbol.ts
1109
- var Symbol$1 = class {
1110
- /**
1111
- * Canonical symbol this stub resolves to, if any.
1112
- *
1113
- * Stubs created during DSL construction may later be associated
1114
- * with a fully registered symbol. Once set, all property lookups
1115
- * should defer to the canonical symbol.
1116
- */
1117
- _canonical;
1118
- /**
1119
- * True if this symbol is exported from its defining file.
1120
- *
1121
- * @default false
1122
- */
1123
- _exported;
1124
- /**
1125
- * Names of files (without extension) from which this symbol is re-exported.
1126
- *
1127
- * @default []
1128
- */
1129
- _exportFrom;
1130
- /**
1131
- * External module name if this symbol is imported from a module not managed
1132
- * by the project (e.g. "zod", "lodash").
1133
- *
1134
- * @default undefined
1135
- */
1136
- _external;
1137
- /**
1138
- * The file this symbol is ultimately emitted into.
1139
- *
1140
- * Only top-level symbols have an assigned file.
1141
- */
1142
- _file;
1143
- /**
1144
- * The alias-resolved, conflict-free emitted name.
1145
- */
1146
- _finalName;
1147
- /**
1148
- * Custom strategy to determine file output path.
1149
- *
1150
- * @returns The file path to output the symbol to, or undefined to fallback to default behavior.
1151
- */
1152
- _getFilePath;
1153
- /**
1154
- * How this symbol should be imported (namespace/default/named).
1155
- *
1156
- * @default 'named'
1157
- */
1158
- _importKind;
1159
- /**
1160
- * Kind of symbol (class, type, alias, etc.).
1161
- *
1162
- * @default 'var'
1163
- */
1164
- _kind;
1165
- /**
1166
- * Arbitrary user metadata.
1167
- *
1168
- * @default undefined
1169
- */
1170
- _meta;
1171
- /**
1172
- * Intended user-facing name before conflict resolution.
1173
- *
1174
- * @example "UserModel"
1175
- */
1176
- _name;
1177
- /**
1178
- * Node that defines this symbol.
1179
- */
1180
- _node;
1181
- /** Brand used for identifying symbols. */
1182
- "~brand" = symbolBrand;
1183
- /** Globally unique, stable symbol ID. */
1184
- id;
1185
- constructor(input, id) {
1186
- this._exported = input.exported ?? false;
1187
- this._exportFrom = input.exportFrom ?? [];
1188
- this._external = input.external;
1189
- this._getFilePath = input.getFilePath;
1190
- this.id = id;
1191
- this._importKind = input.importKind ?? "named";
1192
- this._kind = input.kind ?? "var";
1193
- this._meta = input.meta;
1194
- this._name = input.name;
1195
- }
1196
- /**
1197
- * Returns the canonical symbol for this instance.
1198
- *
1199
- * If this symbol was created as a stub, this getter returns
1200
- * the fully registered canonical symbol. Otherwise, it returns
1201
- * the symbol itself.
1202
- */
1203
- get canonical() {
1204
- return this._canonical ?? this;
1205
- }
1206
- /**
1207
- * Indicates whether this symbol is exported from its defining file.
1208
- */
1209
- get exported() {
1210
- return this.canonical._exported;
1211
- }
1212
- /**
1213
- * Names of files (without extension) that re-export this symbol.
1214
- */
1215
- get exportFrom() {
1216
- return this.canonical._exportFrom;
1217
- }
1218
- /**
1219
- * External module from which this symbol originates, if any.
1220
- */
1221
- get external() {
1222
- return this.canonical._external;
1223
- }
1224
- /**
1225
- * Read‑only accessor for the assigned output file.
1226
- *
1227
- * Only top-level symbols have an assigned file.
1228
- */
1229
- get file() {
1230
- return this.canonical._file;
1231
- }
1232
- /**
1233
- * Read‑only accessor for the resolved final emitted name.
1234
- */
1235
- get finalName() {
1236
- if (!this.canonical._finalName) {
1237
- const message = `Symbol finalName has not been resolved yet for ${this.canonical.toString()}`;
1238
- log.debug(message, "symbol");
1239
- throw new Error(message);
1240
- }
1241
- return this.canonical._finalName;
1242
- }
1243
- /**
1244
- * Custom file path resolver, if provided.
1245
- */
1246
- get getFilePath() {
1247
- return this.canonical._getFilePath;
1248
- }
1249
- /**
1250
- * How this symbol should be imported (named/default/namespace).
1251
- */
1252
- get importKind() {
1253
- return this.canonical._importKind;
1254
- }
1255
- /**
1256
- * Indicates whether this is a canonical symbol (not a stub).
1257
- */
1258
- get isCanonical() {
1259
- return !this._canonical || this._canonical === this;
1260
- }
1261
- /**
1262
- * The symbol's kind (class, type, alias, variable, etc.).
1263
- */
1264
- get kind() {
1265
- return this.canonical._kind;
1266
- }
1267
- /**
1268
- * Arbitrary user‑provided metadata associated with this symbol.
1269
- */
1270
- get meta() {
1271
- return this.canonical._meta;
1272
- }
1273
- /**
1274
- * User-intended name before aliasing or conflict resolution.
1275
- */
1276
- get name() {
1277
- return this.canonical._name;
1278
- }
1279
- /**
1280
- * Read‑only accessor for the defining node.
1281
- */
1282
- get node() {
1283
- return this.canonical._node;
1284
- }
1285
- /**
1286
- * Marks this symbol as a stub and assigns its canonical symbol.
1287
- *
1288
- * After calling this, all semantic queries (name, kind, file,
1289
- * meta, etc.) should reflect the canonical symbol's values.
1290
- *
1291
- * @param symbol — The canonical symbol this stub should resolve to.
1292
- */
1293
- setCanonical(symbol) {
1294
- this._canonical = symbol;
1295
- }
1296
- /**
1297
- * Marks the symbol as exported from its file.
1298
- *
1299
- * @param exported — Whether the symbol is exported.
1300
- */
1301
- setExported(exported) {
1302
- this.assertCanonical();
1303
- this._exported = exported;
1304
- }
1305
- /**
1306
- * Records file names that re‑export this symbol.
1307
- *
1308
- * @param list — Source files re‑exporting this symbol.
1309
- */
1310
- setExportFrom(list) {
1311
- this.assertCanonical();
1312
- this._exportFrom = list;
1313
- }
1314
- /**
1315
- * Assigns the output file this symbol will be emitted into.
1316
- *
1317
- * This may only be set once.
1318
- */
1319
- setFile(file) {
1320
- this.assertCanonical();
1321
- if (this._file && this._file !== file) {
1322
- const message = `Symbol ${this.canonical.toString()} is already assigned to a different file.`;
1323
- log.debug(message, "symbol");
1324
- throw new Error(message);
1325
- }
1326
- this._file = file;
1327
- }
1328
- /**
1329
- * Assigns the conflict‑resolved final local name for this symbol.
1330
- *
1331
- * This may only be set once.
1332
- */
1333
- setFinalName(name) {
1334
- this.assertCanonical();
1335
- if (this._finalName && this._finalName !== name) {
1336
- const message = `Symbol finalName has already been resolved for ${this.canonical.toString()}.`;
1337
- log.debug(message, "symbol");
1338
- throw new Error(message);
1339
- }
1340
- this._finalName = name;
1341
- }
1342
- /**
1343
- * Sets how this symbol should be imported.
1344
- *
1345
- * @param kind — The import strategy (named/default/namespace).
1346
- */
1347
- setImportKind(kind) {
1348
- this.assertCanonical();
1349
- this._importKind = kind;
1350
- }
1351
- /**
1352
- * Sets the symbol's kind (class, type, alias, variable, etc.).
1353
- *
1354
- * @param kind — The new symbol kind.
1355
- */
1356
- setKind(kind) {
1357
- this.assertCanonical();
1358
- this._kind = kind;
1359
- }
1360
- /**
1361
- * Updates the intended user‑facing name for this symbol.
1362
- *
1363
- * @param name — The new name.
1364
- */
1365
- setName(name) {
1366
- this.assertCanonical();
1367
- this._name = name;
1368
- }
1369
- /**
1370
- * Binds the node that defines this symbol.
1371
- *
1372
- * This may only be set once.
1373
- */
1374
- setNode(node) {
1375
- this.assertCanonical();
1376
- if (this._node && this._node !== node) {
1377
- const message = `Symbol ${this.canonical.toString()} is already bound to a different node.`;
1378
- log.debug(message, "symbol");
1379
- }
1380
- this._node = node;
1381
- node.symbol = this;
1382
- }
1383
- /**
1384
- * Returns a debug‑friendly string representation identifying the symbol.
1385
- */
1386
- toString() {
1387
- const canonical = this.canonical;
1388
- if (canonical._finalName && canonical._finalName !== canonical._name) return `[Symbol ${canonical._name} → ${canonical._finalName}#${canonical.id}]`;
1389
- return `[Symbol ${canonical._name}#${canonical.id}]`;
1390
- }
1391
- /**
1392
- * Ensures this symbol is canonical before allowing mutation.
1393
- *
1394
- * A symbol that has been marked as a stub (i.e., its `_canonical` points
1395
- * to a different symbol) may not be mutated. This guard throws an error
1396
- * if any setter attempts to modify a stub, preventing accidental writes
1397
- * to non‑canonical instances.
1398
- *
1399
- * @throws {Error} If the symbol is a stub and is being mutated.
1400
- */
1401
- assertCanonical() {
1402
- if (this._canonical && this._canonical !== this) {
1403
- const message = `Illegal mutation of stub symbol ${this.toString()} → canonical: ${this._canonical.toString()}`;
1404
- log.debug(message, "symbol");
1405
- throw new Error(message);
1406
- }
1407
- }
1408
- };
1409
-
1410
- //#endregion
1411
- //#region src/symbols/registry.ts
1412
- var SymbolRegistry = class {
1413
- _id = 0;
1414
- _indices = /* @__PURE__ */ new Map();
1415
- _queryCache = /* @__PURE__ */ new Map();
1416
- _queryCacheDependencies = /* @__PURE__ */ new Map();
1417
- _registered = /* @__PURE__ */ new Set();
1418
- _stubs = /* @__PURE__ */ new Set();
1419
- _stubCache = /* @__PURE__ */ new Map();
1420
- _values = /* @__PURE__ */ new Map();
1421
- get(identifier) {
1422
- return typeof identifier === "number" ? this._values.get(identifier) : this.query(identifier)[0];
1423
- }
1424
- isRegistered(identifier) {
1425
- const symbol = this.get(identifier);
1426
- return symbol ? this._registered.has(symbol.id) : false;
1427
- }
1428
- get nextId() {
1429
- return this._id++;
1430
- }
1431
- query(filter) {
1432
- const cacheKey = this.buildCacheKey(filter);
1433
- const cachedIds = this._queryCache.get(cacheKey);
1434
- if (cachedIds) return cachedIds.map((symbolId) => this._values.get(symbolId));
1435
- const sets = [];
1436
- const indexKeySpace = this.buildIndexKeySpace(filter);
1437
- const cacheDependencies = /* @__PURE__ */ new Set();
1438
- let missed = false;
1439
- for (const indexEntry of indexKeySpace) {
1440
- cacheDependencies.add(this.serializeIndexEntry(indexEntry));
1441
- const values = this._indices.get(indexEntry[0]);
1442
- if (!values) {
1443
- missed = true;
1444
- break;
1445
- }
1446
- const set = values.get(indexEntry[1]);
1447
- if (!set) {
1448
- missed = true;
1449
- break;
1450
- }
1451
- sets.push(set);
1452
- }
1453
- if (missed || !sets.length) {
1454
- this._queryCacheDependencies.set(cacheKey, cacheDependencies);
1455
- this._queryCache.set(cacheKey, []);
1456
- return [];
1457
- }
1458
- let result = new Set(sets[0]);
1459
- for (const set of sets.slice(1)) result = new Set([...result].filter((symbolId) => set.has(symbolId)));
1460
- const resultIds = [...result];
1461
- this._queryCacheDependencies.set(cacheKey, cacheDependencies);
1462
- this._queryCache.set(cacheKey, resultIds);
1463
- return resultIds.map((symbolId) => this._values.get(symbolId));
1464
- }
1465
- reference(meta) {
1466
- const [registered] = this.query(meta);
1467
- if (registered) return registered;
1468
- const cacheKey = this.buildCacheKey(meta);
1469
- const cachedId = this._stubCache.get(cacheKey);
1470
- if (cachedId !== void 0) return this._values.get(cachedId);
1471
- const stub = new Symbol$1({
1472
- meta,
1473
- name: ""
1474
- }, this.nextId);
1475
- this._values.set(stub.id, stub);
1476
- this._stubs.add(stub.id);
1477
- this._stubCache.set(cacheKey, stub.id);
1478
- return stub;
1479
- }
1480
- register(symbol) {
1481
- const result = new Symbol$1(symbol, this.nextId);
1482
- this._values.set(result.id, result);
1483
- this._registered.add(result.id);
1484
- if (result.meta) {
1485
- const indexKeySpace = this.buildIndexKeySpace(result.meta);
1486
- this.indexSymbol(result.id, indexKeySpace);
1487
- this.invalidateCache(indexKeySpace);
1488
- this.replaceStubs(result, indexKeySpace);
1489
- }
1490
- return result;
1491
- }
1492
- *registered() {
1493
- for (const id of this._registered.values()) yield this._values.get(id);
1494
- }
1495
- buildCacheKey(filter) {
1496
- return this.buildIndexKeySpace(filter).map((indexEntry) => this.serializeIndexEntry(indexEntry)).sort().join("|");
1497
- }
1498
- buildIndexKeySpace(meta, prefix = "") {
1499
- const entries = [];
1500
- for (const [key, value] of Object.entries(meta)) {
1501
- const path$4 = prefix ? `${prefix}.${key}` : key;
1502
- if (value && typeof value === "object" && !Array.isArray(value)) entries.push(...this.buildIndexKeySpace(value, path$4));
1503
- else entries.push([path$4, value]);
1504
- }
1505
- return entries;
1506
- }
1507
- indexSymbol(symbolId, indexKeySpace) {
1508
- for (const [key, value] of indexKeySpace) {
1509
- if (!this._indices.has(key)) this._indices.set(key, /* @__PURE__ */ new Map());
1510
- const values = this._indices.get(key);
1511
- const set = values.get(value) ?? /* @__PURE__ */ new Set();
1512
- set.add(symbolId);
1513
- values.set(value, set);
1514
- }
1515
- }
1516
- invalidateCache(indexKeySpace) {
1517
- const changed = indexKeySpace.map((indexEntry) => this.serializeIndexEntry(indexEntry));
1518
- for (const [cacheKey, cacheDependencies] of this._queryCacheDependencies.entries()) for (const key of changed) if (cacheDependencies.has(key)) {
1519
- this._queryCacheDependencies.delete(cacheKey);
1520
- this._queryCache.delete(cacheKey);
1521
- break;
1522
- }
1523
- }
1524
- isSubset(sub, sup) {
1525
- const supMap = new Map(sup);
1526
- for (const [key, value] of sub) if (!supMap.has(key) || supMap.get(key) !== value) return false;
1527
- return true;
1528
- }
1529
- replaceStubs(symbol, indexKeySpace) {
1530
- for (const stubId of this._stubs.values()) {
1531
- const stub = this._values.get(stubId);
1532
- if (stub?.meta && this.isSubset(this.buildIndexKeySpace(stub.meta), indexKeySpace)) {
1533
- const cacheKey = this.buildCacheKey(stub.meta);
1534
- this._stubCache.delete(cacheKey);
1535
- this._stubs.delete(stubId);
1536
- stub.setCanonical(symbol);
1537
- }
1538
- }
1539
- }
1540
- serializeIndexEntry(indexEntry) {
1541
- return `${indexEntry[0]}:${JSON.stringify(indexEntry[1])}`;
1542
- }
1543
- };
1544
-
1545
- //#endregion
1546
- //#region src/project/project.ts
1547
- var Project = class {
1548
- _isPlanned = false;
1549
- files;
1550
- nodes = new NodeRegistry();
1551
- symbols = new SymbolRegistry();
1552
- defaultFileName;
1553
- defaultNameConflictResolver;
1554
- extensions;
1555
- fileName;
1556
- nameConflictResolvers;
1557
- renderers;
1558
- root;
1559
- constructor(args) {
1560
- const fileName = args.fileName;
1561
- this.defaultFileName = args.defaultFileName ?? "main";
1562
- this.defaultNameConflictResolver = args.defaultNameConflictResolver ?? simpleNameConflictResolver;
1563
- this.extensions = {
1564
- ...defaultExtensions,
1565
- ...args.extensions
1566
- };
1567
- this.fileName = typeof fileName === "string" ? () => fileName : fileName;
1568
- this.files = new FileRegistry(this);
1569
- this.nameConflictResolvers = {
1570
- ...defaultNameConflictResolvers,
1571
- ...args.nameConflictResolvers
1572
- };
1573
- this.renderers = args.renderers ?? [];
1574
- this.root = node_path.default.resolve(args.root).replace(/[/\\]+$/, "");
1575
- }
1576
- plan(meta) {
1577
- if (this._isPlanned) return;
1578
- new Planner(this).plan(meta);
1579
- this._isPlanned = true;
1580
- }
1581
- render(meta) {
1582
- if (!this._isPlanned) this.plan(meta);
1583
- const files = [];
1584
- for (const file of this.files.registered()) if (!file.external && file.finalPath && file.renderer) {
1585
- const content = file.renderer.render({
1586
- file,
1587
- meta,
1588
- project: this
1589
- });
1590
- files.push({
1591
- content,
1592
- path: file.finalPath
1593
- });
1594
- }
1595
- return files;
1596
- }
1597
- };
1598
-
1599
- //#endregion
1600
- //#region src/structure/node.ts
1601
- var StructureNode = class StructureNode {
1602
- /** Nested nodes within this node. */
1603
- children = /* @__PURE__ */ new Map();
1604
- /** Items contained in this node. */
1605
- items = [];
1606
- /** The name of this node (e.g., "Users", "Accounts"). */
1607
- name;
1608
- /** Parent node in the hierarchy. Undefined if this is the root node. */
1609
- parent;
1610
- /** Shell claimed for this node. */
1611
- shell;
1612
- /** Source of the claimed shell. */
1613
- shellSource;
1614
- /** True if this is a virtual root. */
1615
- virtual;
1616
- constructor(name, parent, options) {
1617
- this.name = name;
1618
- this.parent = parent;
1619
- this.virtual = options?.virtual ?? false;
1620
- }
1621
- get isRoot() {
1622
- return !this.parent;
1623
- }
1624
- /**
1625
- * Gets or creates a child node.
1626
- *
1627
- * If the child doesn't exist, it's created automatically.
1628
- *
1629
- * @param name - The name of the child node
1630
- * @returns The child node instance
1631
- */
1632
- child(name) {
1633
- if (!this.children.has(name)) this.children.set(name, new StructureNode(name, this));
1634
- return this.children.get(name);
1635
- }
1636
- /**
1637
- * Gets the full path of this node in the hierarchy.
1638
- *
1639
- * @returns An array of node names from the root to this node
1640
- */
1641
- getPath() {
1642
- const path$4 = [];
1643
- let cursor = this;
1644
- while (cursor) {
1645
- path$4.unshift(cursor.name);
1646
- cursor = cursor.parent;
1647
- }
1648
- return path$4;
1649
- }
1650
- /**
1651
- * Yields items from a specific source with typed data.
1652
- *
1653
- * @param source - The source symbol to filter by
1654
- * @returns Generator of items from that source
1655
- */
1656
- *itemsFrom(source) {
1657
- for (const item of this.items) if (item.source === source) yield item;
1658
- }
1659
- /**
1660
- * Walk all nodes in the structure (depth-first, post-order).
1661
- *
1662
- * @returns Generator of all structure nodes
1663
- */
1664
- *walk() {
1665
- for (const node of this.children.values()) yield* node.walk();
1666
- yield this;
1667
- }
1668
- };
1669
-
1670
- //#endregion
1671
- //#region src/structure/model.ts
1672
- var StructureModel = class {
1673
- /** Root nodes mapped by their names. */
1674
- _roots = /* @__PURE__ */ new Map();
1675
- /** Node for data without a specific root. */
1676
- _virtualRoot;
1677
- /**
1678
- * Get all root nodes.
1679
- */
1680
- get roots() {
1681
- const roots = Array.from(this._roots.values());
1682
- if (this._virtualRoot) roots.unshift(this._virtualRoot);
1683
- return roots;
1684
- }
1685
- /**
1686
- * Insert data into the structure.
1687
- */
1688
- insert(args) {
1689
- const { data, locations, source } = args;
1690
- for (const location of locations) {
1691
- const { path: path$4, shell } = location;
1692
- const fullPath = path$4.filter((s) => Boolean(s));
1693
- const segments = fullPath.slice(0, -1);
1694
- if (!fullPath[fullPath.length - 1]) throw new Error("Cannot insert data without path.");
1695
- let cursor = null;
1696
- for (const segment of segments) {
1697
- if (!cursor) cursor = this.root(segment);
1698
- else cursor = cursor.child(segment);
1699
- if (shell && !cursor.shell) {
1700
- cursor.shell = shell;
1701
- cursor.shellSource = source;
1702
- }
1703
- }
1704
- if (!cursor) cursor = this.root(null);
1705
- cursor.items.push({
1706
- data,
1707
- location: fullPath,
1708
- source
1709
- });
1710
- }
1711
- }
1712
- /**
1713
- * Gets or creates a root by name.
1714
- *
1715
- * If the root doesn't exist, it's created automatically.
1716
- *
1717
- * @param name - The name of the root
1718
- * @returns The root instance
1719
- */
1720
- root(name) {
1721
- if (!name) return this._virtualRoot ??= new StructureNode("", void 0, { virtual: true });
1722
- if (!this._roots.has(name)) this._roots.set(name, new StructureNode(name));
1723
- return this._roots.get(name);
1724
- }
1725
- /**
1726
- * Walk all nodes in the structure (depth-first, post-order).
1727
- *
1728
- * @returns Generator of all structure nodes
1729
- */
1730
- *walk() {
1731
- if (this._virtualRoot) yield* this._virtualRoot.walk();
1732
- for (const root of this._roots.values()) yield* root.walk();
1733
- }
1734
- };
1735
-
1736
- //#endregion
1737
- exports.File = File;
1738
- exports.Logger = Logger;
1739
- exports.Project = Project;
1740
- exports.StructureModel = StructureModel;
1741
- exports.StructureNode = StructureNode;
1742
- exports.Symbol = Symbol$1;
1743
- exports.defaultExtensions = defaultExtensions;
1744
- exports.defaultNameConflictResolvers = defaultNameConflictResolvers;
1745
- exports.fromRef = fromRef;
1746
- exports.fromRefs = fromRefs;
1747
- exports.isNode = isNode;
1748
- exports.isNodeRef = isNodeRef;
1749
- exports.isRef = isRef;
1750
- exports.isSymbol = isSymbol;
1751
- exports.isSymbolRef = isSymbolRef;
1752
- exports.log = log;
1753
- exports.nodeBrand = nodeBrand;
1754
- exports.ref = ref;
1755
- exports.refs = refs;
1756
- exports.simpleNameConflictResolver = simpleNameConflictResolver;
1757
- exports.symbolBrand = symbolBrand;
1758
- exports.underscoreNameConflictResolver = underscoreNameConflictResolver;
1759
- //# sourceMappingURL=index.cjs.map