@velarscript/compiler 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +70 -0
  3. package/dist/analyzer.d.ts +1175 -0
  4. package/dist/analyzer.d.ts.map +1 -0
  5. package/dist/analyzer.js +12130 -0
  6. package/dist/analyzer.js.map +1 -0
  7. package/dist/application-package-host.d.ts +28 -0
  8. package/dist/application-package-host.d.ts.map +1 -0
  9. package/dist/application-package-host.js +2 -0
  10. package/dist/application-package-host.js.map +1 -0
  11. package/dist/ast.d.ts +853 -0
  12. package/dist/ast.d.ts.map +1 -0
  13. package/dist/ast.js +245 -0
  14. package/dist/ast.js.map +1 -0
  15. package/dist/class-runtime.d.ts +6 -0
  16. package/dist/class-runtime.d.ts.map +1 -0
  17. package/dist/class-runtime.js +45 -0
  18. package/dist/class-runtime.js.map +1 -0
  19. package/dist/collection-lowering-runtime.d.ts +7 -0
  20. package/dist/collection-lowering-runtime.d.ts.map +1 -0
  21. package/dist/collection-lowering-runtime.js +952 -0
  22. package/dist/collection-lowering-runtime.js.map +1 -0
  23. package/dist/collection-runtime.d.ts +16 -0
  24. package/dist/collection-runtime.d.ts.map +1 -0
  25. package/dist/collection-runtime.js +231 -0
  26. package/dist/collection-runtime.js.map +1 -0
  27. package/dist/core-vocabulary.d.ts +141 -0
  28. package/dist/core-vocabulary.d.ts.map +1 -0
  29. package/dist/core-vocabulary.js +88 -0
  30. package/dist/core-vocabulary.js.map +1 -0
  31. package/dist/diagnostic.d.ts +42 -0
  32. package/dist/diagnostic.d.ts.map +1 -0
  33. package/dist/diagnostic.js +66 -0
  34. package/dist/diagnostic.js.map +1 -0
  35. package/dist/embedded-javascript.d.ts +74 -0
  36. package/dist/embedded-javascript.d.ts.map +1 -0
  37. package/dist/embedded-javascript.js +341 -0
  38. package/dist/embedded-javascript.js.map +1 -0
  39. package/dist/embedded-module.d.ts +11 -0
  40. package/dist/embedded-module.d.ts.map +1 -0
  41. package/dist/embedded-module.js +67 -0
  42. package/dist/embedded-module.js.map +1 -0
  43. package/dist/embedded-source.d.ts +19 -0
  44. package/dist/embedded-source.d.ts.map +1 -0
  45. package/dist/embedded-source.js +61 -0
  46. package/dist/embedded-source.js.map +1 -0
  47. package/dist/emitter.d.ts +188 -0
  48. package/dist/emitter.d.ts.map +1 -0
  49. package/dist/emitter.js +3254 -0
  50. package/dist/emitter.js.map +1 -0
  51. package/dist/error-runtime.d.ts +17 -0
  52. package/dist/error-runtime.d.ts.map +1 -0
  53. package/dist/error-runtime.js +109 -0
  54. package/dist/error-runtime.js.map +1 -0
  55. package/dist/extension.d.ts +373 -0
  56. package/dist/extension.d.ts.map +1 -0
  57. package/dist/extension.js +25 -0
  58. package/dist/extension.js.map +1 -0
  59. package/dist/formatter.d.ts +13 -0
  60. package/dist/formatter.d.ts.map +1 -0
  61. package/dist/formatter.js +1440 -0
  62. package/dist/formatter.js.map +1 -0
  63. package/dist/framework-host.d.ts +87 -0
  64. package/dist/framework-host.d.ts.map +1 -0
  65. package/dist/framework-host.js +2 -0
  66. package/dist/framework-host.js.map +1 -0
  67. package/dist/index.d.ts +75 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +932 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/interpolated-string.d.ts +47 -0
  72. package/dist/interpolated-string.d.ts.map +1 -0
  73. package/dist/interpolated-string.js +344 -0
  74. package/dist/interpolated-string.js.map +1 -0
  75. package/dist/json-runtime.d.ts +2 -0
  76. package/dist/json-runtime.d.ts.map +1 -0
  77. package/dist/json-runtime.js +193 -0
  78. package/dist/json-runtime.js.map +1 -0
  79. package/dist/language-guidance.d.ts +31 -0
  80. package/dist/language-guidance.d.ts.map +1 -0
  81. package/dist/language-guidance.js +187 -0
  82. package/dist/language-guidance.js.map +1 -0
  83. package/dist/lexer.d.ts +85 -0
  84. package/dist/lexer.d.ts.map +1 -0
  85. package/dist/lexer.js +1004 -0
  86. package/dist/lexer.js.map +1 -0
  87. package/dist/limits.d.ts +2 -0
  88. package/dist/limits.d.ts.map +1 -0
  89. package/dist/limits.js +2 -0
  90. package/dist/limits.js.map +1 -0
  91. package/dist/mechanical-fix.d.ts +26 -0
  92. package/dist/mechanical-fix.d.ts.map +1 -0
  93. package/dist/mechanical-fix.js +61 -0
  94. package/dist/mechanical-fix.js.map +1 -0
  95. package/dist/narrowing-runtime.d.ts +5 -0
  96. package/dist/narrowing-runtime.d.ts.map +1 -0
  97. package/dist/narrowing-runtime.js +27 -0
  98. package/dist/narrowing-runtime.js.map +1 -0
  99. package/dist/number-runtime.d.ts +3 -0
  100. package/dist/number-runtime.d.ts.map +1 -0
  101. package/dist/number-runtime.js +37 -0
  102. package/dist/number-runtime.js.map +1 -0
  103. package/dist/parser.d.ts +245 -0
  104. package/dist/parser.d.ts.map +1 -0
  105. package/dist/parser.js +3582 -0
  106. package/dist/parser.js.map +1 -0
  107. package/dist/primitive-runtime.d.ts +4 -0
  108. package/dist/primitive-runtime.d.ts.map +1 -0
  109. package/dist/primitive-runtime.js +39 -0
  110. package/dist/primitive-runtime.js.map +1 -0
  111. package/dist/promise-runtime.d.ts +6 -0
  112. package/dist/promise-runtime.d.ts.map +1 -0
  113. package/dist/promise-runtime.js +86 -0
  114. package/dist/promise-runtime.js.map +1 -0
  115. package/dist/reactive-bridge-runtime.d.ts +7 -0
  116. package/dist/reactive-bridge-runtime.d.ts.map +1 -0
  117. package/dist/reactive-bridge-runtime.js +112 -0
  118. package/dist/reactive-bridge-runtime.js.map +1 -0
  119. package/dist/runtime-abi.d.ts +24 -0
  120. package/dist/runtime-abi.d.ts.map +1 -0
  121. package/dist/runtime-abi.js +24 -0
  122. package/dist/runtime-abi.js.map +1 -0
  123. package/dist/semantic.d.ts +142 -0
  124. package/dist/semantic.d.ts.map +1 -0
  125. package/dist/semantic.js +845 -0
  126. package/dist/semantic.js.map +1 -0
  127. package/dist/source-names.d.ts +32 -0
  128. package/dist/source-names.d.ts.map +1 -0
  129. package/dist/source-names.js +101 -0
  130. package/dist/source-names.js.map +1 -0
  131. package/dist/source.d.ts +19 -0
  132. package/dist/source.d.ts.map +1 -0
  133. package/dist/source.js +64 -0
  134. package/dist/source.js.map +1 -0
  135. package/dist/text-runtime.d.ts +2 -0
  136. package/dist/text-runtime.d.ts.map +1 -0
  137. package/dist/text-runtime.js +285 -0
  138. package/dist/text-runtime.js.map +1 -0
  139. package/dist/token.d.ts +19 -0
  140. package/dist/token.d.ts.map +1 -0
  141. package/dist/token.js +51 -0
  142. package/dist/token.js.map +1 -0
  143. package/dist/type-registry-runtime.d.ts +7 -0
  144. package/dist/type-registry-runtime.d.ts.map +1 -0
  145. package/dist/type-registry-runtime.js +51 -0
  146. package/dist/type-registry-runtime.js.map +1 -0
  147. package/dist/type-validation-runtime.d.ts +17 -0
  148. package/dist/type-validation-runtime.d.ts.map +1 -0
  149. package/dist/type-validation-runtime.js +150 -0
  150. package/dist/type-validation-runtime.js.map +1 -0
  151. package/dist/types.d.ts +356 -0
  152. package/dist/types.d.ts.map +1 -0
  153. package/dist/types.js +1170 -0
  154. package/dist/types.js.map +1 -0
  155. package/dist/utf8-runtime.d.ts +7 -0
  156. package/dist/utf8-runtime.d.ts.map +1 -0
  157. package/dist/utf8-runtime.js +43 -0
  158. package/dist/utf8-runtime.js.map +1 -0
  159. package/package.json +48 -0
package/dist/index.js ADDED
@@ -0,0 +1,932 @@
1
+ import { Analyzer, inferredResultPlaceholderType, isCorePrimitiveName } from "./analyzer.js";
2
+ import { astNodesOfKind, blockContainsDirectAwait, testFunctionName } from "./ast.js";
3
+ import { diagnostic } from "./diagnostic.js";
4
+ import { JavaScriptEmitter } from "./emitter.js";
5
+ import { programWithEmbeddedJavaScriptImports } from "./embedded-module.js";
6
+ import { Lexer } from "./lexer.js";
7
+ import { isParserComplexityFailure, Parser } from "./parser.js";
8
+ import { SourceText } from "./source.js";
9
+ import { bindingNameRestriction, memberNameRestriction } from "./source-names.js";
10
+ import { buildSemanticIndex } from "./semantic.js";
11
+ import { MAX_VELAR_SOURCE_CODE_UNITS } from "./limits.js";
12
+ import { bindNamedTypeParameters, boolType, genericApplicationType, invalidType, isTypeParameterBound, mergeTypes, nullType, numberType, optionalOf, resolveTypeReference, readonlyViewOf, resolvedAsyncType, stringType, unknownType, } from "./types.js";
13
+ export { diagnostic, formatDiagnostic, mechanicalEdits, mechanicalFix } from "./diagnostic.js";
14
+ export { applyMechanicalFixes } from "./mechanical-fix.js";
15
+ export { formatSource } from "./formatter.js";
16
+ export { collectionMemberGuidance, removedStandardFunctionGuidance, sourceTypeNameGuidance } from "./language-guidance.js";
17
+ export { SourceText } from "./source.js";
18
+ export { MAX_VELAR_SOURCE_CODE_UNITS } from "./limits.js";
19
+ export { bindingNameRestriction, isCoreReservedBinding, isForbiddenPrototypeMember, isJavaScriptReservedBinding, isSourceIdentifierPart, isSourceIdentifierStart, isValidSourceIdentifier, memberNameRestriction } from "./source-names.js";
20
+ export { VELAR_EXTENSION_PROTOCOL_VERSION } from "./extension.js";
21
+ export { CORE_EXPRESSION_CONSTRUCTS, CORE_STATEMENT_CONSTRUCTS, coreStatementConstructKey } from "./ast.js";
22
+ // D62 rule 157: the editor's keyword list is the lexer's table plus Core's
23
+ // contextual roster, so it is published rather than retyped downstream.
24
+ export { keywordKinds } from "./token.js";
25
+ export { semanticImportAt, semanticModuleReferenceAt, semanticSymbolAt, semanticVisibleSymbolsAt } from "./semantic.js";
26
+ export { analysisTypeIdentity, binaryStorageKind, describeType, genericApplicationType, isReadonlyView, optionalOf, readonlyViewOf, semanticTypeIdentity, unionOf, VELAR_BYTES_TYPE_IDENTITY, VELAR_FLOAT32_BUFFER_TYPE_IDENTITY, VELAR_UINT8_BUFFER_TYPE_IDENTITY, VELAR_UINT16_BUFFER_TYPE_IDENTITY, VELAR_UINT32_BUFFER_TYPE_IDENTITY } from "./types.js";
27
+ export { permanentNamespaceCoveringModule } from "./analyzer.js";
28
+ export { CORE_CONTEXTUAL_KEYWORD_WORDS, CORE_CONTEXTUAL_KEYWORDS, CORE_NUMERIC_SUFFIXES, CORE_PRELUDE_NAMES, CORE_STATEMENT_HEAD_KEYWORDS, CORE_VOCABULARY_NAMES, CORE_WORDS, PERMANENT_NAMESPACE_NAMES, TYPE_PARAMETER_DECLARATION_FORMS, typeParameterDeclarationFormsPhrase, } from "./core-vocabulary.js";
29
+ export function inspectModule(text, options = {}) {
30
+ const extensions = normalizedExtensions(options.extensions ?? []);
31
+ const parsed = parseModule(text, options.path ?? "<source>", extensions);
32
+ const semanticProgram = programWithEmbeddedJavaScriptImports(parsed.program, parsed.source.path);
33
+ return {
34
+ diagnostics: parsed.diagnostics,
35
+ source: parsed.source,
36
+ dependencies: dependenciesOf(parsed.program),
37
+ resources: resourcesOf(parsed.program, extensions),
38
+ moduleInterface: interfaceOf(semanticProgram, parsed.source.path, extensions),
39
+ semanticIndex: buildSemanticIndex(semanticProgram, parsed.source, new Map(), new Map(), new Map(), new Map(), new Map(), new Map(), new Map(), new Map(), new Map(), new Map(), extensions.flatMap((extension) => extension.semantic ? [extension.semantic] : [])),
40
+ };
41
+ }
42
+ export function compile(text, options = {}) {
43
+ try {
44
+ return compileUnchecked(text, options);
45
+ }
46
+ catch (error) {
47
+ if (!isJavaScriptStackOverflow(error))
48
+ throw error;
49
+ return complexityFailureResult(text, options);
50
+ }
51
+ }
52
+ function compileUnchecked(text, options) {
53
+ const extensions = normalizedExtensions(options.extensions ?? []);
54
+ const parsed = parseModule(text, options.path ?? "<source>", extensions);
55
+ const semanticProgram = programWithEmbeddedJavaScriptImports(parsed.program, parsed.source.path);
56
+ const diagnostics = [...parsed.diagnostics];
57
+ const analysisExtensions = extensions.flatMap((extension) => extension.analysis ? [extension.analysis] : []);
58
+ const analyzerExtensions = extensions.filter((extension) => extension.analyzer);
59
+ if (analyzerExtensions.length > 1)
60
+ throw new Error("Only one compiler extension may own semantic analysis");
61
+ const analysisResources = options.resourceContents ?? options.analysis?.resources;
62
+ const analysisContext = {
63
+ ...options.analysis,
64
+ path: parsed.source.path,
65
+ ...(analysisResources ? { resources: analysisResources } : {}),
66
+ };
67
+ const createAnalyzer = (inferredFunctionResults = new Map(), finalizeFunctionResultInference = false) => {
68
+ const context = {
69
+ ...analysisContext,
70
+ inferredFunctionResults,
71
+ finalizeFunctionResultInference,
72
+ };
73
+ return analyzerExtensions[0]?.analyzer?.create(context, analysisExtensions)
74
+ ?? new Analyzer(context, analysisExtensions);
75
+ };
76
+ let analyzer = createAnalyzer();
77
+ // Semantic analysis also runs when every earlier diagnostic is a guidance
78
+ // diagnostic that recovered as the guided spelling, so lexer-, parser-, and
79
+ // analyzer-level guidance co-reports in one compile. Compilation still
80
+ // fails: the emission gate below requires zero diagnostics.
81
+ if (diagnostics.every((item) => item.recovered)) {
82
+ // Omitted results use isolated semantic passes so forward and recursive
83
+ // calls converge before the one authoritative diagnostic/lowering pass.
84
+ // Intermediate diagnostics are intentionally discarded.
85
+ const initialDiagnostics = analyzer.analyze(semanticProgram);
86
+ let inferredResults = analyzer.inferredFunctionResults();
87
+ if (inferredResults.size === 0) {
88
+ diagnostics.push(...initialDiagnostics);
89
+ }
90
+ else {
91
+ const maximumPasses = Math.min(Math.max(inferredResults.size + 2, 4), 256);
92
+ for (let pass = 0; pass < maximumPasses; pass += 1) {
93
+ const probe = createAnalyzer(inferredResults);
94
+ probe.analyze(semanticProgram);
95
+ const next = probe.inferredFunctionResults();
96
+ const stable = Analyzer.inferredFunctionResultsMatch(inferredResults, next);
97
+ inferredResults = next;
98
+ if (stable)
99
+ break;
100
+ }
101
+ analyzer = createAnalyzer(inferredResults, true);
102
+ diagnostics.push(...analyzer.analyze(semanticProgram));
103
+ }
104
+ }
105
+ diagnostics.sort((left, right) => left.span.start - right.span.start || left.code.localeCompare(right.code));
106
+ const emitterExtensions = extensions.filter((extension) => extension.createEmitter);
107
+ if (emitterExtensions.length > 1)
108
+ throw new Error("Only one compiler extension may own JavaScript emission");
109
+ const emitterOptions = {
110
+ sourcePath: parsed.source.path,
111
+ ...(options.sharedRuntimeModules === undefined ? {} : { sharedRuntimeModules: options.sharedRuntimeModules }),
112
+ };
113
+ const emitter = emitterExtensions[0]?.createEmitter?.(analyzer.loweringHints(), options.exportFunctions ?? new Set(), options.resourceContents ?? new Map(), options.analysis?.extensionImports ?? new Map(), emitterOptions)
114
+ ?? new JavaScriptEmitter(analyzer.loweringHints(), options.exportFunctions, emitterOptions);
115
+ const code = diagnostics.length === 0 ? emitter.emit(parsed.program) : null;
116
+ const sourceMap = code === null ? null : emitter.sourceMap(parsed.source);
117
+ const embeddedModules = code === null ? [] : emitter.embeddedModules?.(parsed.source) ?? [];
118
+ const css = code === null ? null : emitter.css?.() ?? null;
119
+ const styleSegments = code === null ? null : emitter.styleSegments?.() ?? null;
120
+ const runtimeModules = code === null ? [] : emitter.runtimeModules?.() ?? [];
121
+ const semanticExpressions = analyzer.semanticExpressions();
122
+ const semanticIndex = buildSemanticIndex(semanticProgram, parsed.source, analyzer.semanticTypes(), analyzer.semanticMembers(), semanticExpressions.types, semanticExpressions.members, semanticExpressions.owners, semanticExpressions.objectPropertyOwners, semanticExpressions.bindingEntryOwners, semanticExpressions.jsxAttributeOwners, semanticExpressions.contexts, semanticExpressions.contextMembers, extensions.flatMap((extension) => extension.semantic ? [extension.semantic] : []));
123
+ return {
124
+ code,
125
+ sourceMap,
126
+ embeddedModules,
127
+ css,
128
+ styleSegments,
129
+ runtimeModules,
130
+ extensions: extensions.map((extension) => extension.id),
131
+ diagnostics,
132
+ source: parsed.source,
133
+ dependencies: dependenciesOf(parsed.program),
134
+ resources: resourcesOf(parsed.program, extensions),
135
+ moduleInterface: interfaceOf(semanticProgram, parsed.source.path, extensions, analyzer.semanticTypes(), analyzer.analyzedClasses()),
136
+ semanticIndex,
137
+ initializationImportReads: analyzer.moduleInitializationImportReads(),
138
+ };
139
+ }
140
+ function complexityFailureResult(text, options) {
141
+ const path = options.path ?? "<source>";
142
+ const extensions = normalizedExtensions(options.extensions ?? []);
143
+ const source = new SourceText(path, text);
144
+ const program = { kind: "Program", body: [], span: { start: 0, end: 0 } };
145
+ return {
146
+ code: null,
147
+ sourceMap: null,
148
+ embeddedModules: [],
149
+ css: null,
150
+ styleSegments: null,
151
+ runtimeModules: [],
152
+ extensions: extensions.map((extension) => extension.id),
153
+ diagnostics: [diagnostic("VEL2008", "VelarScript source nesting is too complex to process safely", { start: 0, end: Math.min(1, text.length) })],
154
+ source,
155
+ dependencies: [],
156
+ resources: [],
157
+ moduleInterface: interfaceOf(program, path, extensions),
158
+ semanticIndex: buildSemanticIndex(program, source),
159
+ initializationImportReads: [],
160
+ };
161
+ }
162
+ function resourcesOf(program, extensions) {
163
+ const output = [];
164
+ const seen = new Set();
165
+ for (const extension of extensions) {
166
+ for (const resource of extension.inspection?.resources?.(program) ?? []) {
167
+ const key = `${resource.kind}\0${resource.source}`;
168
+ if (seen.has(key))
169
+ continue;
170
+ seen.add(key);
171
+ output.push(resource);
172
+ }
173
+ }
174
+ return output;
175
+ }
176
+ function parseModule(text, path, extensions) {
177
+ if (text.length > MAX_VELAR_SOURCE_CODE_UNITS) {
178
+ const source = new SourceText(path, text, false);
179
+ return {
180
+ source,
181
+ program: { kind: "Program", body: [], span: { start: 0, end: 0 } },
182
+ diagnostics: [diagnostic("VEL1003", `A VelarScript source module cannot exceed ${MAX_VELAR_SOURCE_CODE_UNITS / 1024 / 1024} MiB`, { start: 0, end: Math.min(1, text.length) })],
183
+ };
184
+ }
185
+ const source = new SourceText(path, text);
186
+ try {
187
+ const lexicalExtensions = extensions.flatMap((extension) => extension.lexical ? [extension.lexical] : []);
188
+ const lexed = new Lexer(text, lexicalExtensions).lex();
189
+ // Guidance diagnostics that recovered as the guided token stream do not
190
+ // suppress parsing; only unrecovered lexical failures gate the parser.
191
+ if (lexed.diagnostics.some((item) => (item.code === "VEL1005" || item.code === "VEL1006") && !item.recovered)) {
192
+ return {
193
+ source,
194
+ program: { kind: "Program", body: [], span: { start: 0, end: text.length } },
195
+ diagnostics: lexed.diagnostics,
196
+ };
197
+ }
198
+ const parserExtensions = extensions.filter((extension) => extension.parser);
199
+ if (parserExtensions.length > 1)
200
+ throw new Error("Only one compiler extension may own syntax parsing");
201
+ const parser = parserExtensions[0]?.parser?.create(lexed.tokens, lexicalExtensions)
202
+ ?? new Parser(lexed.tokens, lexicalExtensions);
203
+ const parsed = parser.parse();
204
+ return { source, program: parsed.program, diagnostics: [...lexed.diagnostics, ...parsed.diagnostics] };
205
+ }
206
+ catch (error) {
207
+ if (!isParserComplexityFailure(error) && !isJavaScriptStackOverflow(error))
208
+ throw error;
209
+ return {
210
+ source,
211
+ program: { kind: "Program", body: [], span: { start: 0, end: 0 } },
212
+ diagnostics: [diagnostic("VEL2008", "VelarScript source nesting is too complex to parse safely", { start: 0, end: Math.min(1, text.length) })],
213
+ };
214
+ }
215
+ }
216
+ function isJavaScriptStackOverflow(error) {
217
+ return error instanceof RangeError && /Maximum call stack size exceeded|too much recursion/iu.test(error.message);
218
+ }
219
+ function normalizedExtensions(extensions) {
220
+ const seen = new Set();
221
+ const capabilities = new Set();
222
+ const primitiveOwners = new Map();
223
+ const globalOwners = new Map();
224
+ const extensionReservedBindings = new Set(extensions.flatMap((extension) => [...extension.analysis?.reservedBindings ?? []]));
225
+ for (const extension of extensions) {
226
+ if (!extension.id || seen.has(extension.id))
227
+ throw new Error(`Compiler extension '${extension.id}' is invalid or duplicated`);
228
+ seen.add(extension.id);
229
+ for (const capability of extension.capabilities ?? []) {
230
+ if (!/^[a-z][a-z0-9-]*$/u.test(capability) || capabilities.has(capability)) {
231
+ throw new Error(`Compiler capability '${capability}' is invalid or has more than one owner`);
232
+ }
233
+ capabilities.add(capability);
234
+ }
235
+ for (const name of extension.analysis?.primitiveTypes ?? []) {
236
+ const owner = primitiveOwners.get(name);
237
+ if (isCorePrimitiveName(name))
238
+ throw new Error(`Compiler extension '${extension.id}' cannot replace Core primitive '${name}'`);
239
+ if (bindingNameRestriction(name, extensionReservedBindings) || owner) {
240
+ throw new Error(`Compiler primitive '${name}' is invalid or has more than one owner${owner ? ` (${owner}, ${extension.id})` : ""}`);
241
+ }
242
+ primitiveOwners.set(name, extension.id);
243
+ }
244
+ for (const name of extension.analysis?.globals?.keys() ?? []) {
245
+ const restriction = bindingNameRestriction(name);
246
+ if (restriction === "core")
247
+ throw new Error(`Compiler extension '${extension.id}' cannot replace reserved Core binding '${name}'`);
248
+ if (restriction)
249
+ throw new Error(`Compiler extension '${extension.id}' declares invalid global '${name}'`);
250
+ const owner = globalOwners.get(name);
251
+ if (owner)
252
+ throw new Error(`Compiler global '${name}' has more than one owner (${owner}, ${extension.id})`);
253
+ globalOwners.set(name, extension.id);
254
+ }
255
+ }
256
+ const parents = new Map();
257
+ for (const extension of extensions) {
258
+ for (const [name, values] of extension.analysis?.primitiveParents ?? []) {
259
+ if (primitiveOwners.get(name) !== extension.id) {
260
+ throw new Error(`Compiler extension '${extension.id}' cannot define parents for primitive '${name}' that it does not own`);
261
+ }
262
+ const collected = parents.get(name) ?? new Set();
263
+ for (const parent of values) {
264
+ if (!primitiveOwners.has(parent))
265
+ throw new Error(`Compiler primitive '${name}' has unknown parent '${parent}'`);
266
+ collected.add(parent);
267
+ }
268
+ parents.set(name, collected);
269
+ }
270
+ for (const [name, fields] of extension.analysis?.primitiveMutableFields ?? []) {
271
+ if (primitiveOwners.get(name) !== extension.id) {
272
+ throw new Error(`Compiler extension '${extension.id}' cannot make fields writable on primitive '${name}' that it does not own`);
273
+ }
274
+ for (const field of fields) {
275
+ if (memberNameRestriction(field, "data"))
276
+ throw new Error(`Compiler primitive '${name}' has invalid writable field '${field}'`);
277
+ }
278
+ }
279
+ }
280
+ const completed = new Set();
281
+ const active = new Set();
282
+ const visit = (name) => {
283
+ if (completed.has(name))
284
+ return;
285
+ if (active.has(name))
286
+ throw new Error(`Compiler primitive inheritance contains a cycle at '${name}'`);
287
+ active.add(name);
288
+ for (const parent of parents.get(name) ?? [])
289
+ visit(parent);
290
+ active.delete(name);
291
+ completed.add(name);
292
+ };
293
+ for (const name of primitiveOwners.keys())
294
+ visit(name);
295
+ return extensions;
296
+ }
297
+ // No extension list: dependency discovery asks the AST and nothing else.
298
+ function dependenciesOf(program) {
299
+ const externSources = new Set(program.body
300
+ .filter((statement) => statement.kind === "ExternModuleDeclaration")
301
+ .map((statement) => statement.source));
302
+ const dependencies = program.body
303
+ .filter((statement) => statement.kind === "ImportDeclaration")
304
+ .map((statement) => ({
305
+ source: statement.source,
306
+ span: statement.sourceSpan,
307
+ javascript: statement.javascript,
308
+ unsafe: statement.unsafe,
309
+ dynamic: false,
310
+ ...(statement.javascript && !statement.unsafe && externSources.has(statement.source) ? { externOwned: true } : {}),
311
+ specifiers: statement.specifiers.map((specifier) => ({
312
+ imported: specifier.imported,
313
+ local: specifier.local,
314
+ namespace: specifier.namespace,
315
+ })),
316
+ }));
317
+ for (const statement of program.body) {
318
+ if (statement.kind !== "ReExportDeclaration")
319
+ continue;
320
+ dependencies.push({
321
+ source: statement.source,
322
+ span: statement.sourceSpan,
323
+ javascript: false,
324
+ unsafe: false,
325
+ dynamic: false,
326
+ reExport: true,
327
+ specifiers: statement.specifiers.map((specifier) => ({
328
+ imported: specifier.imported,
329
+ local: specifier.exported,
330
+ namespace: false,
331
+ })),
332
+ });
333
+ }
334
+ // Inline JavaScript is still part of the project's JavaScript dependency
335
+ // graph. The source spelling no longer decides whether check can see it:
336
+ // Acorn's module parse above supplies the same literal specifiers that the
337
+ // sibling module will execute after emission.
338
+ for (const statement of program.body) {
339
+ if (statement.kind !== "EmbeddedJavaScriptDeclaration")
340
+ continue;
341
+ for (const dependency of statement.dependencies) {
342
+ dependencies.push({
343
+ source: dependency.source,
344
+ span: dependency.span,
345
+ javascript: true,
346
+ unsafe: statement.unsafe,
347
+ dynamic: dependency.dynamic,
348
+ specifiers: [],
349
+ });
350
+ }
351
+ }
352
+ // Dynamic imports are found by walking the AST structurally rather than by
353
+ // a second switch over the statement and expression kinds this function
354
+ // remembers. A-010: that switch existed, and it had no case for `try`,
355
+ // `using`, or `test "…":`, and descended into a class only through its
356
+ // fields, `constructor:` and methods — so a `import("./dep.vel")` in a
357
+ // getter, a `@dispose:`, or a `@iterate:` left the module out of the graph
358
+ // while `check` reported success. `@iterate:` was missing from the day D68
359
+ // added it: a hand-kept copy of the AST drifts the moment the AST grows,
360
+ // and every future container would have drifted the same way.
361
+ const dynamicSources = new Set();
362
+ for (const expression of astNodesOfKind(program, "DynamicImportExpression")) {
363
+ if (dynamicSources.has(expression.source))
364
+ continue;
365
+ dynamicSources.add(expression.source);
366
+ dependencies.push({
367
+ source: expression.source,
368
+ span: expression.span,
369
+ javascript: false,
370
+ unsafe: false,
371
+ dynamic: true,
372
+ specifiers: [],
373
+ });
374
+ }
375
+ return dependencies;
376
+ }
377
+ function interfaceOf(program, path, extensions, analyzedBindings = new Map(), analyzedClasses = new Map()) {
378
+ const classIdentities = new Map([["Error", "Error"]]);
379
+ for (const statement of program.body) {
380
+ if (statement.kind === "ClassDeclaration")
381
+ classIdentities.set(statement.name, `velar:${path}#${statement.name}`);
382
+ }
383
+ // Extern classes are nominal per JavaScript source and class name, so
384
+ // signatures that mention them stay nominal across module interfaces. A
385
+ // Velar class declaration owns its bare name if both exist in one module.
386
+ for (const statement of program.body) {
387
+ if (statement.kind !== "ExternModuleDeclaration")
388
+ continue;
389
+ for (const declaration of statement.classes) {
390
+ if (!classIdentities.has(declaration.name))
391
+ classIdentities.set(declaration.name, `js:${statement.source}#${declaration.name}`);
392
+ }
393
+ }
394
+ const enumNames = new Map(program.body
395
+ .filter((statement) => statement.kind === "EnumDeclaration")
396
+ .map((statement) => [statement.name, { identity: `${path}#enum:${statement.name}`, members: new Set(statement.members.map((member) => member.name)) }]));
397
+ const namedTypeIdentities = new Map(program.body
398
+ .filter((statement) => statement.kind === "TypeDeclaration")
399
+ .map((statement) => [statement.name, `velar:${path}#type:${statement.name}`]));
400
+ const aliasDeclarations = new Map();
401
+ for (const statement of program.body) {
402
+ if (statement.kind === "TypeAliasDeclaration")
403
+ aliasDeclarations.set(statement.name, statement);
404
+ }
405
+ const analysisExtensions = extensions.flatMap((extension) => extension.analysis ? [extension.analysis] : []);
406
+ const directAwaitExpression = (expression, contains) => {
407
+ for (const extension of analysisExtensions) {
408
+ const result = extension.directAwaitExpression?.(expression, contains);
409
+ if (result !== undefined)
410
+ return result;
411
+ }
412
+ return undefined;
413
+ };
414
+ const directAwaitStatement = (statement, containsExpression, containsBlock) => {
415
+ for (const extension of analysisExtensions) {
416
+ const result = extension.directAwaitStatement?.(statement, containsExpression, containsBlock);
417
+ if (result !== undefined)
418
+ return result;
419
+ }
420
+ return undefined;
421
+ };
422
+ const resolveRaw = (reference) => resolveTypeReference(reference, (syntax, nested) => {
423
+ for (const extension of analysisExtensions) {
424
+ const resolved = extension.resolveTypeSyntax?.(syntax, nested);
425
+ if (resolved)
426
+ return resolved;
427
+ }
428
+ return undefined;
429
+ });
430
+ const aliasCache = new Map();
431
+ const expandAliases = (type, seen = new Set()) => {
432
+ if (type.kind === "named" && aliasDeclarations.has(type.name)) {
433
+ if (seen.has(type.name))
434
+ return unknownType;
435
+ const cached = aliasCache.get(type.name);
436
+ if (cached)
437
+ return type.readonlyView ? readonlyViewOf(cached) : cached;
438
+ const declaration = aliasDeclarations.get(type.name);
439
+ const expanded = expandAliases(resolveRaw(declaration.target), new Set([...seen, type.name]));
440
+ aliasCache.set(type.name, expanded);
441
+ return type.readonlyView ? readonlyViewOf(expanded) : expanded;
442
+ }
443
+ // D55 rule 121: an alias inside a type argument is transparent here too,
444
+ // so the interface publishes the same instantiation identity the analyzer
445
+ // computed for the body.
446
+ if (type.kind === "named" && type.application) {
447
+ return { ...type, application: { ...type.application, arguments: type.application.arguments.map((argument) => expandAliases(argument, seen)) } };
448
+ }
449
+ if (type.kind === "optional")
450
+ return optionalOf(expandAliases(type.inner, seen));
451
+ if (type.kind === "list")
452
+ return { ...type, element: expandAliases(type.element, seen) };
453
+ if (type.kind === "set")
454
+ return { ...type, element: expandAliases(type.element, seen) };
455
+ if (type.kind === "map")
456
+ return { ...type, key: expandAliases(type.key, seen), value: expandAliases(type.value, seen) };
457
+ if (type.kind === "record")
458
+ return { ...type, value: expandAliases(type.value, seen) };
459
+ if (type.kind === "promise")
460
+ return { kind: "promise", value: expandAliases(type.value, seen) };
461
+ if (type.kind === "runtimeType")
462
+ return { kind: "runtimeType", value: expandAliases(type.value, seen) };
463
+ if (type.kind === "typeObject")
464
+ return type.value ? { ...type, value: expandAliases(type.value, seen) } : type;
465
+ if (type.kind === "object")
466
+ return { ...type, fields: new Map([...type.fields].map(([name, value]) => [name, expandAliases(value, seen)])) };
467
+ if (type.kind === "extension") {
468
+ return {
469
+ ...type,
470
+ properties: new Map([...type.properties].map(([name, value]) => [name, expandAliases(value, seen)])),
471
+ arguments: type.arguments.map((argument) => expandAliases(argument, seen)),
472
+ };
473
+ }
474
+ if (type.kind === "function" || type.kind === "action" || type.kind === "intrinsic")
475
+ return {
476
+ ...type,
477
+ parameters: type.parameters.map((parameter) => expandAliases(parameter, seen)),
478
+ ...(type.rest ? { rest: expandAliases(type.rest, seen) } : {}),
479
+ result: expandAliases(type.result, seen),
480
+ };
481
+ if (type.kind === "union")
482
+ return { kind: "union", members: type.members.map((member) => expandAliases(member, seen)) };
483
+ return type;
484
+ };
485
+ const resolve = (reference) => resolveNominals(expandAliases(reference ? resolveRaw(reference) : unknownType), classIdentities, enumNames, namedTypeIdentities);
486
+ const resolveAnalyzed = (type) => resolveNominals(expandAliases(type), classIdentities, enumNames, namedTypeIdentities);
487
+ const resolvedAnalyzedBindings = new Map([...analyzedBindings]
488
+ .map(([name, type]) => [name, resolveNominals(expandAliases(type), classIdentities, enumNames, namedTypeIdentities)]));
489
+ const namedTypes = new Map();
490
+ const namedTypeReadonlyFields = new Map();
491
+ const genericTypes = new Map();
492
+ const typeAliases = new Map();
493
+ const enums = new Map();
494
+ const classes = new Map();
495
+ const exports = new Map();
496
+ const hoistedExports = new Set();
497
+ const mutableExports = new Set();
498
+ const reactiveExports = new Map();
499
+ const inspectionExtensions = extensions.flatMap((extension) => extension.inspection ? [extension.inspection] : []);
500
+ const tests = [];
501
+ const extensionExports = new Map(extensions.map((extension) => [extension.id, new Map()]));
502
+ const extensionData = new Map();
503
+ for (const extension of extensions) {
504
+ const data = extension.inspection?.moduleData?.(program, path);
505
+ if (data !== undefined)
506
+ extensionData.set(extension.id, data);
507
+ const annotations = extension.inspection?.exportAnnotations?.(program);
508
+ if (annotations) {
509
+ const values = extensionExports.get(extension.id);
510
+ for (const [name, value] of annotations)
511
+ values.set(name, value);
512
+ }
513
+ }
514
+ for (const [name, declaration] of aliasDeclarations)
515
+ typeAliases.set(name, resolve(declaration.target));
516
+ for (const statement of program.body) {
517
+ if (statement.kind === "TypeDeclaration") {
518
+ const readonlyFields = new Set(statement.fields.filter((field) => field.readonly).map((field) => field.name));
519
+ // D55 rule 120: a generic record crosses the boundary as a template. Its
520
+ // field table still has the `parameter` kinds in it, which is what lets a
521
+ // dependent instantiate it with an argument this module never named.
522
+ if (statement.typeParameters?.length) {
523
+ const frame = new Map(statement.typeParameters
524
+ .map((parameter, index) => [parameter.name, { kind: "parameter", name: parameter.name, index }]));
525
+ genericTypes.set(statement.name, {
526
+ identity: namedTypeIdentities.get(statement.name),
527
+ name: statement.name,
528
+ parameterNames: statement.typeParameters.map((parameter) => parameter.name),
529
+ parameterBounds: statement.typeParameters.map((parameter) => parameter.bound && isTypeParameterBound(parameter.bound) ? parameter.bound : null),
530
+ fields: new Map(statement.fields.map((field) => [field.name, bindNamedTypeParameters(resolve(field.type), frame)])),
531
+ ...(readonlyFields.size > 0 ? { readonlyFields } : {}),
532
+ });
533
+ continue;
534
+ }
535
+ namedTypes.set(statement.name, new Map(statement.fields.map((field) => [field.name, resolve(field.type)])));
536
+ if (readonlyFields.size > 0)
537
+ namedTypeReadonlyFields.set(statement.name, readonlyFields);
538
+ }
539
+ else if (statement.kind === "EnumDeclaration") {
540
+ enums.set(statement.name, enumNames.get(statement.name));
541
+ }
542
+ else if (statement.kind === "ClassDeclaration") {
543
+ const fields = new Map([
544
+ ...statement.parameters
545
+ .filter((parameter) => parameter.binding && !parameter.private)
546
+ .map((parameter) => [parameter.name, { mutable: parameter.binding === "let", type: resolve(parameter.type) }]),
547
+ ...statement.fields
548
+ .filter((field) => !field.static && !field.private)
549
+ .map((field) => [field.name, { mutable: field.binding === "let", type: resolve(field.type) }]),
550
+ ...statement.getters
551
+ .filter((getter) => !getter.static && !getter.private)
552
+ .map((getter) => [getter.name, { mutable: false, type: resolve(getter.returnType) }]),
553
+ ]);
554
+ const staticFields = new Map(statement.fields
555
+ .filter((field) => field.static && !field.private)
556
+ .map((field) => [field.name, { mutable: field.binding === "let", type: resolve(field.type) }]));
557
+ for (const getter of statement.getters.filter((candidate) => candidate.static && !candidate.private)) {
558
+ staticFields.set(getter.name, { mutable: false, type: resolve(getter.returnType) });
559
+ }
560
+ const methods = new Map(statement.methods.filter((method) => !method.static && !method.private).map((method) => [method.name, functionSignature(method, resolve)]));
561
+ const staticMethods = new Map(statement.methods.filter((method) => method.static && !method.private).map((method) => [method.name, functionSignature(method, resolve)]));
562
+ const identity = classIdentities.get(statement.name);
563
+ classes.set(statement.name, {
564
+ identity,
565
+ // D43 item 69: the release contract crosses the module boundary with
566
+ // the class, so an imported handle stays usable with `using`.
567
+ ...(statement.dispose ? {
568
+ dispose: blockContainsDirectAwait(statement.dispose.body, directAwaitExpression, directAwaitStatement) ? "async" : "sync",
569
+ } : {}),
570
+ parameters: statement.parameters.map((parameter) => resolve(parameter.type)),
571
+ parameterNames: statement.parameters.map((parameter) => parameter.name),
572
+ requiredParameters: statement.parameters.filter((parameter) => !parameter.defaultValue).length,
573
+ base: statement.base ? classIdentities.get(statement.base.name) ?? statement.base.name : null,
574
+ abstract: statement.abstract,
575
+ fields,
576
+ getters: new Set(statement.getters.filter((getter) => !getter.static && !getter.private).map((getter) => getter.name)),
577
+ abstractGetters: new Set(statement.getters.filter((getter) => getter.abstract && !getter.private).map((getter) => getter.name)),
578
+ methods,
579
+ abstractMethods: new Set(statement.methods.filter((method) => method.abstract && !method.private).map((method) => method.name)),
580
+ staticFields,
581
+ staticGetters: new Set(statement.getters.filter((getter) => getter.static && !getter.private).map((getter) => getter.name)),
582
+ staticMethods,
583
+ });
584
+ const analyzed = analyzedClasses.get(statement.name);
585
+ if (analyzed) {
586
+ classes.set(statement.name, {
587
+ ...analyzed,
588
+ identity,
589
+ parameters: analyzed.parameters.map(resolveAnalyzed),
590
+ ...(analyzed.constructorRest ? { constructorRest: resolveAnalyzed(analyzed.constructorRest) } : {}),
591
+ // D68 rule 177: the iteration contract crosses the module boundary
592
+ // with the class, so an imported Bag iterates in the importing module
593
+ // exactly as it does in its own.
594
+ ...(analyzed.iterate ? { iterate: resolveAnalyzed(analyzed.iterate) } : {}),
595
+ base: analyzed.base ? classIdentities.get(analyzed.base) ?? analyzed.base : null,
596
+ fields: new Map([...analyzed.fields].map(([name, field]) => [name, { ...field, type: resolveAnalyzed(field.type) }])),
597
+ methods: new Map([...analyzed.methods].map(([name, type]) => [name, resolveAnalyzed(type)])),
598
+ staticFields: new Map([...analyzed.staticFields].map(([name, field]) => [name, { ...field, type: resolveAnalyzed(field.type) }])),
599
+ staticMethods: new Map([...analyzed.staticMethods].map(([name, type]) => [name, resolveAnalyzed(type)])),
600
+ });
601
+ }
602
+ }
603
+ else if (statement.kind === "ExternModuleDeclaration") {
604
+ // Extern classes travel with the interface under their identity so a
605
+ // dependent module that declares the same class for the same source can
606
+ // verify that both declarations agree on one contract.
607
+ for (const declaration of statement.classes) {
608
+ const identity = `js:${statement.source}#${declaration.name}`;
609
+ const analyzed = analyzedClasses.get(identity);
610
+ if (analyzed) {
611
+ classes.set(identity, {
612
+ ...analyzed,
613
+ identity,
614
+ parameters: analyzed.parameters.map(resolveAnalyzed),
615
+ ...(analyzed.constructorRest ? { constructorRest: resolveAnalyzed(analyzed.constructorRest) } : {}),
616
+ fields: new Map([...analyzed.fields].map(([name, field]) => [name, { ...field, type: resolveAnalyzed(field.type) }])),
617
+ methods: new Map([...analyzed.methods].map(([name, type]) => [name, resolveAnalyzed(type)])),
618
+ staticFields: new Map([...analyzed.staticFields].map(([name, field]) => [name, { ...field, type: resolveAnalyzed(field.type) }])),
619
+ staticMethods: new Map([...analyzed.staticMethods].map(([name, type]) => [name, resolveAnalyzed(type)])),
620
+ });
621
+ continue;
622
+ }
623
+ const fields = new Map();
624
+ const staticFields = new Map();
625
+ for (const parameter of declaration.parameters) {
626
+ if (parameter.binding)
627
+ fields.set(parameter.name, { mutable: parameter.binding === "let", type: resolve(parameter.type) });
628
+ }
629
+ for (const field of declaration.fields) {
630
+ (field.static ? staticFields : fields).set(field.name, { mutable: field.mutable, type: resolve(field.type) });
631
+ }
632
+ const getters = new Set();
633
+ const staticGetters = new Set();
634
+ for (const getter of declaration.getters) {
635
+ (getter.static ? staticFields : fields).set(getter.name, { mutable: false, type: resolve(getter.type) });
636
+ (getter.static ? staticGetters : getters).add(getter.name);
637
+ }
638
+ const methods = new Map();
639
+ const staticMethods = new Map();
640
+ for (const method of declaration.methods) {
641
+ (method.static ? staticMethods : methods).set(method.name, functionSignature(method, resolve));
642
+ }
643
+ const rest = declaration.parameters.find((parameter) => parameter.rest);
644
+ classes.set(identity, {
645
+ identity,
646
+ parameters: declaration.parameters.filter((parameter) => !parameter.rest).map((parameter) => resolve(parameter.type)),
647
+ requiredParameters: declaration.parameters.filter((parameter) => !parameter.rest && !parameter.defaultValue).length,
648
+ ...(rest ? { constructorRest: resolve(rest.type) } : {}),
649
+ base: declaration.base ? `js:${statement.source}#${declaration.base}` : null,
650
+ abstract: false,
651
+ fields,
652
+ getters,
653
+ abstractGetters: new Set(),
654
+ methods,
655
+ abstractMethods: new Set(),
656
+ staticFields,
657
+ staticGetters,
658
+ staticMethods,
659
+ });
660
+ }
661
+ }
662
+ else if (statement.kind === "TestDeclaration") {
663
+ tests.push({ name: testFunctionName(statement), title: statement.title });
664
+ }
665
+ }
666
+ for (const statement of program.body) {
667
+ if (!("exported" in statement) || !statement.exported)
668
+ continue;
669
+ if (statement.kind === "TypeDeclaration") {
670
+ // D55 rule 126: a generic record's export is the instantiation factory,
671
+ // not a Type object — it has no `is` of its own, so it carries no value
672
+ // shape and the analyzer refuses to read it as one.
673
+ exports.set(statement.name, statement.typeParameters?.length
674
+ ? { kind: "typeObject", name: statement.name }
675
+ : {
676
+ kind: "typeObject",
677
+ name: statement.name,
678
+ value: {
679
+ kind: "named",
680
+ name: statement.name,
681
+ identity: namedTypeIdentities.get(statement.name),
682
+ },
683
+ });
684
+ }
685
+ else if (statement.kind === "TypeAliasDeclaration") {
686
+ exports.set(statement.name, { kind: "typeObject", name: statement.name, value: typeAliases.get(statement.name) });
687
+ }
688
+ else if (statement.kind === "EnumDeclaration") {
689
+ const info = enums.get(statement.name);
690
+ exports.set(statement.name, { kind: "enumObject", name: statement.name, identity: info.identity, members: info.members });
691
+ }
692
+ else if (statement.kind === "ClassDeclaration") {
693
+ exports.set(statement.name, { kind: "classConstructor", name: statement.name, identity: classIdentities.get(statement.name) });
694
+ }
695
+ else if (statement.kind === "FunctionDeclaration") {
696
+ exports.set(statement.name, resolvedAnalyzedBindings.get(`${statement.span.start}:${statement.name}`) ?? functionSignature(statement, resolve));
697
+ // `def` emits a JavaScript function declaration, which the host
698
+ // initializes at link time; `class`, `enum`, and `const`/`let` all emit
699
+ // bindings that stay in their temporal dead zone until the module body
700
+ // reaches them.
701
+ hoistedExports.add(statement.name);
702
+ }
703
+ else if (statement.kind === "VariableDeclaration") {
704
+ exportPattern(statement.pattern, statement.type ? resolve(statement.type) : inferPublicExpression(statement.initializer, inspectionExtensions), exports, mutableExports, statement.binding === "let", namedTypes, resolvedAnalyzedBindings);
705
+ }
706
+ else {
707
+ for (const extension of extensions) {
708
+ if (!extension.inspection)
709
+ continue;
710
+ const context = {
711
+ exports,
712
+ reactiveExports,
713
+ extensionExports: extensionExports.get(extension.id),
714
+ resolve,
715
+ inferPublicExpression: (expression) => inferPublicExpression(expression, inspectionExtensions),
716
+ bindingType: (name, spanStart) => resolvedAnalyzedBindings.get(`${spanStart}:${name}`) ?? null,
717
+ unresolvedInferredResult: inferredResultPlaceholderType,
718
+ };
719
+ if (extension.inspection.contributeInterface?.(statement, context))
720
+ break;
721
+ }
722
+ }
723
+ }
724
+ const reExports = new Map();
725
+ for (const statement of program.body) {
726
+ if (statement.kind !== "ReExportDeclaration")
727
+ continue;
728
+ for (const specifier of statement.specifiers) {
729
+ reExports.set(specifier.exported, { source: statement.source, imported: specifier.imported });
730
+ }
731
+ }
732
+ return {
733
+ exports,
734
+ hoistedExports,
735
+ mutableExports,
736
+ reactiveExports,
737
+ reExports,
738
+ namedTypes,
739
+ namedTypeReadonlyFields,
740
+ namedTypeIdentities,
741
+ ...(genericTypes.size > 0 ? { genericTypes } : {}),
742
+ typeAliases,
743
+ enums,
744
+ classes,
745
+ tests,
746
+ extensionExports: new Map([...extensionExports].filter(([, values]) => values.size > 0)),
747
+ extensionData,
748
+ };
749
+ }
750
+ function functionSignature(statement, resolve) {
751
+ const frame = new Map();
752
+ // D41 item 61 risk 4: this is the cross-module export interface. A bound
753
+ // dropped here would silently disappear from every imported generic.
754
+ const bounds = [];
755
+ for (const declaration of statement.typeParameters ?? []) {
756
+ if (frame.has(declaration.name))
757
+ continue;
758
+ frame.set(declaration.name, { kind: "parameter", name: declaration.name, index: frame.size });
759
+ bounds.push(declaration.bound && isTypeParameterBound(declaration.bound) ? declaration.bound : null);
760
+ }
761
+ const boundVector = bounds.some((bound) => bound !== null) ? bounds : null;
762
+ const resolveBound = (reference) => frame.size === 0 ? resolve(reference) : bindNamedTypeParameters(resolve(reference), frame);
763
+ const result = statement.returnType
764
+ ? resolveBound(statement.returnType)
765
+ : "abstract" in statement && statement.abstract === true ? invalidType : inferredResultPlaceholderType;
766
+ const rest = statement.parameters.find((parameter) => parameter.rest);
767
+ return {
768
+ kind: "function",
769
+ ...(frame.size > 0 ? { typeParameterNames: [...frame.keys()] } : {}),
770
+ ...(frame.size > 0 && boundVector ? { typeParameterBounds: boundVector } : {}),
771
+ parameters: statement.parameters.filter((parameter) => !parameter.rest).map((parameter) => resolveBound(parameter.type)),
772
+ parameterNames: statement.parameters.filter((parameter) => !parameter.rest).map((parameter) => parameter.name),
773
+ requiredParameters: statement.parameters.filter((parameter) => !parameter.rest && !parameter.defaultValue).length,
774
+ ...(rest ? { rest: resolveBound(rest.type) } : {}),
775
+ result: statement.asynchronous ? { kind: "promise", value: resolvedAsyncType(result) } : result,
776
+ };
777
+ }
778
+ function resolveNominals(type, classIdentities, enumNames, namedTypeIdentities) {
779
+ // D55 rule 121: the interface is where a module's local names become the
780
+ // identities its dependents see, and an application has to make that crossing
781
+ // whole — the declaration *and* every argument — or the two sides of the
782
+ // boundary would compute two different instantiation identities for one type.
783
+ if (type.kind === "named" && type.application) {
784
+ const arguments_ = type.application.arguments.map((argument) => resolveNominals(argument, classIdentities, enumNames, namedTypeIdentities));
785
+ const declaration = namedTypeIdentities.get(type.application.name) ?? type.application.declaration;
786
+ return genericApplicationType(declaration, type.application.name, arguments_, type.readonlyView === true);
787
+ }
788
+ if (type.kind === "named" && classIdentities.has(type.name)) {
789
+ const identity = classIdentities.get(type.name);
790
+ return {
791
+ kind: "class",
792
+ name: type.name,
793
+ ...(identity === type.name ? {} : { identity }),
794
+ };
795
+ }
796
+ if (type.kind === "named" && enumNames.has(type.name))
797
+ return { kind: "enum", name: type.name, identity: enumNames.get(type.name).identity };
798
+ if (type.kind === "named" && namedTypeIdentities.has(type.name)) {
799
+ return { ...type, identity: namedTypeIdentities.get(type.name) };
800
+ }
801
+ if ((type.kind === "class" || type.kind === "classConstructor") && classIdentities.has(type.name)
802
+ && (!type.identity || type.identity === type.name)) {
803
+ return { ...type, identity: classIdentities.get(type.name) };
804
+ }
805
+ if ((type.kind === "enum" || type.kind === "enumMember" || type.kind === "enumObject") && enumNames.has(type.name)
806
+ && type.identity === type.name) {
807
+ return { ...type, identity: enumNames.get(type.name).identity };
808
+ }
809
+ if (type.kind === "optional")
810
+ return optionalOf(resolveNominals(type.inner, classIdentities, enumNames, namedTypeIdentities));
811
+ if (type.kind === "list")
812
+ return { ...type, element: resolveNominals(type.element, classIdentities, enumNames, namedTypeIdentities) };
813
+ if (type.kind === "set")
814
+ return { ...type, element: resolveNominals(type.element, classIdentities, enumNames, namedTypeIdentities) };
815
+ if (type.kind === "map")
816
+ return { ...type, key: resolveNominals(type.key, classIdentities, enumNames, namedTypeIdentities), value: resolveNominals(type.value, classIdentities, enumNames, namedTypeIdentities) };
817
+ if (type.kind === "record")
818
+ return { ...type, value: resolveNominals(type.value, classIdentities, enumNames, namedTypeIdentities) };
819
+ if (type.kind === "promise")
820
+ return { kind: "promise", value: resolveNominals(type.value, classIdentities, enumNames, namedTypeIdentities) };
821
+ if (type.kind === "runtimeType")
822
+ return { kind: "runtimeType", value: resolveNominals(type.value, classIdentities, enumNames, namedTypeIdentities) };
823
+ if (type.kind === "typeObject")
824
+ return type.value
825
+ ? { ...type, value: resolveNominals(type.value, classIdentities, enumNames, namedTypeIdentities) }
826
+ : type;
827
+ if (type.kind === "object")
828
+ return { ...type, fields: new Map([...type.fields].map(([name, value]) => [name, resolveNominals(value, classIdentities, enumNames, namedTypeIdentities)])) };
829
+ if (type.kind === "function" || type.kind === "action" || type.kind === "intrinsic")
830
+ return {
831
+ ...type,
832
+ parameters: type.parameters.map((parameter) => resolveNominals(parameter, classIdentities, enumNames, namedTypeIdentities)),
833
+ ...(type.rest ? { rest: resolveNominals(type.rest, classIdentities, enumNames, namedTypeIdentities) } : {}),
834
+ result: resolveNominals(type.result, classIdentities, enumNames, namedTypeIdentities),
835
+ };
836
+ if (type.kind === "union")
837
+ return { kind: "union", members: type.members.map((member) => resolveNominals(member, classIdentities, enumNames, namedTypeIdentities)) };
838
+ if (type.kind === "extension")
839
+ return {
840
+ ...type,
841
+ properties: new Map([...type.properties].map(([name, value]) => [name, resolveNominals(value, classIdentities, enumNames, namedTypeIdentities)])),
842
+ arguments: type.arguments.map((argument) => resolveNominals(argument, classIdentities, enumNames, namedTypeIdentities)),
843
+ };
844
+ return type;
845
+ }
846
+ function inferPublicExpression(expression, extensions) {
847
+ for (const extension of extensions) {
848
+ const inferred = extension.inferPublicExpression?.(expression);
849
+ if (inferred)
850
+ return inferred;
851
+ }
852
+ switch (expression.kind) {
853
+ case "LiteralExpression":
854
+ return expression.value === null ? nullType : typeof expression.value === "string" ? stringType : typeof expression.value === "number" ? numberType : boolType;
855
+ case "FStringExpression":
856
+ return stringType;
857
+ case "ListExpression": {
858
+ let element = unknownType;
859
+ for (const item of expression.elements) {
860
+ const type = inferPublicExpression(item.kind === "SpreadExpression" ? item.value : item, extensions);
861
+ element = mergeTypes(element, item.kind === "SpreadExpression" && type.kind === "list" ? type.element : type);
862
+ }
863
+ return { kind: "list", element };
864
+ }
865
+ case "ObjectExpression": {
866
+ const fields = new Map();
867
+ const optionalFields = new Set();
868
+ for (const property of expression.properties) {
869
+ if (property.kind === "ObjectProperty") {
870
+ fields.set(property.name, inferPublicExpression(property.value, extensions));
871
+ optionalFields.delete(property.name);
872
+ }
873
+ else {
874
+ const spread = inferPublicExpression(property.value, extensions);
875
+ if (spread.kind === "object")
876
+ for (const [name, type] of spread.fields) {
877
+ const alreadyRequired = fields.has(name) && !optionalFields.has(name);
878
+ fields.set(name, type);
879
+ if (!alreadyRequired && spread.optionalFields?.has(name))
880
+ optionalFields.add(name);
881
+ else
882
+ optionalFields.delete(name);
883
+ }
884
+ }
885
+ }
886
+ return { kind: "object", fields, ...(optionalFields.size > 0 ? { optionalFields } : {}) };
887
+ }
888
+ case "SpreadExpression":
889
+ return inferPublicExpression(expression.value, extensions);
890
+ default:
891
+ return unknownType;
892
+ }
893
+ }
894
+ function exportPattern(pattern, type, exports, mutableExports, mutable, namedTypes, analyzedBindings = new Map()) {
895
+ if (pattern.kind === "NameBindingPattern") {
896
+ exports.set(pattern.name, analyzedBindings.get(`${pattern.span.start}:${pattern.name}`) ?? type);
897
+ if (mutable)
898
+ mutableExports.add(pattern.name);
899
+ return;
900
+ }
901
+ if (pattern.kind === "ListBindingPattern") {
902
+ const element = type.kind === "list" ? type.element : unknownType;
903
+ for (const child of pattern.elements)
904
+ if (child) {
905
+ exportPattern(child, element, exports, mutableExports, mutable, namedTypes, analyzedBindings);
906
+ }
907
+ if (pattern.rest)
908
+ exports.set(pattern.rest.name, analyzedBindings.get(`${pattern.rest.span.start}:${pattern.rest.name}`) ?? { kind: "list", element });
909
+ if (pattern.rest && mutable)
910
+ mutableExports.add(pattern.rest.name);
911
+ return;
912
+ }
913
+ const fields = type.kind === "object" ? type.fields : type.kind === "named" ? namedTypes.get(type.name) : null;
914
+ const selected = new Set(pattern.entries.map((entry) => entry.property));
915
+ for (const entry of pattern.entries) {
916
+ const field = fields?.get(entry.property) ?? unknownType;
917
+ exportPattern(entry.pattern, type.kind === "object" && type.optionalFields?.has(entry.property) ? optionalOf(field) : field, exports, mutableExports, mutable, namedTypes, analyzedBindings);
918
+ }
919
+ if (pattern.rest) {
920
+ const optionalFields = type.kind === "object"
921
+ ? new Set([...type.optionalFields ?? []].filter((name) => !selected.has(name)))
922
+ : new Set();
923
+ exports.set(pattern.rest.name, analyzedBindings.get(`${pattern.rest.span.start}:${pattern.rest.name}`) ?? {
924
+ kind: "object",
925
+ fields: new Map([...(fields ?? [])].filter(([name]) => !selected.has(name))),
926
+ ...(optionalFields.size > 0 ? { optionalFields } : {}),
927
+ });
928
+ if (mutable)
929
+ mutableExports.add(pattern.rest.name);
930
+ }
931
+ }
932
+ //# sourceMappingURL=index.js.map