@bleedingdev/modern-js-app-tools-extensions 3.9.3-ultramodern.1 → 3.9.3-ultramodern.11

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 (38) hide show
  1. package/dist/cjs/build-config/build-environment.js +7 -5
  2. package/dist/cjs/build-config/internal-effect-discovery.js +94 -6
  3. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +24 -3
  4. package/dist/cjs/native-type-checker.js +66 -20
  5. package/dist/cjs/release-identity.js +1 -1
  6. package/dist/cjs/renderer-build-identity.js +13 -3
  7. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +18 -1
  8. package/dist/esm/build-config/build-environment.mjs +7 -5
  9. package/dist/esm/build-config/internal-effect-discovery.mjs +90 -5
  10. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +24 -3
  11. package/dist/esm/native-type-checker.mjs +68 -22
  12. package/dist/esm/release-identity.mjs +1 -1
  13. package/dist/esm/renderer-build-identity.mjs +13 -3
  14. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +18 -1
  15. package/dist/esm-node/build-config/build-environment.mjs +7 -5
  16. package/dist/esm-node/build-config/internal-effect-discovery.mjs +90 -5
  17. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +24 -3
  18. package/dist/esm-node/native-type-checker.mjs +68 -22
  19. package/dist/esm-node/release-identity.mjs +1 -1
  20. package/dist/esm-node/renderer-build-identity.mjs +13 -3
  21. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +18 -1
  22. package/dist/types/build-config/build-environment.d.ts +3 -1
  23. package/dist/types/build-config/internal-effect-discovery.d.ts +3 -1
  24. package/dist/types/cloudflare/worker-manifest.d.ts +3 -2
  25. package/dist/types/config.d.ts +4 -0
  26. package/dist/types/native-type-checker.d.ts +4 -2
  27. package/dist/types/release-identity.d.ts +10 -0
  28. package/dist/types/renderer-build-identity.d.ts +12 -1
  29. package/package.json +27 -27
  30. package/src/build-config/build-environment.ts +11 -9
  31. package/src/build-config/internal-effect-discovery.ts +238 -19
  32. package/src/cloudflare/worker-manifest.ts +7 -3
  33. package/src/cloudflare-output-verifier/mutation-policy.ts +41 -3
  34. package/src/config.ts +4 -0
  35. package/src/native-type-checker.ts +87 -43
  36. package/src/release-identity.ts +11 -2
  37. package/src/renderer-build-identity.ts +51 -11
  38. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +18 -1
@@ -1,7 +1,8 @@
1
1
  import promises from "node:fs/promises";
2
2
  import node_path from "node:path";
3
3
  import { addIssue } from "./issues.mjs";
4
- const SOURCE_SCAN_FILE_PATTERN = /\.(?:[cm]?[jt]s|json)$/u;
4
+ const SOURCE_SCAN_FILE_PATTERN = /\.(?:[cm]?[jt]sx?)$/u;
5
+ const DECLARATION_FILE_PATTERN = /\.d\.[cm]?ts$/u;
5
6
  const SOURCE_SCAN_IGNORED_DIRECTORY_NAMES = new Set([
6
7
  '.codex',
7
8
  '.fastcontext',
@@ -74,7 +75,7 @@ const walkFiles = async (root)=>{
74
75
  files.push(...await walkFiles(entryPath));
75
76
  continue;
76
77
  }
77
- if (SOURCE_SCAN_FILE_PATTERN.test(entry.name)) files.push(entryPath);
78
+ if ('package.json' === entry.name || SOURCE_SCAN_FILE_PATTERN.test(entry.name) && !DECLARATION_FILE_PATTERN.test(entry.name)) files.push(entryPath);
78
79
  }
79
80
  return files;
80
81
  };
@@ -91,7 +92,26 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
91
92
  if ('ENOENT' === error.code) continue;
92
93
  throw error;
93
94
  }
94
- for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (forbidden.pattern.test(source)) addIssue(issues, {
95
+ let executableSources = [
96
+ source
97
+ ];
98
+ if ('package.json' === node_path.basename(file)) try {
99
+ const manifest = JSON.parse(source);
100
+ if (!isRecord(manifest)) throw new TypeError('Package metadata must be an object.');
101
+ const scripts = Object.hasOwn(manifest, "scripts") ? manifest.scripts : {};
102
+ if (!isRecord(scripts)) throw new TypeError("Package scripts must be an object.");
103
+ const commands = Object.values(scripts);
104
+ if (!commands.every((value)=>'string' == typeof value)) throw new TypeError("Package scripts must contain only strings.");
105
+ executableSources = commands;
106
+ } catch {
107
+ addIssue(issues, {
108
+ code: 'invalid-package',
109
+ message: "Cannot verify executable package lifecycle scripts: invalid package metadata.",
110
+ path: file
111
+ });
112
+ continue;
113
+ }
114
+ for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (executableSources.some((value)=>forbidden.pattern.test(value))) addIssue(issues, {
95
115
  code: forbidden.code,
96
116
  message: forbidden.message,
97
117
  path: file
@@ -102,4 +122,5 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
102
122
  issues
103
123
  };
104
124
  };
125
+ const isRecord = (value)=>null !== value && 'object' == typeof value && !Array.isArray(value);
105
126
  export { verifyCloudflareOutputMutationPolicy, walkFiles };
@@ -1,29 +1,59 @@
1
- import { execFile } from "node:child_process";
1
+ import { execFile, execFileSync } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
- import { existsSync, readFileSync, realpathSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { existsSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
4
5
  import node_path from "node:path";
5
6
  import { promisify } from "node:util";
6
7
  import { refreshTsgoCheckerConfig } from "@modern-js/builder";
7
8
  import { parse, printParseErrorCode } from "jsonc-parser";
9
+ import { resolveEffectTsgoCompiler } from "./build-config/build-environment.mjs";
8
10
  const execute = promisify(execFile);
9
11
  const native_type_checker_name = 'UltramodernNativeTypeChecker';
10
- function resolveNativeTypeCheckerCommand(packageJsonPath) {
11
- const manifestFile = realpathSync(packageJsonPath);
12
- const directory = node_path.dirname(manifestFile);
13
- const manifest = JSON.parse(readFileSync(manifestFile, 'utf8'));
14
- if ("typescript" !== manifest.name || '7.0.2' !== manifest.version) throw new Error(`${native_type_checker_name} requires typescript@7.0.2; found ${String(manifest.name)}@${String(manifest.version)} at ${manifestFile}.`);
15
- const bin = manifest.bin?.tsc;
16
- if ('string' != typeof bin || !bin || node_path.isAbsolute(bin)) throw new Error(`${native_type_checker_name} requires the selected package's public tsc bin.`);
17
- const launcher = realpathSync(node_path.resolve(directory, bin));
18
- const relative = node_path.relative(directory, launcher);
19
- if (!relative || node_path.isAbsolute(relative) || '..' === relative || relative.startsWith(`..${node_path.sep}`) || !statSync(launcher).isFile()) throw new Error(`${native_type_checker_name} public tsc bin must belong to its selected package.`);
12
+ function resolveNativeTypeCheckerCommand(from) {
13
+ const executable = resolveEffectTsgoCompiler({
14
+ from
15
+ });
16
+ const version = execFileSync(executable, [
17
+ '--version'
18
+ ], {
19
+ encoding: 'utf8',
20
+ env: {
21
+ ...process.env,
22
+ TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go'
23
+ },
24
+ timeout: 10000
25
+ });
26
+ if (version.includes('(tsrs ')) return {
27
+ executable,
28
+ args: [],
29
+ configApi: createRequire(from).resolve('tsrs/sync')
30
+ };
20
31
  return {
21
- executable: process.execPath,
22
- args: [
23
- launcher
24
- ]
32
+ executable,
33
+ args: []
25
34
  };
26
35
  }
36
+ const inspectNativeConfig = `
37
+ import { pathToFileURL } from 'node:url';
38
+ const [modulePath, compiler, configFile] = process.argv.slice(1);
39
+ const { API, formatDiagnostics } = await import(pathToFileURL(modulePath).href);
40
+ const api = new API({ tsserverPath: compiler });
41
+ try {
42
+ const config = api.parseConfigFile(configFile);
43
+ if (config.errors.length) {
44
+ process.stdout.write(formatDiagnostics(config.errors, api));
45
+ process.exitCode = 1;
46
+ } else {
47
+ process.stdout.write(JSON.stringify({
48
+ compilerOptions: config.options,
49
+ files: config.fileNames,
50
+ references: config.projectReferences,
51
+ }));
52
+ }
53
+ } finally {
54
+ api.close();
55
+ }
56
+ `;
27
57
  const watchDependencyPath = (file)=>{
28
58
  if ('win32' !== process.platform) return file;
29
59
  try {
@@ -135,19 +165,37 @@ class UltramodernNativeTypeChecker {
135
165
  executable: selected,
136
166
  args: []
137
167
  } : selected;
138
- const result = await execute(command.executable, [
168
+ const inspect = '--showConfig' === args[0] && command.configApi;
169
+ const result = await execute(inspect ? process.execPath : command.executable, inspect ? [
170
+ '--input-type=module',
171
+ '--eval',
172
+ inspectNativeConfig,
173
+ command.configApi,
174
+ command.executable,
175
+ args[args.indexOf('--project') + 1]
176
+ ] : [
139
177
  ...command.args,
140
178
  ...args
141
179
  ], {
142
180
  cwd: node_path.dirname(this.options.configFile),
143
- maxBuffer: 16777216
181
+ env: {
182
+ ...process.env,
183
+ TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go'
184
+ },
185
+ maxBuffer: 16777216,
186
+ ...inspect ? {
187
+ timeout: 10000
188
+ } : {}
144
189
  });
145
190
  return result.stdout;
146
191
  } catch (cause) {
147
192
  const output = cause;
148
193
  const diagnostics = `${output.stdout ?? ''}${output.stderr ?? ''}`;
149
- throw new Error(`${native_type_checker_name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}${missingJsxRuntimeHint(diagnostics)}`, {
194
+ throw Object.assign(new Error(`${native_type_checker_name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}${missingJsxRuntimeHint(diagnostics)}`, {
150
195
  cause
196
+ }), {
197
+ code: output.code,
198
+ signal: output.signal
151
199
  });
152
200
  }
153
201
  }
@@ -209,9 +257,7 @@ class UltramodernNativeTypeChecker {
209
257
  this.removeProjection(configFile);
210
258
  }
211
259
  } catch (cause) {
212
- compilation.errors.push(new Error(cause instanceof Error ? cause.message : String(cause), {
213
- cause
214
- }));
260
+ compilation.errors.push(cause instanceof Error ? cause : new Error(String(cause)));
215
261
  }
216
262
  });
217
263
  });
@@ -20,7 +20,7 @@ const gitOutput = (workspaceRoot, args)=>{
20
20
  return;
21
21
  }
22
22
  };
23
- const createUltramodernReleaseBuildMarker = ({ generationBuildMarker, sourceRevision, unitId })=>createHash('sha256').update(`${BUILD_MARKER_NAMESPACE}:${unitId}:${generationBuildMarker}:${sourceRevision}`).digest('hex').slice(0, 16);
23
+ const createUltramodernReleaseBuildMarker = ({ generationBuildMarker, sourceRevision, unitId })=>createHash('sha256').update(`${BUILD_MARKER_NAMESPACE}:${unitId}:${generationBuildMarker}:${sourceRevision}`).digest('hex');
24
24
  const resolveUltramodernSourceRevision = (workspaceRoot = process.cwd(), configuredSourceRevision, environment = process.env)=>{
25
25
  const configuredArgumentValue = configuredSourceRevision?.trim();
26
26
  const configuredEnvironmentValue = environment.ULTRAMODERN_SOURCE_REVISION?.trim();
@@ -10,6 +10,7 @@ const LOCKFILES = [
10
10
  'bun.lock',
11
11
  'bun.lockb'
12
12
  ];
13
+ const RELEASE_BUILD_MARKER_PATTERN = /^[a-f0-9]{64}$/u;
13
14
  const processNonce = randomUUID();
14
15
  const sha256 = (value)=>createHash('sha256').update(value).digest('hex');
15
16
  function rendererProfileKey(profile) {
@@ -54,12 +55,21 @@ function resolveRendererBuildIdentities(options) {
54
55
  const projectRoot = node_path.resolve(options.projectRoot);
55
56
  const appId = options.appId ?? JSON.parse(node_fs.readFileSync(node_path.join(projectRoot, 'package.json'), 'utf8')).name;
56
57
  if ('string' != typeof appId || !appId || appId.trim() !== appId) throw new Error('Renderer application identity requires a delivery artifact appId or a package name.');
58
+ const { deliveryUnit } = options;
59
+ if (void 0 !== deliveryUnit && ('string' != typeof deliveryUnit.unitId || !deliveryUnit.unitId || deliveryUnit.unitId.trim() !== deliveryUnit.unitId)) throw new Error('Renderer delivery-unit identity requires a nonempty trimmed unitId.');
57
60
  const profileKey = rendererProfileKey(options.profile);
58
- const sourceRevision = resolveUltramodernSourceRevision(projectRoot, options.sourceRevision);
59
- const buildId = sha256(JSON.stringify([
61
+ const sourceRevision = resolveUltramodernSourceRevision(projectRoot, deliveryUnit ? deliveryUnit.sourceRevision : options.sourceRevision);
62
+ const promotable = 'production' === options.mode && 'workspace' !== sourceRevision;
63
+ let buildId;
64
+ if (promotable && deliveryUnit) {
65
+ if (deliveryUnit.sourceRevision !== sourceRevision || !RELEASE_BUILD_MARKER_PATTERN.test(deliveryUnit.buildMarker)) throw new Error(`Renderer delivery unit ${deliveryUnit.unitId} must carry the release identity of source revision ${sourceRevision}.`);
66
+ buildId = deliveryUnit.buildMarker;
67
+ } else buildId = sha256(JSON.stringify([
68
+ appId,
69
+ deliveryUnit?.unitId ?? null,
60
70
  profileKey,
61
71
  lockfileHash(projectRoot),
62
- 'development' === options.mode || 'workspace' === sourceRevision ? processNonce : sourceRevision
72
+ promotable ? sourceRevision : processNonce
63
73
  ]));
64
74
  const identities = Object.fromEntries(entries.map((entryName)=>[
65
75
  entryName,
@@ -306,7 +306,24 @@ async function invokeNativeRouteWorker(route, identity, request, env, ctx) {
306
306
  const resources =
307
307
  nativeRenderer?.renderer === identity.renderer &&
308
308
  Object.hasOwn(nativeRenderer.entries, route.entryName)
309
- ? nativeRenderer.entries[route.entryName]
309
+ ? {
310
+ ...nativeRenderer.entries[route.entryName],
311
+ ...(Object.hasOwn(nativeRenderer, 'workerFederation')
312
+ ? { workerFederation: nativeRenderer.workerFederation }
313
+ : {}),
314
+ ...(Object.hasOwn(nativeRenderer, 'workerFederationManifestUrl')
315
+ ? {
316
+ workerFederationManifestUrl:
317
+ nativeRenderer.workerFederationManifestUrl,
318
+ }
319
+ : {}),
320
+ ...(Object.hasOwn(nativeRenderer, 'workerFederationServices')
321
+ ? {
322
+ workerFederationServices:
323
+ nativeRenderer.workerFederationServices,
324
+ }
325
+ : {}),
326
+ }
310
327
  : undefined;
311
328
  // Native renderers have no Flight transport. Reject before the bundle loads.
312
329
  if (
@@ -10,8 +10,8 @@ const owningManifest = findPackageJSON(owningModuleUrl, owningModuleUrl);
10
10
  if (!owningManifest) throw new Error('Cannot find owning build-config package manifest');
11
11
  const owningName = JSON.parse(readFileSync(owningManifest, 'utf8')).name;
12
12
  if ('string' != typeof owningName || 0 === owningName.length) throw new Error(`Invalid owning build-config package name: ${owningManifest}`);
13
- const { effectCompilerDiscoveryFailureStage: effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler: resolveInstalledEffectCompiler } = createRequire(owningModuleUrl)(`${owningName}/internal-effect-discovery`);
14
- const EFFECT_TSGO_RESOLUTION_ERROR = 'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN.';
13
+ const { effectCompilerDiscoveryFailureStage: effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler: resolveInstalledEffectCompiler, validateConfiguredEffectCompiler: validateConfiguredEffectCompiler } = createRequire(owningModuleUrl)(`${owningName}/internal-effect-discovery`);
14
+ const EFFECT_TSGO_RESOLUTION_ERROR = 'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN. Bundled tsrs requires matching installed tsrs metadata.';
15
15
  const executableEffectTsgoCompilers = new Map();
16
16
  function effectTsgoResolutionError(cause, stage, from) {
17
17
  const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
@@ -20,10 +20,10 @@ function effectTsgoResolutionError(cause, stage, from) {
20
20
  });
21
21
  }
22
22
  function resolveExecutableEffectTsgoCompiler(compilerPath) {
23
- if ('win32' === process.platform) return compilerPath;
24
23
  const sourcePath = realpathSync(compilerPath);
25
24
  const source = lstatSync(sourcePath);
26
- if (!source.isFile()) throw new Error(`Effect TS-Go compiler path is not a regular file: ${sourcePath}`);
25
+ if (!source.isFile() || 0 === source.size) throw new Error(`Effect TS-Go compiler path is not a nonempty regular file: ${sourcePath}`);
26
+ if ('win32' === process.platform) return compilerPath;
27
27
  try {
28
28
  accessSync(sourcePath, constants.X_OK);
29
29
  return compilerPath;
@@ -73,7 +73,9 @@ function getBuildConfigEnvironment(name) {
73
73
  function resolveEffectTsgoCompiler(options) {
74
74
  const configuredCompiler = getBuildConfigEnvironment('EFFECT_TSGO_BIN')?.trim();
75
75
  if (configuredCompiler) try {
76
- return resolveExecutableEffectTsgoCompiler(configuredCompiler);
76
+ const compiler = resolveExecutableEffectTsgoCompiler(configuredCompiler);
77
+ validateConfiguredEffectCompiler(compiler, options.from);
78
+ return compiler;
77
79
  } catch (cause) {
78
80
  throw effectTsgoResolutionError(cause, 'Configured compiler validation', options.from);
79
81
  }
@@ -1,4 +1,5 @@
1
1
  import "node:module";
2
+ import { execFileSync } from "node:child_process";
2
3
  import { lstatSync, readFileSync, realpathSync } from "node:fs";
3
4
  import { createRequire } from "node:module";
4
5
  import { dirname, join, resolve } from "node:path";
@@ -9,7 +10,14 @@ const NATIVE_TYPESCRIPT_PACKAGES = [
9
10
  "typescript",
10
11
  "@typescript/native"
11
12
  ];
13
+ const EFFECT_TSRS_PROVIDER_VERSION = '0.59.0';
14
+ const EFFECT_TSRS_TARGETS = new Set([
15
+ '@tsrs/darwin-arm64',
16
+ '@tsrs/linux-x64-gnu',
17
+ '@tsrs/linux-arm64-gnu'
18
+ ]);
12
19
  const failureStages = new WeakMap();
20
+ const reportedUnsupportedTsrsTargets = new Set();
13
21
  function effectCompilerDiscoveryFailureStage(error) {
14
22
  return error && 'object' == typeof error ? failureStages.get(error) : void 0;
15
23
  }
@@ -27,7 +35,7 @@ function resolveEffectTsgoPackage(from) {
27
35
  cliPath
28
36
  };
29
37
  }
30
- function isAbsentNativeTypeScriptPackage(projectRequire, from, packageName, error) {
38
+ function isAbsentPackage(projectRequire, from, packageName, error) {
31
39
  const filename = 'string' != typeof from || from.startsWith('file:') ? fileURLToPath(from) : from;
32
40
  if (!(error instanceof Error) || !('code' in error) || 'MODULE_NOT_FOUND' !== error.code || 'path' in error && void 0 !== error.path || error.message.split('\n')[0] !== `Cannot find module '${packageName}/package.json'` || !('requireStack' in error) || !Array.isArray(error.requireStack) || error.requireStack[0] !== filename) return false;
33
41
  for (const searchDirectory of projectRequire.resolve.paths(packageName) ?? [])try {
@@ -48,7 +56,7 @@ function resolveNativeTypeScriptPackage(from) {
48
56
  try {
49
57
  packageJsonPath = projectRequire.resolve(`${packageName}/package.json`);
50
58
  } catch (error) {
51
- if (isAbsentNativeTypeScriptPackage(projectRequire, from, packageName, error)) continue;
59
+ if (isAbsentPackage(projectRequire, from, packageName, error)) continue;
52
60
  throw error;
53
61
  }
54
62
  const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
@@ -72,10 +80,85 @@ function readCompilerArtifact(filename) {
72
80
  if (0 === stats.size) throw new Error(`Compiler artifact is empty: ${filename}`);
73
81
  return canonicalPath;
74
82
  }
83
+ function resolveTsrsManifest(from) {
84
+ const projectRequire = createRequire(from);
85
+ try {
86
+ return projectRequire.resolve('tsrs/package.json');
87
+ } catch (error) {
88
+ if (isAbsentPackage(projectRequire, from, 'tsrs', error)) return;
89
+ throw error;
90
+ }
91
+ }
92
+ function compilerVersion(compiler) {
93
+ return execFileSync(compiler, [
94
+ '--version'
95
+ ], {
96
+ encoding: 'utf8',
97
+ env: {
98
+ ...process.env,
99
+ TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go'
100
+ },
101
+ timeout: 10000,
102
+ maxBuffer: 1048576
103
+ }).trim();
104
+ }
105
+ function tsrsPlatform(manifestPath) {
106
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
107
+ const { getPlatformPackageName } = createRequire(manifestPath)(join(dirname(manifestPath), 'lib/platform.js'));
108
+ const nativeName = getPlatformPackageName();
109
+ const embedded = manifest.tsrs;
110
+ if ('string' != typeof nativeName || !nativeName || !manifest.optionalDependencies || 'object' != typeof manifest.optionalDependencies || Array.isArray(manifest.optionalDependencies) || 0 === Object.keys(manifest.optionalDependencies).length) throw new Error(`Invalid native tsrs capability metadata: ${manifestPath}`);
111
+ if ('string' != typeof manifest.name || !manifest.name || 'string' != typeof manifest.version || !/^\d+\.\d+\.\d+-ts[\w.-]+$/u.test(manifest.version) || 'string' != typeof embedded?.typescriptVersion || !/^7\.\d+\.\d+(?:[-+][\w.-]+)?$/u.test(embedded.typescriptVersion) || !manifest.version.endsWith(`-ts${embedded.typescriptVersion}`) || 'string' != typeof embedded.typescriptCommit || !/^[a-f\d]{40}$/u.test(embedded.typescriptCommit)) throw new Error(`Invalid native tsrs metadata: ${manifestPath}`);
112
+ const supported = Object.hasOwn(manifest.optionalDependencies, nativeName);
113
+ if (supported && manifest.optionalDependencies[nativeName] !== manifest.version) throw new Error(`Native tsrs target version does not match its selected package: ${manifestPath}`);
114
+ return {
115
+ manifest: manifest,
116
+ nativeName,
117
+ supported
118
+ };
119
+ }
120
+ function resolveTsrsCompiler(manifestPath, effect, configuredCompiler, configuredVersion) {
121
+ const { manifest } = tsrsPlatform(manifestPath);
122
+ const embedded = manifest.tsrs;
123
+ const effectName = `@effect/tsgo-${process.platform}-${process.arch}`;
124
+ const effectManifest = createRequire(effect.cliPath).resolve(`${effectName}/package.json`);
125
+ const platform = JSON.parse(readFileSync(effectManifest, 'utf8'));
126
+ const metadataPath = join(dirname(effectManifest), 'lib/upstream.json');
127
+ const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
128
+ const component = metadata.components?.tsrs?.[manifest.version];
129
+ if (platform.name !== effectName || platform.version !== effect.packageJson.version || 5 !== metadata.schemaVersion || !/^[a-f\d]{40}$/u.test(component?.gitHead ?? '') || component?.typescript?.version !== embedded.typescriptVersion || component.typescript.gitHead !== embedded.typescriptCommit) throw new Error(`Effect replacement metadata does not match tsrs: ${metadataPath}`);
130
+ const compiler = readCompilerArtifact(configuredCompiler ?? join(dirname(effectManifest), 'artifacts/tsrs', manifest.version, 'win32' === process.platform ? 'tsrs.exe' : 'tsrs'));
131
+ const version = configuredVersion ?? compilerVersion(compiler);
132
+ const stamp = /\+effect-tsgo\.([\d.]+)\s+\(tsrs ([^,]+), microsoft\/TypeScript@([a-f\d]+)\)$/u.exec(version);
133
+ if (!stamp || stamp[1] !== effect.packageJson.version || stamp[2] !== manifest.version || stamp[3].length < 12 || !embedded.typescriptCommit.startsWith(stamp[3])) throw new Error(`Effect-enabled tsrs matching ${manifest.version} and @effect/tsgo@${effect.packageJson.version} is required: ${compiler}. Install matching packages or select a matching Effect-enabled candidate with EFFECT_TSGO_BIN.`);
134
+ return compiler;
135
+ }
136
+ function validateConfiguredEffectCompiler(compiler, from) {
137
+ const version = compilerVersion(compiler);
138
+ if (!/^Version \d+\.\d+\.\d+[\w.-]*\+effect-tsgo\.\d+\.\d+\.\d+(?:\s|$)/u.test(version)) throw new Error(`EFFECT_TSGO_BIN must select an Effect-enabled native compiler: ${compiler}`);
139
+ if (version.includes('(tsrs ')) {
140
+ const manifest = resolveTsrsManifest(from);
141
+ if (!manifest) throw new Error(`Cannot validate tsrs without its installed package metadata for ${String(from)}`);
142
+ resolveTsrsCompiler(manifest, resolveEffectTsgoPackage(from), compiler, version);
143
+ }
144
+ }
75
145
  function resolveInstalledEffectCompiler(from) {
76
146
  let stage = 'Package CLI resolution';
77
147
  try {
78
148
  const effect = resolveEffectTsgoPackage(from);
149
+ stage = 'Native tsrs package resolution';
150
+ const tsrsManifest = resolveTsrsManifest(from);
151
+ const tsrsTarget = tsrsManifest ? tsrsPlatform(tsrsManifest) : void 0;
152
+ if (tsrsTarget && effect.packageJson.version !== EFFECT_TSRS_PROVIDER_VERSION) throw new Error(`Unreviewed Effect tsrs capability contract: ${effect.packageJson.version}`);
153
+ const configuredTsrsCompiler = process.env.TSRS_BINARY || void 0;
154
+ if (tsrsManifest && tsrsTarget?.supported && (configuredTsrsCompiler || EFFECT_TSRS_TARGETS.has(tsrsTarget.nativeName))) {
155
+ stage = 'Native tsrs validation';
156
+ return resolveTsrsCompiler(tsrsManifest, effect, configuredTsrsCompiler);
157
+ }
158
+ if (tsrsTarget && !reportedUnsupportedTsrsTargets.has(tsrsTarget.nativeName)) {
159
+ reportedUnsupportedTsrsTargets.add(tsrsTarget.nativeName);
160
+ console.warn(`[UltraModern] ${tsrsTarget.supported ? `@effect/tsgo@${effect.packageJson.version} does not provide a tsrs replacement for ${tsrsTarget.nativeName}` : `tsrs does not declare ${tsrsTarget.nativeName}`}; selecting the Effect-enabled TypeScript-Go compiler.`);
161
+ }
79
162
  stage = 'Native TypeScript package resolution';
80
163
  const typeScriptManifest = resolveNativeTypeScriptManifest(from);
81
164
  const typeScript = JSON.parse(readFileSync(typeScriptManifest, 'utf8'));
@@ -93,11 +176,13 @@ function resolveInstalledEffectCompiler(from) {
93
176
  const metadataPath = join(dirname(effectManifest), 'lib', 'upstream.json');
94
177
  const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
95
178
  const component = metadata.components?.typescript?.[native.version];
96
- if (5 !== metadata.schemaVersion || component?.provider !== "typescript-go" || component.gitHead !== native.gitHead) throw new Error(`Effect replacement metadata does not match the selected native TypeScript backend: ${metadataPath}`);
97
- return readCompilerArtifact(join(dirname(effectManifest), 'artifacts', "typescript", native.version, binaryName));
179
+ const latestTypeScript = metadata.tags?.typescript?.latest;
180
+ if (5 !== metadata.schemaVersion || 'string' != typeof latestTypeScript || component?.provider !== "typescript-go" || component.gitHead !== native.gitHead) throw new Error(`Effect replacement metadata does not match the selected native TypeScript backend: ${metadataPath}`);
181
+ const compilerDirectory = native.version === latestTypeScript ? join(dirname(effectManifest), 'lib') : join(dirname(effectManifest), 'artifacts', "typescript", native.version);
182
+ return readCompilerArtifact(join(compilerDirectory, binaryName));
98
183
  } catch (error) {
99
184
  if (error && 'object' == typeof error && !failureStages.has(error)) failureStages.set(error, stage);
100
185
  throw error;
101
186
  }
102
187
  }
103
- export { effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler };
188
+ export { effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler, validateConfiguredEffectCompiler };
@@ -2,7 +2,8 @@ import "node:module";
2
2
  import promises from "node:fs/promises";
3
3
  import node_path from "node:path";
4
4
  import { addIssue } from "./issues.mjs";
5
- const SOURCE_SCAN_FILE_PATTERN = /\.(?:[cm]?[jt]s|json)$/u;
5
+ const SOURCE_SCAN_FILE_PATTERN = /\.(?:[cm]?[jt]sx?)$/u;
6
+ const DECLARATION_FILE_PATTERN = /\.d\.[cm]?ts$/u;
6
7
  const SOURCE_SCAN_IGNORED_DIRECTORY_NAMES = new Set([
7
8
  '.codex',
8
9
  '.fastcontext',
@@ -75,7 +76,7 @@ const walkFiles = async (root)=>{
75
76
  files.push(...await walkFiles(entryPath));
76
77
  continue;
77
78
  }
78
- if (SOURCE_SCAN_FILE_PATTERN.test(entry.name)) files.push(entryPath);
79
+ if ('package.json' === entry.name || SOURCE_SCAN_FILE_PATTERN.test(entry.name) && !DECLARATION_FILE_PATTERN.test(entry.name)) files.push(entryPath);
79
80
  }
80
81
  return files;
81
82
  };
@@ -92,7 +93,26 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
92
93
  if ('ENOENT' === error.code) continue;
93
94
  throw error;
94
95
  }
95
- for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (forbidden.pattern.test(source)) addIssue(issues, {
96
+ let executableSources = [
97
+ source
98
+ ];
99
+ if ('package.json' === node_path.basename(file)) try {
100
+ const manifest = JSON.parse(source);
101
+ if (!isRecord(manifest)) throw new TypeError('Package metadata must be an object.');
102
+ const scripts = Object.hasOwn(manifest, "scripts") ? manifest.scripts : {};
103
+ if (!isRecord(scripts)) throw new TypeError("Package scripts must be an object.");
104
+ const commands = Object.values(scripts);
105
+ if (!commands.every((value)=>'string' == typeof value)) throw new TypeError("Package scripts must contain only strings.");
106
+ executableSources = commands;
107
+ } catch {
108
+ addIssue(issues, {
109
+ code: 'invalid-package',
110
+ message: "Cannot verify executable package lifecycle scripts: invalid package metadata.",
111
+ path: file
112
+ });
113
+ continue;
114
+ }
115
+ for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (executableSources.some((value)=>forbidden.pattern.test(value))) addIssue(issues, {
96
116
  code: forbidden.code,
97
117
  message: forbidden.message,
98
118
  path: file
@@ -103,4 +123,5 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
103
123
  issues
104
124
  };
105
125
  };
126
+ const isRecord = (value)=>null !== value && 'object' == typeof value && !Array.isArray(value);
106
127
  export { verifyCloudflareOutputMutationPolicy, walkFiles };
@@ -1,30 +1,60 @@
1
1
  import "node:module";
2
- import { execFile } from "node:child_process";
2
+ import { execFile, execFileSync } from "node:child_process";
3
3
  import { randomUUID } from "node:crypto";
4
- import { existsSync, readFileSync, realpathSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { existsSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from "node:fs";
5
+ import { createRequire } from "node:module";
5
6
  import node_path from "node:path";
6
7
  import { promisify } from "node:util";
7
8
  import { refreshTsgoCheckerConfig } from "@modern-js/builder";
8
9
  import { parse, printParseErrorCode } from "jsonc-parser";
10
+ import { resolveEffectTsgoCompiler } from "./build-config/build-environment.mjs";
9
11
  const execute = promisify(execFile);
10
12
  const native_type_checker_name = 'UltramodernNativeTypeChecker';
11
- function resolveNativeTypeCheckerCommand(packageJsonPath) {
12
- const manifestFile = realpathSync(packageJsonPath);
13
- const directory = node_path.dirname(manifestFile);
14
- const manifest = JSON.parse(readFileSync(manifestFile, 'utf8'));
15
- if ("typescript" !== manifest.name || '7.0.2' !== manifest.version) throw new Error(`${native_type_checker_name} requires typescript@7.0.2; found ${String(manifest.name)}@${String(manifest.version)} at ${manifestFile}.`);
16
- const bin = manifest.bin?.tsc;
17
- if ('string' != typeof bin || !bin || node_path.isAbsolute(bin)) throw new Error(`${native_type_checker_name} requires the selected package's public tsc bin.`);
18
- const launcher = realpathSync(node_path.resolve(directory, bin));
19
- const relative = node_path.relative(directory, launcher);
20
- if (!relative || node_path.isAbsolute(relative) || '..' === relative || relative.startsWith(`..${node_path.sep}`) || !statSync(launcher).isFile()) throw new Error(`${native_type_checker_name} public tsc bin must belong to its selected package.`);
13
+ function resolveNativeTypeCheckerCommand(from) {
14
+ const executable = resolveEffectTsgoCompiler({
15
+ from
16
+ });
17
+ const version = execFileSync(executable, [
18
+ '--version'
19
+ ], {
20
+ encoding: 'utf8',
21
+ env: {
22
+ ...process.env,
23
+ TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go'
24
+ },
25
+ timeout: 10000
26
+ });
27
+ if (version.includes('(tsrs ')) return {
28
+ executable,
29
+ args: [],
30
+ configApi: createRequire(from).resolve('tsrs/sync')
31
+ };
21
32
  return {
22
- executable: process.execPath,
23
- args: [
24
- launcher
25
- ]
33
+ executable,
34
+ args: []
26
35
  };
27
36
  }
37
+ const inspectNativeConfig = `
38
+ import { pathToFileURL } from 'node:url';
39
+ const [modulePath, compiler, configFile] = process.argv.slice(1);
40
+ const { API, formatDiagnostics } = await import(pathToFileURL(modulePath).href);
41
+ const api = new API({ tsserverPath: compiler });
42
+ try {
43
+ const config = api.parseConfigFile(configFile);
44
+ if (config.errors.length) {
45
+ process.stdout.write(formatDiagnostics(config.errors, api));
46
+ process.exitCode = 1;
47
+ } else {
48
+ process.stdout.write(JSON.stringify({
49
+ compilerOptions: config.options,
50
+ files: config.fileNames,
51
+ references: config.projectReferences,
52
+ }));
53
+ }
54
+ } finally {
55
+ api.close();
56
+ }
57
+ `;
28
58
  const watchDependencyPath = (file)=>{
29
59
  if ('win32' !== process.platform) return file;
30
60
  try {
@@ -136,19 +166,37 @@ class UltramodernNativeTypeChecker {
136
166
  executable: selected,
137
167
  args: []
138
168
  } : selected;
139
- const result = await execute(command.executable, [
169
+ const inspect = '--showConfig' === args[0] && command.configApi;
170
+ const result = await execute(inspect ? process.execPath : command.executable, inspect ? [
171
+ '--input-type=module',
172
+ '--eval',
173
+ inspectNativeConfig,
174
+ command.configApi,
175
+ command.executable,
176
+ args[args.indexOf('--project') + 1]
177
+ ] : [
140
178
  ...command.args,
141
179
  ...args
142
180
  ], {
143
181
  cwd: node_path.dirname(this.options.configFile),
144
- maxBuffer: 16777216
182
+ env: {
183
+ ...process.env,
184
+ TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go'
185
+ },
186
+ maxBuffer: 16777216,
187
+ ...inspect ? {
188
+ timeout: 10000
189
+ } : {}
145
190
  });
146
191
  return result.stdout;
147
192
  } catch (cause) {
148
193
  const output = cause;
149
194
  const diagnostics = `${output.stdout ?? ''}${output.stderr ?? ''}`;
150
- throw new Error(`${native_type_checker_name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}${missingJsxRuntimeHint(diagnostics)}`, {
195
+ throw Object.assign(new Error(`${native_type_checker_name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}${missingJsxRuntimeHint(diagnostics)}`, {
151
196
  cause
197
+ }), {
198
+ code: output.code,
199
+ signal: output.signal
152
200
  });
153
201
  }
154
202
  }
@@ -210,9 +258,7 @@ class UltramodernNativeTypeChecker {
210
258
  this.removeProjection(configFile);
211
259
  }
212
260
  } catch (cause) {
213
- compilation.errors.push(new Error(cause instanceof Error ? cause.message : String(cause), {
214
- cause
215
- }));
261
+ compilation.errors.push(cause instanceof Error ? cause : new Error(String(cause)));
216
262
  }
217
263
  });
218
264
  });
@@ -21,7 +21,7 @@ const gitOutput = (workspaceRoot, args)=>{
21
21
  return;
22
22
  }
23
23
  };
24
- const createUltramodernReleaseBuildMarker = ({ generationBuildMarker, sourceRevision, unitId })=>createHash('sha256').update(`${BUILD_MARKER_NAMESPACE}:${unitId}:${generationBuildMarker}:${sourceRevision}`).digest('hex').slice(0, 16);
24
+ const createUltramodernReleaseBuildMarker = ({ generationBuildMarker, sourceRevision, unitId })=>createHash('sha256').update(`${BUILD_MARKER_NAMESPACE}:${unitId}:${generationBuildMarker}:${sourceRevision}`).digest('hex');
25
25
  const resolveUltramodernSourceRevision = (workspaceRoot = process.cwd(), configuredSourceRevision, environment = process.env)=>{
26
26
  const configuredArgumentValue = configuredSourceRevision?.trim();
27
27
  const configuredEnvironmentValue = environment.ULTRAMODERN_SOURCE_REVISION?.trim();