@scriptc/compiler 0.0.21 → 0.0.23

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 (92) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +117 -37
  2. package/dist/backend/cc-cache.test.d.ts +1 -0
  3. package/dist/backend/cc-cache.test.js +2507 -0
  4. package/dist/backend/cc-cache.test.js.map +1 -0
  5. package/dist/backend/cc.d.ts +69 -11
  6. package/dist/backend/cc.js +1937 -222
  7. package/dist/backend/cc.js.map +1 -1
  8. package/dist/backend/emission/emit-async.js +3 -1
  9. package/dist/backend/emission/emit-async.js.map +1 -1
  10. package/dist/backend/emission/emit-exprs.d.ts +10 -0
  11. package/dist/backend/emission/emit-exprs.js +482 -18
  12. package/dist/backend/emission/emit-exprs.js.map +1 -1
  13. package/dist/backend/emission/emit-stmts.js +13 -5
  14. package/dist/backend/emission/emit-stmts.js.map +1 -1
  15. package/dist/backend/emission/emit-types.js +4 -1
  16. package/dist/backend/emission/emit-types.js.map +1 -1
  17. package/dist/backend/emission/emit-walkers.js +108 -7
  18. package/dist/backend/emission/emit-walkers.js.map +1 -1
  19. package/dist/backend/emission/emitter.d.ts +27 -1
  20. package/dist/backend/emission/emitter.js +84 -3
  21. package/dist/backend/emission/emitter.js.map +1 -1
  22. package/dist/backend/llvm/dyn.d.ts +1 -0
  23. package/dist/backend/llvm/dyn.js +305 -8
  24. package/dist/backend/llvm/dyn.js.map +1 -1
  25. package/dist/backend/llvm/emitter.js +831 -23
  26. package/dist/backend/llvm/emitter.js.map +1 -1
  27. package/dist/backend/llvm/shapes.js +3 -2
  28. package/dist/backend/llvm/shapes.js.map +1 -1
  29. package/dist/compat/fetch-profile.d.ts +118 -0
  30. package/dist/compat/fetch-profile.js +531 -0
  31. package/dist/compat/fetch-profile.js.map +1 -0
  32. package/dist/coverage/surface-manifest.js +69 -0
  33. package/dist/coverage/surface-manifest.js.map +1 -1
  34. package/dist/diagnostics/diagnostic.d.ts +10 -0
  35. package/dist/diagnostics/diagnostic.js +27 -1
  36. package/dist/diagnostics/diagnostic.js.map +1 -1
  37. package/dist/frontend/lowering/lower-builtins.d.ts +24 -15
  38. package/dist/frontend/lowering/lower-builtins.js +217 -69
  39. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  40. package/dist/frontend/lowering/lower-calls.js +41 -0
  41. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  42. package/dist/frontend/lowering/lower-classes.js +32 -11
  43. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  44. package/dist/frontend/lowering/lower-exprs.d.ts +3 -3
  45. package/dist/frontend/lowering/lower-exprs.js +87 -15
  46. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  47. package/dist/frontend/lowering/lower-generators.js +7 -1
  48. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  49. package/dist/frontend/lowering/lower-island.d.ts +123 -7
  50. package/dist/frontend/lowering/lower-island.js +1976 -26
  51. package/dist/frontend/lowering/lower-island.js.map +1 -1
  52. package/dist/frontend/lowering/lower-modules.js +17 -1
  53. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  54. package/dist/frontend/lowering/lower-stmts.js +33 -9
  55. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  56. package/dist/frontend/lowering/lowerer.d.ts +40 -0
  57. package/dist/frontend/lowering/lowerer.js +290 -18
  58. package/dist/frontend/lowering/lowerer.js.map +1 -1
  59. package/dist/frontend/lowering/surfaces.js +55 -12
  60. package/dist/frontend/lowering/surfaces.js.map +1 -1
  61. package/dist/frontend/program.d.ts +20 -4
  62. package/dist/frontend/program.js +391 -49
  63. package/dist/frontend/program.js.map +1 -1
  64. package/dist/frontend/resolve.d.ts +10 -0
  65. package/dist/frontend/resolve.js +37 -0
  66. package/dist/frontend/resolve.js.map +1 -1
  67. package/dist/frontend/ts7/enums.d.ts +9 -0
  68. package/dist/frontend/ts7/enums.js +8 -6
  69. package/dist/frontend/ts7/enums.js.map +1 -1
  70. package/dist/frontend/ts7/program.js +4 -1
  71. package/dist/frontend/ts7/program.js.map +1 -1
  72. package/dist/frontend/types.d.ts +16 -9
  73. package/dist/frontend/types.js +119 -38
  74. package/dist/frontend/types.js.map +1 -1
  75. package/dist/index.d.ts +7 -1
  76. package/dist/index.js +70 -14
  77. package/dist/index.js.map +1 -1
  78. package/dist/ir/nodes.d.ts +55 -26
  79. package/dist/ir/nodes.js +42 -29
  80. package/dist/ir/nodes.js.map +1 -1
  81. package/dist/ir/validate.js +106 -9
  82. package/dist/ir/validate.js.map +1 -1
  83. package/dist/library/int-infer.js +7 -1
  84. package/dist/library/int-infer.js.map +1 -1
  85. package/dist/library/int-infer.test.js +80 -0
  86. package/dist/library/int-infer.test.js.map +1 -1
  87. package/dist/library/sidecar-validate.js +5 -0
  88. package/dist/library/sidecar-validate.js.map +1 -1
  89. package/dist/library/sidecar.d.ts +3 -0
  90. package/dist/library/sidecar.js.map +1 -1
  91. package/package.json +2 -2
  92. package/surface-manifest.json +1097 -130
@@ -215,6 +215,12 @@ export interface LowerOptions {
215
215
  * imports; without this option ambient declarations keep Node's ordinary
216
216
  * ReferenceError behavior. */
217
217
  ffiImports?: readonly IrFfiImport[];
218
+ /** Coverage-only external host type surfaces. Their declarations inform
219
+ * the checker, while every runtime value use remains an SC1010 fence. */
220
+ externalTypes?: ReadonlyMap<string, string>;
221
+ /** The mapped entries plus relative declaration dependencies, attributed
222
+ * to their owning external specifier. */
223
+ externalTypeSpecifiersByFile?: ReadonlyMap<string, readonly string[]>;
218
224
  }
219
225
  /** The Lowerer's pass configuration (see lowerToIr). */
220
226
  export interface LowererMode {
@@ -240,6 +246,10 @@ export interface LowererMode {
240
246
  /** Program-validated ambient declaration symbols for each FFI name.
241
247
  * Undefined in discovery's legacy call-local validation path. */
242
248
  ffiBindingSymbols?: ReadonlyMap<string, ReadonlySet<ts.Symbol>>;
249
+ /** LowerOptions.externalTypes, threaded through every lowering pass. */
250
+ externalTypes?: ReadonlyMap<string, string>;
251
+ /** LowerOptions.externalTypeSpecifiersByFile, shared by every pass. */
252
+ externalTypeSpecifiersByFile?: ReadonlyMap<string, readonly string[]>;
243
253
  }
244
254
  /** Build lowering runs in two passes over the same ts.Program:
245
255
  *
@@ -752,6 +762,9 @@ export declare class Lowerer {
752
762
  readonly ffiImportsByName: ReadonlyMap<string, IrFfiImport>;
753
763
  /** Non-null after whole-program FFI declaration validation. */
754
764
  readonly ffiBindingSymbols: ReadonlyMap<string, ReadonlySet<ts.Symbol>> | null;
765
+ /** Exact specifier mappings and their reverse declaration-file lookup. */
766
+ readonly externalTypes: ReadonlyMap<string, string>;
767
+ readonly externalTypeSpecifiersByFile: ReadonlyMap<string, readonly string[]>;
755
768
  /** Symbols a POISONED declaration statement would have bound: the
756
769
  * declaration's own diagnostic is already recorded, and no local/global
757
770
  * registered, so later references fall through every resolution step —
@@ -810,6 +823,21 @@ export declare class Lowerer {
810
823
  * point for deferred collection diagnostics: resolving a reference to a
811
824
  * broken declaration reports what collection deferred. */
812
825
  resolveValueSymbol(ident: ts.Identifier): ts.Symbol | null;
826
+ /** The configured external host module owning an expression's runtime
827
+ * value, or null. Alias chains are followed to their declaration file so
828
+ * direct imports and local re-export facades classify identically. Type
829
+ * references never call this helper and remain ordinary checker input. */
830
+ externalTypeSpecifierOf(expr: ts.Expression): string | null;
831
+ /** The source file a checker-resolved module-specifier node names. The
832
+ * checker path covers package.json aliases as well as relative imports;
833
+ * resolveImport is the fallback for the latter. */
834
+ private moduleSourceFileOf;
835
+ /** Follow one project-module export through the checker's resolved export
836
+ * table, then recover its exact external route where alias declarations
837
+ * retain one. */
838
+ private externalTypeSpecifierOfModuleExport;
839
+ private externalTypeSpecifierOfNamespaceMember;
840
+ private externalTypeSpecifierOfSymbol;
813
841
  /** The default-snapshot storage symbol a DEFAULT-import alias chain
814
842
  * lands on, or null. A mutable entity-name default (`export default
815
843
  * someLet`) registers its Node-semantics snapshot global under the
@@ -938,6 +966,7 @@ export declare class Lowerer {
938
966
  * types made the (source-anchored) construct fail. */
939
967
  pushDiag(diag: ScrDiagnostic): void;
940
968
  unsupported(code: keyof typeof UNSUPPORTED & `SC${number}`, node: ts.Node, featureOverride?: string, hintOverride?: string): never;
969
+ externalHostFence(specifier: string, node: ts.Node, valueUse?: boolean): never;
941
970
  /** The dynamic-family fence for an OPERATION on an `any`-origin
942
971
  * checked-dynamic value that only the engine can execute (operators,
943
972
  * iteration, computed member names, ...). Carries SC2011 — the same
@@ -959,6 +988,17 @@ export declare class Lowerer {
959
988
  * adopted @types/node is blamed at @types/node. */
960
989
  noLowering(surface: string, node: ts.Node, hint?: string, sym?: ts.Symbol | null): never;
961
990
  stdlibMemberFence(access: ts.PropertyAccessExpression): void;
991
+ fenceStaticResponseMember(access: ts.PropertyAccessExpression | ts.ElementAccessExpression, use: "read" | "call"): IrExpr | null;
992
+ fenceUnsupportedFetchConstructorMember(access: ts.PropertyAccessExpression | ts.ElementAccessExpression): IrExpr | null;
993
+ fenceStaticHeadersMember(access: ts.PropertyAccessExpression | ts.ElementAccessExpression, use: "read" | "call"): IrExpr | null;
994
+ fenceStaticAbortControllerMemberRead(access: ts.PropertyAccessExpression | ts.ElementAccessExpression): IrExpr | null;
995
+ fenceStaticHeadersIteration(node: ts.Node): void;
996
+ fenceFetchObjectAssignment(target: ts.ObjectLiteralExpression, source: ts.Expression): void;
997
+ lowerDynamicHeadersIteratorCall(call: ts.CallExpression, access: ts.ElementAccessExpression): IrExpr | null;
998
+ lowerDynamicHeadersSpread(node: ts.Expression, type: IrType & {
999
+ kind: "array";
1000
+ }): IrExpr | null;
1001
+ fenceStaticReadableStreamMember(access: ts.PropertyAccessExpression | ts.ElementAccessExpression, use: "read" | "call"): IrExpr | null;
962
1002
  typeOf(node: ts.Node): ts.Type;
963
1003
  /** mapType with this Lowerer's registries and (while a generic instance
964
1004
  * body lowers) type-parameter bindings threaded through. */
@@ -12,7 +12,8 @@
12
12
  * - Lexical scoping is resolved here: locals get function-unique ids
13
13
  * ("x.0", "x.1" for shadowing); the IR is scope-flat.
14
14
  */
15
- import { isRelativeSpecifier } from "../shared.js";
15
+ import { resolve } from "node:path";
16
+ import { isRelativeSpecifier, tsgoPath } from "../shared.js";
16
17
  import * as ts from "../ts7/adapter.js";
17
18
  import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, componentTypeDiag, isCheckerPanic, genericSignatureTypeDiag, indexSignatureTypeDiag, intersectionTypeDiag, noLoweringDiag, overloadedSignatureTypeDiag, recordShapeMismatchDiag, requiresDynamicApiDiag, requiresDynamicPackageDiag, requiresDynamicTypeDiag, unionMismatchDiag, unsupportedDiag, unsupportedTypeDiag, } from "../../diagnostics/diagnostic.js";
18
19
  import { arrayOf, BOOL, canAdaptDynFuncTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, F64, isJsonSafeType, isUndefinedArmedUnion, isUnitType, JSVAL, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID } from "../../ir/nodes.js";
@@ -27,8 +28,8 @@ import { bindingNeverReassigned, bodyReadsArguments, isThisParameter, paramShape
27
28
  import { lowerArrayMethodCall, lowerBufferStaticCall, lowerBytesMethodCall, lowerBytesNew, lowerMapMethodCall, lowerMapForEachCall, buildMapForEachFn, lowerRecordOvfCaptureHelper, lowerEnvToPairsHelper, lowerSetMethodCall, lowerSetForEachCall, buildSetForEachFn, lowerRegexMethodCall, lowerStringMethodCall } from "./lower-containers.js";
28
29
  import { lowerStreamModuleCall } from "./lower-stream.js";
29
30
  import { lowerEmitOverrideSpec } from "./lower-emitter.js";
30
- import { builtinImportOf, createRequireBindingDecl, createRequireNamespaceDecl, createRequireSpecOf, stripTypeCasts, lowerBuiltinModuleCall, lowerFsToUnixTimestampCall, lowerFsLadderCall, lowerChildArgsArg, lowerSpawnSyncCall, lowerSpawnCall, lowerExecSyncCall, recordToEnvPairs, lowerJsonMethodCall, fencedBuiltinImportOf, lowerCryptoComposedCall, lowerUrlMethodCall, lowerSearchParamsMethodCall, lowerStatsMethodCall, lowerChildMethodCall, lowerAtomicsCall, lowerBuiltinExtraProperty, promisifiedExecFileDecl, lowerExecFileAsyncCall, execFileAsyncHelper, lowerStringDecoderMethodCall, strdecHelper, lowerReadlineMethodCall, lowerDcChannelMethodCall, lowerDcChannelProperty, lowerAlsMethodCall, lowerDcTracingChannelMethodCall, lowerDcTracingChannelProperty, lowerJsonProperty, lowerErrorCodeProperty, lowerProcessProperty, isProcessEnv, envValueType, lowerProcessEnvGet, lowerProcessMethodCall, lowerProcessOptionalMethodCall, lowerTimeoutMethodCall, envSnapshotHelper, isConsoleLog, consoleCallMember, lowerNumberStaticCall, lowerNumberStaticProperty, lowerDateCall, lowerTextCodecCall, lowerCryptoModuleCall, lowerFsConstantsProperty, lowerBuiltinConstantsProperty, builtinConstantBindingOf, builtinConstantsDestructureDecl, lowerProcessStreamProperty, lowerStringStaticCall, lowerStringLastIndexOfCall, lowerPromiseStaticCall } from "./lower-builtins.js";
31
- import { isIslandExpr, islandFuncValueFence, islandRegexpOf, jsvalIn, requireDynamicApi, islandGlobalFnOf, lowerDynamicImportCall, lowerFetchCall, lowerIslandMethodCall, lowerMathProperty, npmPackageOf, npmMemberFence, npmPackageOfSymbol } from "./lower-island.js";
31
+ import { builtinImportOf, createRequireBindingDecl, createRequireNamespaceDecl, createRequireSpecOf, stripTypeCasts, lowerBuiltinModuleCall, lowerFsToUnixTimestampCall, lowerFsLadderCall, lowerChildArgsArg, lowerSpawnSyncCall, lowerSpawnCall, lowerExecSyncCall, recordToEnvPairs, lowerJsonMethodCall, fencedBuiltinImportOf, lowerCryptoComposedCall, lowerUrlMethodCall, lowerSearchParamsMethodCall, lowerStatsMethodCall, lowerChildMethodCall, lowerAtomicsCall, lowerBuiltinExtraProperty, promisifiedExecFileDecl, lowerExecFileAsyncCall, execFileAsyncHelper, lowerStringDecoderMethodCall, strdecHelper, lowerReadlineMethodCall, lowerDcChannelMethodCall, lowerDcChannelProperty, lowerAlsMethodCall, lowerDcTracingChannelMethodCall, lowerDcTracingChannelProperty, lowerJsonProperty, lowerErrorCodeProperty, lowerProcessProperty, isProcessEnv, envValueType, lowerProcessEnvGet, lowerProcessMethodCall, lowerProcessOptionalMethodCall, lowerTimeoutMethodCall, envSnapshotHelper, isConsoleLog, consoleCallMember, lowerNumberStaticCall, lowerNumberStaticProperty, lowerDateCall, lowerTextCodecCall, lowerCryptoModuleCall, lowerFsConstantsProperty, lowerBuiltinConstantsProperty, builtinConstantBindingOf, builtinConstantsDestructureDecl, lowerProcessStreamProperty, lowerStringStaticCall, lowerStringLastIndexOfCall, lowerPromiseStaticCall, textCodecBindingClassOf } from "./lower-builtins.js";
32
+ import { fenceFetchObjectAssignment, fenceFetchObjectBinding, fenceStaticAbortControllerMemberRead, fenceStaticHeadersIteration, fenceStaticHeadersMember, fenceStaticReadableStreamMember, fenceStaticResponseMember, fenceUnsupportedFetchConstructorMember, isIslandExpr, islandFuncValueFence, islandRegexpOf, jsvalIn, requireDynamicApi, islandGlobalFnOf, lowerAbortControllerNew, lowerDynamicHeadersIteratorCall, lowerDynamicHeadersSpread, lowerDynamicImportCall, lowerFetchCall, lowerFetchElementMethodCall, lowerResponseNew, lowerStaticFetchCompanionCall, lowerStaticAbortControllerCall, lowerStaticAbortSignalListenerCall, lowerStaticReadableStreamCancelCall, lowerStaticReadableStreamControllerCall, lowerStaticReadableStreamNew, lowerStaticReadableStreamReaderCall, lowerStaticResponseCall, lowerIslandMethodCall, lowerMathProperty, npmPackageOf, npmMemberFence, npmPackageOfSymbol } from "./lower-island.js";
32
33
  import { lowerHttpHeadersElement, lowerNetModuleCall, lowerServerMethodCall, lowerServerProperty, lowerTlsRootCertificates } from "./lower-server.js";
33
34
  import { lowerDgramDnsModuleCall, lowerDgramMethodCall } from "./lower-dgram.js";
34
35
  import { lowerNodeTestModuleCall, lowerTestDirectCall, lowerTestMethodCall, lowerTestCtxProperty } from "./lower-test.js";
@@ -126,6 +127,18 @@ export function stmtUsesIsland(stmts) {
126
127
  visit(stmts, true);
127
128
  return found;
128
129
  }
130
+ function directExternalTypeSpecifiersByFile(externalTypes) {
131
+ const out = new Map();
132
+ for (const [specifier, file] of externalTypes) {
133
+ const key = tsgoPath(resolve(file));
134
+ const owners = out.get(key);
135
+ if (owners === undefined)
136
+ out.set(key, [specifier]);
137
+ else if (!owners.includes(specifier))
138
+ owners.push(specifier);
139
+ }
140
+ return out;
141
+ }
129
142
  /** Build lowering runs in two passes over the same ts.Program:
130
143
  *
131
144
  * 1. DISCOVERY — a worklist computes the set of reachable bodies. Seeds are
@@ -164,9 +177,14 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
164
177
  });
165
178
  }
166
179
  const ffiImports = options.ffiImports ?? [];
180
+ const externalTypes = options.externalTypes ?? new Map();
181
+ const externalTypeSpecifiersByFile = options.externalTypeSpecifiersByFile ??
182
+ directExternalTypeSpecifiersByFile(externalTypes);
167
183
  const validation = new Lowerer(program, entry, moduleOrder, dynamic, {
168
184
  targetPlatform,
169
185
  ffiImports,
186
+ externalTypes,
187
+ externalTypeSpecifiersByFile,
170
188
  });
171
189
  const ffiValidation = validateFfiImports(validation);
172
190
  // Discovery must use the same exact-symbol ownership as emit. Otherwise a
@@ -180,6 +198,8 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
180
198
  : new Lowerer(program, entry, moduleOrder, dynamic, {
181
199
  targetPlatform,
182
200
  ffiImports,
201
+ externalTypes,
202
+ externalTypeSpecifiersByFile,
183
203
  ffiBindingSymbols: ffiValidation.symbolsByName,
184
204
  });
185
205
  const reachable = discovery.discover(options.libRoots);
@@ -189,6 +209,8 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
189
209
  startupCrash,
190
210
  ffiImports,
191
211
  ffiBindingSymbols: ffiValidation.symbolsByName,
212
+ externalTypes,
213
+ externalTypeSpecifiersByFile,
192
214
  });
193
215
  for (const d of dynamicCycleDiags)
194
216
  emit.pushDiag(d);
@@ -204,6 +226,8 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
204
226
  targetPlatform,
205
227
  ffiImports,
206
228
  ffiBindingSymbols: ffiValidation.symbolsByName,
229
+ externalTypes,
230
+ externalTypeSpecifiersByFile,
207
231
  });
208
232
  const rem = remainder.run();
209
233
  return { ...result, unreached: { diagnostics: rem.diagnostics, stats: rem.stats } };
@@ -918,6 +942,9 @@ export class Lowerer {
918
942
  ffiImportsByName;
919
943
  /** Non-null after whole-program FFI declaration validation. */
920
944
  ffiBindingSymbols;
945
+ /** Exact specifier mappings and their reverse declaration-file lookup. */
946
+ externalTypes;
947
+ externalTypeSpecifiersByFile;
921
948
  /** Symbols a POISONED declaration statement would have bound: the
922
949
  * declaration's own diagnostic is already recorded, and no local/global
923
950
  * registered, so later references fall through every resolution step —
@@ -971,6 +998,9 @@ export class Lowerer {
971
998
  this.ffiImports = mode.ffiImports ?? [];
972
999
  this.ffiImportsByName = new Map(this.ffiImports.map((entry) => [entry.name, entry]));
973
1000
  this.ffiBindingSymbols = mode.ffiBindingSymbols ?? null;
1001
+ this.externalTypes = mode.externalTypes ?? new Map();
1002
+ this.externalTypeSpecifiersByFile = mode.externalTypeSpecifiersByFile ??
1003
+ directExternalTypeSpecifiersByFile(this.externalTypes);
974
1004
  this.checker = program.getTypeChecker();
975
1005
  this.typeCtx = {
976
1006
  checker: this.checker,
@@ -986,6 +1016,7 @@ export class Lowerer {
986
1016
  mixinIntersectionInstance: (widened) => mixinIntersectionInstanceType(this, widened),
987
1017
  isStdlibFile: this.isStdlibFile,
988
1018
  isNpmFile: this.isNpmFile,
1019
+ isExternalTypeFile: (sf) => this.externalTypeSpecifiersByFile.has(tsgoPath(resolve(sf.fileName))),
989
1020
  dynamic: this.dynamic,
990
1021
  // fileTag is filled just below; the hook is only ever CALLED during
991
1022
  // lowering, long after the constructor completes.
@@ -1117,6 +1148,183 @@ export class Lowerer {
1117
1148
  this.flushDeferred(symbol);
1118
1149
  return symbol;
1119
1150
  }
1151
+ /** The configured external host module owning an expression's runtime
1152
+ * value, or null. Alias chains are followed to their declaration file so
1153
+ * direct imports and local re-export facades classify identically. Type
1154
+ * references never call this helper and remain ordinary checker input. */
1155
+ externalTypeSpecifierOf(expr) {
1156
+ if (this.externalTypes.size === 0)
1157
+ return null;
1158
+ let value = expr;
1159
+ while (ts.isParenthesizedExpression(value) ||
1160
+ ts.isAsExpression(value) ||
1161
+ ts.isTypeAssertion(value) ||
1162
+ ts.isNonNullExpression(value)) {
1163
+ value = value.expression;
1164
+ }
1165
+ if (ts.isCallExpression(value)) {
1166
+ if (value.expression.kind === ts.SyntaxKind.ImportKeyword) {
1167
+ const spec = value.arguments[0];
1168
+ return spec !== undefined && ts.isStringLiteralLike(spec) && this.externalTypes.has(spec.text)
1169
+ ? spec.text
1170
+ : null;
1171
+ }
1172
+ if (ts.isIdentifier(value.expression) &&
1173
+ value.expression.text === "require" &&
1174
+ value.arguments.length === 1) {
1175
+ const spec = value.arguments[0];
1176
+ if (ts.isStringLiteralLike(spec) && this.externalTypes.has(spec.text))
1177
+ return spec.text;
1178
+ }
1179
+ return this.externalTypeSpecifierOf(value.expression);
1180
+ }
1181
+ if (ts.isNewExpression(value))
1182
+ return this.externalTypeSpecifierOf(value.expression);
1183
+ if (ts.isTaggedTemplateExpression(value)) {
1184
+ return this.externalTypeSpecifierOf(value.tag);
1185
+ }
1186
+ if (ts.isPropertyAccessExpression(value) || ts.isElementAccessExpression(value)) {
1187
+ // Follow the runtime RECEIVER, not the property's declaration: a
1188
+ // project-owned record may use an interface declared by the mapped
1189
+ // file and remains ordinary static data (`const x: HostType = ...;
1190
+ // x.field`). Only a value rooted in the imported module is external.
1191
+ const receiver = this.externalTypeSpecifierOf(value.expression);
1192
+ if (receiver !== null)
1193
+ return receiver;
1194
+ const member = ts.isPropertyAccessExpression(value)
1195
+ ? value.name.text
1196
+ : value.argumentExpression !== undefined && ts.isStringLiteralLike(value.argumentExpression)
1197
+ ? value.argumentExpression.text
1198
+ : null;
1199
+ return member !== null
1200
+ ? this.externalTypeSpecifierOfNamespaceMember(value.expression, member)
1201
+ : null;
1202
+ }
1203
+ if (!ts.isIdentifier(value))
1204
+ return null;
1205
+ return this.externalTypeSpecifierOfSymbol(this.checker.getSymbolAtLocation(value));
1206
+ }
1207
+ /** The source file a checker-resolved module-specifier node names. The
1208
+ * checker path covers package.json aliases as well as relative imports;
1209
+ * resolveImport is the fallback for the latter. */
1210
+ moduleSourceFileOf(from, spec) {
1211
+ const moduleSymbol = this.checker.getSymbolAtLocation(spec);
1212
+ for (const decl of moduleSymbol ? this.checker.declarationsOf(moduleSymbol) : []) {
1213
+ if (ts.isSourceFile(decl))
1214
+ return decl;
1215
+ }
1216
+ return isRelativeSpecifier(spec.text) ? resolveImport(this.program, from, spec.text) : null;
1217
+ }
1218
+ /** Follow one project-module export through the checker's resolved export
1219
+ * table, then recover its exact external route where alias declarations
1220
+ * retain one. */
1221
+ externalTypeSpecifierOfModuleExport(sf, exportName, seenSymbols, seenExports) {
1222
+ const exportKey = `${tsgoPath(resolve(sf.fileName))}\0${exportName}`;
1223
+ if (seenExports.has(exportKey))
1224
+ return null;
1225
+ seenExports.add(exportKey);
1226
+ // Ask the checker which symbol the module ACTUALLY exports under this
1227
+ // name. Syntax-only `export *` scanning cannot answer shadowing: a local
1228
+ // or explicit export wins over a same-named star export, and a star
1229
+ // contributes only names its target really exports. The resolved symbol
1230
+ // retains route-aware ExportSpecifier/NamespaceExport declarations for
1231
+ // exact mappings, while star exports resolve to the mapped declaration
1232
+ // owner through externalTypeSpecifiersByFile.
1233
+ const moduleSymbol = this.checker.getSymbolAtLocation(sf);
1234
+ const exported = moduleSymbol?.getExports().get(exportName);
1235
+ return this.externalTypeSpecifierOfSymbol(exported, seenSymbols, seenExports);
1236
+ }
1237
+ externalTypeSpecifierOfNamespaceMember(expr, member) {
1238
+ let value = expr;
1239
+ while (ts.isParenthesizedExpression(value) ||
1240
+ ts.isAsExpression(value) ||
1241
+ ts.isTypeAssertion(value) ||
1242
+ ts.isNonNullExpression(value)) {
1243
+ value = value.expression;
1244
+ }
1245
+ if (!ts.isIdentifier(value))
1246
+ return null;
1247
+ const symbol = this.checker.getSymbolAtLocation(value);
1248
+ const namespaceDecl = symbol
1249
+ ? this.checker.declarationsOf(symbol).find(ts.isNamespaceImport)
1250
+ : undefined;
1251
+ if (namespaceDecl === undefined)
1252
+ return null;
1253
+ const importDecl = namespaceDecl.parent.parent;
1254
+ if (!ts.isImportDeclaration(importDecl) || !ts.isStringLiteral(importDecl.moduleSpecifier))
1255
+ return null;
1256
+ if (this.externalTypes.has(importDecl.moduleSpecifier.text))
1257
+ return importDecl.moduleSpecifier.text;
1258
+ const dep = this.moduleSourceFileOf(importDecl.getSourceFile(), importDecl.moduleSpecifier);
1259
+ return dep !== null && !dep.isDeclarationFile
1260
+ ? this.externalTypeSpecifierOfModuleExport(dep, member, new Set(), new Set())
1261
+ : null;
1262
+ }
1263
+ externalTypeSpecifierOfSymbol(symbol, seenSymbols = new Set(), seenExports = new Set()) {
1264
+ if (symbol === undefined || seenSymbols.has(symbol))
1265
+ return null;
1266
+ seenSymbols.add(symbol);
1267
+ const declarations = this.checker.declarationsOf(symbol);
1268
+ // Route-aware alias hops run before declaration-file ownership. An
1269
+ // exact import must keep the specifier it actually named, rather than
1270
+ // inheriting whichever alias happened to register the shared file last.
1271
+ for (const decl of declarations) {
1272
+ let specNode;
1273
+ let importedName = null;
1274
+ if (ts.isImportSpecifier(decl)) {
1275
+ const importDecl = decl.parent.parent.parent;
1276
+ if (ts.isImportDeclaration(importDecl))
1277
+ specNode = importDecl.moduleSpecifier;
1278
+ importedName = (decl.propertyName ?? decl.name).text;
1279
+ }
1280
+ else if (ts.isImportClause(decl)) {
1281
+ if (ts.isImportDeclaration(decl.parent))
1282
+ specNode = decl.parent.moduleSpecifier;
1283
+ importedName = "default";
1284
+ }
1285
+ else if (ts.isNamespaceImport(decl)) {
1286
+ const importDecl = decl.parent.parent;
1287
+ if (ts.isImportDeclaration(importDecl))
1288
+ specNode = importDecl.moduleSpecifier;
1289
+ importedName = null;
1290
+ }
1291
+ else if (ts.isExportSpecifier(decl)) {
1292
+ const exportDecl = decl.parent.parent;
1293
+ if (ts.isExportDeclaration(exportDecl))
1294
+ specNode = exportDecl.moduleSpecifier;
1295
+ importedName = (decl.propertyName ?? decl.name).text;
1296
+ }
1297
+ else if (ts.isNamespaceExport(decl)) {
1298
+ const exportDecl = decl.parent;
1299
+ if (ts.isExportDeclaration(exportDecl))
1300
+ specNode = exportDecl.moduleSpecifier;
1301
+ importedName = "*";
1302
+ }
1303
+ else {
1304
+ continue;
1305
+ }
1306
+ if (specNode === undefined || !ts.isStringLiteral(specNode))
1307
+ continue;
1308
+ if (this.externalTypes.has(specNode.text))
1309
+ return specNode.text;
1310
+ // A namespace OBJECT from a project module is not wholly external;
1311
+ // property accesses resolve their selected member separately above.
1312
+ if (importedName === null)
1313
+ return null;
1314
+ const dep = this.moduleSourceFileOf(decl.getSourceFile(), specNode);
1315
+ return dep !== null && !dep.isDeclarationFile
1316
+ ? this.externalTypeSpecifierOfModuleExport(dep, importedName, seenSymbols, seenExports)
1317
+ : null;
1318
+ }
1319
+ for (const decl of declarations) {
1320
+ const owners = this.externalTypeSpecifiersByFile.get(tsgoPath(resolve(decl.getSourceFile().fileName)));
1321
+ if (owners !== undefined && owners.length > 0)
1322
+ return owners[0];
1323
+ }
1324
+ if ((symbol.flags & ts.SymbolFlags.Alias) === 0)
1325
+ return null;
1326
+ return this.externalTypeSpecifierOfSymbol(this.checker.getAliasedSymbol(symbol), seenSymbols, seenExports);
1327
+ }
1120
1328
  /** The default-snapshot storage symbol a DEFAULT-import alias chain
1121
1329
  * lands on, or null. A mutable entity-name default (`export default
1122
1330
  * someLet`) registers its Node-semantics snapshot global under the
@@ -2097,6 +2305,11 @@ export class Lowerer {
2097
2305
  this.pushDiag(unsupportedDiag(code, locOf(node), featureOverride, hintOverride));
2098
2306
  throw new PoisonError();
2099
2307
  }
2308
+ externalHostFence(specifier, node, valueUse = true) {
2309
+ this.unsupported("SC1010", node, valueUse
2310
+ ? `values from the '${specifier}' external host module (types supplied by --external-types, but no runtime implementation or scriptc lowering was provided)`
2311
+ : `the '${specifier}' external host module (types supplied by --external-types, but no runtime implementation or scriptc lowering was provided)`, `the declaration mapping is analysis-only: coverage continues through project code, while executing ${valueUse ? "this value" : "this module"} requires an embedder integration with explicit runtime semantics`);
2312
+ }
2100
2313
  /** The dynamic-family fence for an OPERATION on an `any`-origin
2101
2314
  * checked-dynamic value that only the engine can execute (operators,
2102
2315
  * iteration, computed member names, ...). Carries SC2011 — the same
@@ -2130,10 +2343,10 @@ export class Lowerer {
2130
2343
  this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node), undefined, true));
2131
2344
  throw new PoisonError();
2132
2345
  }
2133
- // Island-backed ambient TYPES (Response, AbortSignal, RequestInit) in a
2134
- // STATIC build: the values live in the embedded engine, so the honest
2135
- // story is the per-site SC2012 — the same one the fetch call itself
2136
- // reports — not the generic supported-types recitation.
2346
+ // Engine-backed ambient TYPES in a STATIC build report the per-site
2347
+ // SC2012 rather than the generic supported-types recitation. Native
2348
+ // fetch values map earlier to checked-dynamic handles and do not reach
2349
+ // here; constructor objects such as Headers still can.
2137
2350
  if (!this.dynamic &&
2138
2351
  typeSym &&
2139
2352
  ISLAND_AMBIENT_TYPES.includes(typeSym.name) &&
@@ -2291,6 +2504,8 @@ export class Lowerer {
2291
2504
  RelativeTimeFormat: intlHint,
2292
2505
  Segmenter: intlHint,
2293
2506
  DisplayNames: intlHint,
2507
+ TextEncoder: "TextEncoder values have no representation — call through a const initialized with new TextEncoder(), or use the composed new TextEncoder().encode(s) form",
2508
+ TextDecoder: "TextDecoder values have no representation — call through a const initialized with the default new TextDecoder(), or use the composed new TextDecoder().decode(bytes) form",
2294
2509
  };
2295
2510
  this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node), typeHints[stdlibOwnSym?.name ?? ""]));
2296
2511
  throw new PoisonError();
@@ -2318,6 +2533,33 @@ export class Lowerer {
2318
2533
  stdlibMemberFence(access) {
2319
2534
  return stdlibMemberFence(this, access);
2320
2535
  }
2536
+ fenceStaticResponseMember(access, use) {
2537
+ return fenceStaticResponseMember(this, access, use);
2538
+ }
2539
+ fenceUnsupportedFetchConstructorMember(access) {
2540
+ return fenceUnsupportedFetchConstructorMember(this, access);
2541
+ }
2542
+ fenceStaticHeadersMember(access, use) {
2543
+ return fenceStaticHeadersMember(this, access, use);
2544
+ }
2545
+ fenceStaticAbortControllerMemberRead(access) {
2546
+ return fenceStaticAbortControllerMemberRead(this, access);
2547
+ }
2548
+ fenceStaticHeadersIteration(node) {
2549
+ return fenceStaticHeadersIteration(this, node);
2550
+ }
2551
+ fenceFetchObjectAssignment(target, source) {
2552
+ return fenceFetchObjectAssignment(this, target, source);
2553
+ }
2554
+ lowerDynamicHeadersIteratorCall(call, access) {
2555
+ return lowerDynamicHeadersIteratorCall(this, call, access);
2556
+ }
2557
+ lowerDynamicHeadersSpread(node, type) {
2558
+ return lowerDynamicHeadersSpread(this, node, type);
2559
+ }
2560
+ fenceStaticReadableStreamMember(access, use) {
2561
+ return fenceStaticReadableStreamMember(this, access, use);
2562
+ }
2321
2563
  typeOf(node) {
2322
2564
  // Inside an optional-chain body the guarded receiver is typed by its
2323
2565
  // NON-NULLISH type (the chain's tag test proved it), so every
@@ -5431,16 +5673,23 @@ export class Lowerer {
5431
5673
  }
5432
5674
  }
5433
5675
  // An OBJECT LITERAL against a checked-dynamic slot in a JS file (the
5434
- // getSupportInfo options argument — a dyn-ABI param): the value's
5435
- // world IS the checked-dynamic tree — build the dyn literal directly, before the
5436
- // island gate could claim the checker's `any` context (an island
5437
- // build could never land in the slot: no engine→dyn crossing).
5676
+ // getSupportInfo options argument — a dyn-ABI param), or against the
5677
+ // standard RequestInit type in TypeScript: the value's world IS the
5678
+ // checked-dynamic tree — build the dyn literal directly.
5438
5679
  if (expected?.kind === "dyn") {
5439
5680
  let x = node;
5440
5681
  while (ts.isParenthesizedExpression(x))
5441
5682
  x = x.expression;
5442
- if (ts.isObjectLiteralExpression(x) && isJsSourceFile(x.getSourceFile())) {
5443
- return lowerDynObjectLiteral(this, x);
5683
+ if (ts.isObjectLiteralExpression(x)) {
5684
+ const contextual = this.checker.getContextualType(x);
5685
+ const widened = contextual
5686
+ ? this.checker.getBaseTypeOfLiteralType(contextual)
5687
+ : undefined;
5688
+ const sym = widened?.getAliasSymbol() ?? widened?.getSymbol();
5689
+ const requestInit = sym?.name === "RequestInit" && this.isStdlibSymbol(sym);
5690
+ if (isJsSourceFile(x.getSourceFile()) || requestInit) {
5691
+ return lowerDynObjectLiteral(this, x);
5692
+ }
5444
5693
  }
5445
5694
  }
5446
5695
  // An ARRAY LITERAL against a UNION slot whose own type has no static
@@ -6396,6 +6645,17 @@ export class Lowerer {
6396
6645
  // binding-form text.
6397
6646
  if (symbol) {
6398
6647
  const d = this.checker.valueDeclarationOf(symbol);
6648
+ if (d && ts.isVariableDeclaration(d) && ts.isVariableDeclarationList(d.parent) &&
6649
+ (d.parent.flags & ts.NodeFlags.Const) !== 0) {
6650
+ const codec = textCodecBindingClassOf(this, d.name, d.initializer);
6651
+ if (codec !== null) {
6652
+ const call = codec === "TextEncoder" ? `${name}.encode(s)` : `${name}.decode(bytes)`;
6653
+ const composed = codec === "TextEncoder"
6654
+ ? "new TextEncoder().encode(s)"
6655
+ : "new TextDecoder().decode(bytes)";
6656
+ this.noLowering(`${codec} instance stored in ${name}`, node, `${call} compiles through this const; the codec object itself has no representation (the composed ${composed} form also compiles)`);
6657
+ }
6658
+ }
6399
6659
  if (d && ts.isVariableDeclaration(d) && d.initializer === undefined) {
6400
6660
  const t = this.checker.getTypeOfSymbol(symbol);
6401
6661
  const parts = t.isUnionType() ? t.getTypes() : [t];
@@ -6460,6 +6720,9 @@ export class Lowerer {
6460
6720
  return lowerDestructuringAssignParts(this, target, rhs, loc);
6461
6721
  }
6462
6722
  lowerBindingPattern(pattern, srcRef, srcType, isLet, out, dynSpell) {
6723
+ if (ts.isObjectBindingPattern(pattern)) {
6724
+ fenceFetchObjectBinding(this, pattern);
6725
+ }
6463
6726
  // An ISLAND source (`const { readFileSync } = await import("fs")` —
6464
6727
  // a namespace handle, or any 'any'-typed object): each bound name is
6465
6728
  // an engine property read, mirroring the island property-read rule —
@@ -6795,12 +7058,21 @@ export class Lowerer {
6795
7058
  return lowerInstanceOf(this, expr, loc);
6796
7059
  }
6797
7060
  lowerCall(expr) {
6798
- // The island-backed ambient fetch and dynamic import() claim their
6799
- // calls before the general dispatch (the general identifier-call paths
6800
- // have no lowering for a promise-returning ambient global, and
6801
- // `import` is a keyword callee no identifier path matches).
7061
+ // Ambient fetch (native in static builds, island-backed under
7062
+ // --dynamic) and dynamic import() claim their calls before the general
7063
+ // dispatch. The general identifier-call paths have no lowering for a
7064
+ // promise-returning ambient global, and `import` is a keyword callee no
7065
+ // identifier path matches.
6802
7066
  return (lowerFfiCall(this, expr) ??
6803
7067
  lowerFetchCall(this, expr) ??
7068
+ lowerStaticFetchCompanionCall(this, expr) ??
7069
+ lowerStaticAbortControllerCall(this, expr) ??
7070
+ lowerStaticAbortSignalListenerCall(this, expr) ??
7071
+ lowerStaticReadableStreamCancelCall(this, expr) ??
7072
+ lowerStaticReadableStreamControllerCall(this, expr) ??
7073
+ lowerStaticReadableStreamReaderCall(this, expr) ??
7074
+ lowerStaticResponseCall(this, expr) ??
7075
+ lowerFetchElementMethodCall(this, expr) ??
6804
7076
  lowerDynamicImportCall(this, expr) ??
6805
7077
  lowerCall(this, expr));
6806
7078
  }
@@ -6923,7 +7195,7 @@ export class Lowerer {
6923
7195
  const arg = this.lowerExpr(expr.arguments[0]);
6924
7196
  return { kind: "jsOp", op: "construct", args: [ctor, arg], type: JSVAL, loc };
6925
7197
  }
6926
- return lowerNew(this, expr);
7198
+ return lowerAbortControllerNew(this, expr) ?? lowerResponseNew(this, expr) ?? lowerStaticReadableStreamNew(this, expr) ?? lowerNew(this, expr);
6927
7199
  }
6928
7200
  lowerFieldRead(expr) {
6929
7201
  // `C.x` static reads first (the receiver is a CLASS, not an instance