@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.11 → 3.9.0-ultramodern.13

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.
@@ -2,8 +2,9 @@ import "node:module";
2
2
  import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
4
  import { parse } from "@babel/parser";
5
- import { configuredMicroVerticalApiStem, consumerParserPlugins, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
5
+ import { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
6
6
  import { baselinePackage, resolveBaselinePackageDirectory } from "./microvertical-api-owner.js";
7
+ import { consumerParserPlugins } from "./source-analysis.js";
7
8
  import { createEffectApiImportResolver, strictEffectRuntimeTopologyViolation } from "./strict-effect-runtime.js";
8
9
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
9
10
  const ignored = new Set([
@@ -20,13 +21,42 @@ const normalize = (file)=>file.split(node_path.sep).join('/').replace(/^\.\//u,
20
21
  const errorMessage = (error)=>error instanceof Error ? error.message : String(error);
21
22
  const apiApp = (app)=>'vertical' === app.kind && 'ui-only' !== app.surfaceProfile && 'horizontal-remote' !== app.deliveryUnitKind;
22
23
  function configuredApps(root, supplied) {
23
- const source = supplied ?? JSON.parse(node_fs.readFileSync(node_path.join(root, '.modernjs/ultramodern.json'), 'utf8')).topology?.apps;
24
- if (!Array.isArray(source)) throw new Error('.modernjs/ultramodern.json: topology.apps must be an array');
24
+ const topology = supplied ? void 0 : JSON.parse(node_fs.readFileSync(node_path.join(root, 'topology/reference-topology.json'), 'utf8'));
25
+ if (topology && (!topology.shell || !Array.isArray(topology.verticals) || void 0 !== topology.shells && !Array.isArray(topology.shells))) throw new Error('topology/reference-topology.json: shell and verticals must be present');
26
+ const entries = topology?.shell ? [
27
+ topology.shell,
28
+ ...topology.shells ?? [],
29
+ ...topology.verticals ?? []
30
+ ] : void 0;
31
+ const source = supplied ?? entries?.map((entry)=>{
32
+ if (!entry || 'string' != typeof entry.path || !entry.path || node_path.isAbsolute(entry.path) || entry.path.split(/[\\/]/u).includes('..')) throw new Error('topology/reference-topology.json: each app must have an explicit workspace path');
33
+ const packageFile = node_path.join(root, entry.path, 'package.json');
34
+ const manifest = JSON.parse(node_fs.readFileSync(packageFile, 'utf8'));
35
+ if ('string' != typeof manifest.name || !manifest.name || void 0 !== entry.package && entry.package !== manifest.name) throw new Error(`${entry.path}/package.json: package name must match topology`);
36
+ const api = entry.api;
37
+ const stem = api?.stem ?? api?.readiness?.endpoint?.match(/^\/([a-z0-9]+(?:-[a-z0-9]+)*)\/readiness$/u)?.[1];
38
+ return {
39
+ id: entry.id,
40
+ kind: entry.kind,
41
+ path: entry.path,
42
+ package: manifest.name,
43
+ surfaceProfile: entry.surfaceProfile,
44
+ deliveryUnitKind: entry.deliveryUnitKind,
45
+ ...void 0 === api ? {} : {
46
+ api: {
47
+ stem,
48
+ prefix: api.bff?.prefix,
49
+ protocol: api.protocol
50
+ }
51
+ }
52
+ };
53
+ });
54
+ if (!Array.isArray(source)) throw new Error('topology/reference-topology.json: shell and verticals must describe apps');
25
55
  const paths = new Set();
26
56
  for (const app of source){
27
- if (!app || 'object' != typeof app || 'string' != typeof app.path || !app.path || node_path.isAbsolute(app.path) || app.path.split(/[\\/]/u).includes('..')) throw new Error('.modernjs/ultramodern.json: each app must have a relative workspace path');
57
+ if (!app || 'object' != typeof app || 'string' != typeof app.path || !app.path || node_path.isAbsolute(app.path) || app.path.split(/[\\/]/u).includes('..')) throw new Error('topology/reference-topology.json: each app must have a relative workspace path');
28
58
  const key = normalize(app.path);
29
- if (paths.has(key)) throw new Error(`.modernjs/ultramodern.json: duplicate app path ${key}`);
59
+ if (paths.has(key)) throw new Error(`topology/reference-topology.json: duplicate app path ${key}`);
30
60
  paths.add(key);
31
61
  for (const name of [
32
62
  'id',
@@ -35,7 +65,7 @@ function configuredApps(root, supplied) {
35
65
  'surfaceProfile',
36
66
  'deliveryUnitKind'
37
67
  ])if (void 0 !== app[name] && 'string' != typeof app[name]) throw new Error(`${key}: ${name} must be a string`);
38
- for (const [field, allowed] of Object.entries({
68
+ const allowedFields = {
39
69
  kind: [
40
70
  'shell',
41
71
  'vertical'
@@ -49,7 +79,11 @@ function configuredApps(root, supplied) {
49
79
  'microvertical',
50
80
  'horizontal-remote'
51
81
  ]
52
- }))if (void 0 !== app[field] && !allowed.includes(app[field])) throw new Error(`${key}: invalid ${field}`);
82
+ };
83
+ for (const field of Object.keys(allowedFields)){
84
+ const value = app[field];
85
+ if (void 0 !== value && !allowedFields[field].includes(value)) throw new Error(`${key}: invalid ${field}`);
86
+ }
53
87
  if (void 0 !== app.api) {
54
88
  if (!app.api || 'object' != typeof app.api || Array.isArray(app.api)) throw new Error(`${key}: api must be an object`);
55
89
  if (void 0 !== app.api.protocol && ![
@@ -137,12 +171,27 @@ function check(options, topology) {
137
171
  `${base}/index.tsx`
138
172
  ].find((candidate)=>exists(candidate));
139
173
  };
140
- const composedModules = (file)=>{
174
+ const clientExportFile = (appPath, exported)=>{
175
+ if ('string' != typeof exported || !exported.startsWith('./') || exported.split(/[\\/]/u).includes('..') || !/\.[cm]?[jt]sx?$/u.test(exported)) return;
176
+ const appDirectory = absolute(appPath);
177
+ const candidate = node_path.resolve(appDirectory, exported);
178
+ try {
179
+ const workspace = node_fs.realpathSync(root);
180
+ const owner = node_fs.realpathSync(appDirectory);
181
+ const real = node_fs.realpathSync(candidate);
182
+ if (!owner.startsWith(`${workspace}${node_path.sep}`) || !real.startsWith(`${owner}${node_path.sep}`) || !node_fs.statSync(real).isFile()) return;
183
+ } catch {
184
+ return;
185
+ }
186
+ return normalize(node_path.relative(root, candidate));
187
+ };
188
+ const composedModules = (file, appPath)=>{
141
189
  const order = [];
142
190
  const seen = new Set();
143
191
  const queue = [
144
192
  file
145
193
  ];
194
+ const owner = node_fs.realpathSync(absolute(appPath));
146
195
  while(queue.length > 0 && order.length < 128){
147
196
  const current = queue.shift();
148
197
  if (seen.has(current)) continue;
@@ -153,16 +202,19 @@ function check(options, topology) {
153
202
  for (const statement of ast.program.body){
154
203
  const source = (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement)) && statement.source ? statement.source.value : void 0;
155
204
  const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
156
- if (void 0 !== resolved && !seen.has(resolved)) queue.push(resolved);
205
+ if (void 0 !== resolved && !seen.has(resolved)) {
206
+ const real = node_fs.realpathSync(absolute(resolved));
207
+ if (real.startsWith(`${owner}${node_path.sep}`)) queue.push(resolved);
208
+ }
157
209
  }
158
210
  }
159
211
  return order;
160
212
  };
161
- const moduleShape = (file, imports, calls, patterns = [], defaultExport, composed = false)=>{
213
+ const moduleShape = (file, imports, calls, patterns = [], defaultExport, composedAppPath)=>{
162
214
  if (!exists(file)) return;
163
215
  const ast = parseFile(file);
164
216
  if (!ast) return;
165
- const shapeFiles = composed ? composedModules(file) : [
217
+ const shapeFiles = composedAppPath ? composedModules(file, composedAppPath) : [
166
218
  file
167
219
  ];
168
220
  const body = shapeFiles.flatMap((shapeFile)=>parseFile(shapeFile)?.program.body ?? []);
@@ -272,19 +324,25 @@ function check(options, topology) {
272
324
  }
273
325
  return;
274
326
  }
275
- if (app?.kind === 'vertical') assert(void 0 !== app.api, `${appPath}: vertical must declare its Effect API in .modernjs/ultramodern.json`);
327
+ if (app?.kind === 'vertical') assert(void 0 !== app.api, `${appPath}: vertical must declare its Effect API in topology/reference-topology.json`);
276
328
  const stem = app?.api?.stem ?? node_path.posix.basename(appPath);
277
329
  const rpc = app?.api?.protocol === 'rpc';
278
330
  const contract = `${appPath}/shared/${rpc ? 'rpc' : 'api'}.ts`;
279
- const client = `${appPath}/src/api/${stem}-${rpc ? 'rpc-client' : 'client'}.ts`;
280
331
  const entry = `${appPath}/api/index.ts`;
281
332
  for (const file of [
282
333
  entry,
283
- contract,
284
- client
334
+ contract
285
335
  ])assert(exists(file), `${file}: required API surface is missing`);
336
+ const packageFile = `${appPath}/package.json`;
337
+ assert(exists(packageFile), `${packageFile}: required API package is missing`);
338
+ if (!exists(packageFile)) return;
339
+ const manifest = JSON.parse(read(packageFile));
340
+ assert(manifest.exports?.['./api'] === `./shared/${rpc ? 'rpc' : 'api'}.ts`, `${packageFile}: invalid ./api export`);
341
+ const clientKey = `./api/${rpc ? 'rpc-client' : 'client'}`;
342
+ const client = clientExportFile(appPath, manifest.exports?.[clientKey]);
343
+ assert(void 0 !== client, `${packageFile}: invalid API client export`);
344
+ assert(manifest.exports?.[`./api/${rpc ? 'client' : 'rpc-client'}`] === void 0, `${packageFile}: forbidden opposite protocol client export`);
286
345
  noPath(`${appPath}/shared/${rpc ? 'api' : 'rpc'}.ts`, `must not emit a ${rpc ? 'REST' : 'RPC'} contract`);
287
- noPath(`${appPath}/src/api/${stem}-${rpc ? 'client' : 'rpc-client'}.ts`, `must not emit a ${rpc ? 'REST' : 'RPC'} client`);
288
346
  if (rpc) {
289
347
  moduleShape(entry, [
290
348
  [
@@ -350,21 +408,24 @@ function check(options, topology) {
350
408
  const violation = microVerticalApiBaselineViolation(stem, absolute(contract), expectation);
351
409
  if (violation) diagnostics.push(`${contract}: ${violation}`);
352
410
  }
353
- moduleShape(client, [
354
- [
355
- effect,
411
+ if (client) {
412
+ const relativeContract = node_path.posix.relative(node_path.posix.dirname(client), contract);
413
+ moduleShape(client, [
414
+ [
415
+ effect,
416
+ [
417
+ 'Effect',
418
+ rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
419
+ ]
420
+ ],
356
421
  [
357
- 'Effect',
358
- rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
422
+ relativeContract.startsWith('.') ? relativeContract : `./${relativeContract}`,
423
+ []
359
424
  ]
360
- ],
361
- [
362
- rpc ? '../../shared/rpc.ts' : '../../shared/api',
363
- []
364
- ]
365
- ], [
366
- rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
367
- ], [], void 0, true);
425
+ ], [
426
+ rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
427
+ ], [], void 0, appPath);
428
+ }
368
429
  moduleShape(`${appPath}/api/effect-api.ts`, [], [], [
369
430
  [
370
431
  /\bbackendFederationContract\b/u,
@@ -405,13 +466,6 @@ function check(options, topology) {
405
466
  'must enable strictEffectApproach'
406
467
  ]
407
468
  ]);
408
- const packageFile = `${appPath}/package.json`;
409
- if (exists(packageFile)) {
410
- const manifest = JSON.parse(read(packageFile));
411
- assert(manifest.exports?.['./api'] === `./shared/${rpc ? 'rpc' : 'api'}.ts`, `${packageFile}: invalid ./api export`);
412
- assert(manifest.exports?.[`./api/${rpc ? 'rpc-client' : 'client'}`] === `./src/api/${stem}-${rpc ? 'rpc-client' : 'client'}.ts`, `${packageFile}: invalid API client export`);
413
- assert(manifest.exports?.[`./api/${rpc ? 'client' : 'rpc-client'}`] === void 0, `${packageFile}: forbidden opposite protocol client export`);
414
- }
415
469
  });
416
470
  const shell = 'apps/shell-super-app';
417
471
  const verticals = apps.filter((app)=>app.path.startsWith('verticals/') && apiApp(app));
@@ -21,7 +21,7 @@ const createStrictEffectApiBoundariesRule = ()=>({
21
21
  });
22
22
  reportProgramPattern(context, node, source, /@modern-js\/plugin-bff\/(?:server|hono-server)(?![\w-])/u, 'UltraModern API workspaces must not import Hono server helpers; use @modern-js/bff-effect/effect-edge and HttpApi.');
23
23
  reportProgramPattern(context, node, source, /\bruntimeFramework\s*(?::|=)\s*['"]hono['"]/u, 'UltraModern API apps must use bff.runtimeFramework: "effect".');
24
- reportProgramPattern(context, node, source, /(?:from|import)\s*['"][^'"]*(?:api\/effect|shared\/effect)[^'"]*['"]|shared-effect-api/u, 'Import API code from direct api/index.ts, shared/api.ts or src/api/* paths, not api/effect, shared/effect or shared Effect API packages.');
24
+ reportProgramPattern(context, node, source, /(?:from|import)\s*['"][^'"]*(?:api\/effect|shared\/effect)(?=\/|\.[cm]?[jt]sx?['"]|['"])[^'"]*['"]|shared-effect-api/u, 'Import API code from direct api/index.ts, shared/api.ts or src/api/* paths, not api/effect, shared/effect or shared Effect API packages.');
25
25
  reportProgramPattern(context, node, source, /\bstrictEffectApproach\s*(?::|=)\s*false\b/u, 'UltraModern API apps must keep strictEffectApproach enabled.');
26
26
  if (isApiEntryFile(filename)) {
27
27
  const violation = strictEffectRuntimeTopologyViolation(source, createEffectApiImportResolver(filename));
@@ -0,0 +1,45 @@
1
+ import "node:module";
2
+ import { parse } from "@babel/parser";
3
+ import traverse, { Hub, NodePath } from "@babel/traverse";
4
+ import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
5
+ const consumerParserPlugins = (filePath)=>/\.[jt]sx$/u.test(filePath) ? [
6
+ "typescript",
7
+ 'jsx'
8
+ ] : [
9
+ "typescript"
10
+ ];
11
+ class SourceSyntaxError extends Error {
12
+ }
13
+ class SourceValidationHub extends Hub {
14
+ buildError(_node, message) {
15
+ return new SourceSyntaxError(message);
16
+ }
17
+ }
18
+ function parseSource(source, filename, syntaxMessage) {
19
+ try {
20
+ return parse(source, {
21
+ sourceType: 'module',
22
+ sourceFilename: filename,
23
+ plugins: consumerParserPlugins(filename)
24
+ });
25
+ } catch (cause) {
26
+ if ('object' == typeof cause && null !== cause && 'code' in cause && 'BABEL_PARSER_SYNTAX_ERROR' === cause.code && 'reasonCode' in cause && 'string' == typeof cause.reasonCode) throw new SourceSyntaxError(syntaxMessage ?? (cause instanceof Error ? cause.message : String(cause)));
27
+ throw cause;
28
+ }
29
+ }
30
+ function traverseSource(file, visitor) {
31
+ const program = NodePath.get({
32
+ hub: new SourceValidationHub(),
33
+ parentPath: void 0,
34
+ parent: file,
35
+ container: file,
36
+ key: 'program'
37
+ });
38
+ program.setContext();
39
+ traverse(file, visitor, program.scope, void 0, program);
40
+ }
41
+ function unwrapExpression(node, includeAssertions = false) {
42
+ while(__rspack_external__babel_types_6e8337dd.isTSAsExpression(node) || __rspack_external__babel_types_6e8337dd.isParenthesizedExpression(node) || __rspack_external__babel_types_6e8337dd.isTSSatisfiesExpression(node) || includeAssertions && (__rspack_external__babel_types_6e8337dd.isTSTypeAssertion(node) || __rspack_external__babel_types_6e8337dd.isTSNonNullExpression(node)))node = node.expression;
43
+ return node;
44
+ }
45
+ export { SourceSyntaxError, consumerParserPlugins, parseSource, traverseSource, unwrapExpression };
@@ -1,24 +1,13 @@
1
1
  import "node:module";
2
2
  import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
- import { parse } from "@babel/parser";
5
- import traverse, { Hub, NodePath } from "@babel/traverse";
4
+ import { SourceSyntaxError, parseSource, traverseSource, unwrapExpression } from "./source-analysis.js";
6
5
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
7
6
  const edge = '@modern-js/bff-effect/effect-edge';
8
7
  const nodeRuntime = '@modern-js/bff-effect/effect';
9
- const sharedRuntime = /^@[^/]+\/shared-contracts\/server\/effect-bff-runtime$/u;
10
- const failure = 'Generated API entries must export defineEffectBff(...) or the server-only shared Effect BFF assembly helper with an explicitly composed handler Layer and an unshadowed executable root; entries must implement handlers through HttpApiBuilder.group.';
11
- const invalidSources = new WeakSet();
12
- function invalidSource(message) {
13
- const error = new Error(message);
14
- invalidSources.add(error);
15
- return error;
16
- }
17
- class SourceValidationHub extends Hub {
18
- buildError(_node, message) {
19
- return invalidSource(message);
20
- }
21
- }
8
+ const assembly = '@modern-js/bff-effect/assembly';
9
+ const failure = 'Generated API entries must export defineEffectBff(...) or the native Effect BFF assembly helper with an explicitly composed handler Layer and an unshadowed executable root; entries must implement handlers through HttpApiBuilder.group.';
10
+ const MAX_API_SOURCE_MODULES = 256;
22
11
  function createEffectApiImportResolver(filename) {
23
12
  const directory = node_fs.realpathSync(node_path.dirname(filename));
24
13
  const owner = node_fs.realpathSync(node_path.resolve(directory, '..'));
@@ -50,34 +39,14 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
50
39
  const load = (input)=>{
51
40
  const existing = modules.get(input.id);
52
41
  if (existing) return existing;
53
- if (modules.size >= 64 || input.source.length > 1000000) throw invalidSource('API source budget exceeded');
54
- let file;
55
- try {
56
- file = parse(input.source, {
57
- sourceType: 'module',
58
- sourceFilename: input.id,
59
- plugins: [
60
- "typescript"
61
- ]
62
- });
63
- } catch (cause) {
64
- if ('object' == typeof cause && null !== cause && 'code' in cause && 'BABEL_PARSER_SYNTAX_ERROR' === cause.code && 'reasonCode' in cause && 'string' == typeof cause.reasonCode) throw invalidSource('Invalid API syntax');
65
- throw cause;
66
- }
42
+ if (modules.size >= MAX_API_SOURCE_MODULES || input.source.length > 1000000) throw new SourceSyntaxError('API source budget exceeded');
43
+ const file = parseSource(input.source, input.id, 'Invalid API syntax');
67
44
  const module = {
68
45
  ...input,
69
46
  file
70
47
  };
71
48
  modules.set(input.id, module);
72
- const program = NodePath.get({
73
- hub: new SourceValidationHub(),
74
- parentPath: void 0,
75
- parent: file,
76
- container: file,
77
- key: 'program'
78
- });
79
- program.setContext();
80
- traverse(file, {
49
+ traverseSource(file, {
81
50
  enter (nodePath) {
82
51
  paths.set(nodePath.node, nodePath);
83
52
  owners.set(nodePath.node, module);
@@ -96,7 +65,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
96
65
  if (binding) mutated.add(binding);
97
66
  }
98
67
  }
99
- }, program.scope, void 0, program);
68
+ });
100
69
  return module;
101
70
  };
102
71
  try {
@@ -105,10 +74,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
105
74
  source,
106
75
  resolveImport
107
76
  });
108
- const unwrap = (node)=>{
109
- while(__rspack_external__babel_types_6e8337dd.isParenthesizedExpression(node) || __rspack_external__babel_types_6e8337dd.isTSSatisfiesExpression(node) || __rspack_external__babel_types_6e8337dd.isTSAsExpression(node) || __rspack_external__babel_types_6e8337dd.isTSTypeAssertion(node) || __rspack_external__babel_types_6e8337dd.isTSNonNullExpression(node))node = node.expression;
110
- return node;
111
- };
77
+ const unwrap = (node)=>unwrapExpression(node, true);
112
78
  const binding = (node)=>{
113
79
  node = unwrap(node);
114
80
  if (!__rspack_external__babel_types_6e8337dd.isIdentifier(node)) return;
@@ -237,7 +203,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
237
203
  if (!__rspack_external__babel_types_6e8337dd.isBlockStatement(body)) return body;
238
204
  const statements = body.body;
239
205
  const last = statements.at(-1);
240
- return statements.slice(0, -1).every((statement)=>__rspack_external__babel_types_6e8337dd.isVariableDeclaration(statement)) && __rspack_external__babel_types_6e8337dd.isReturnStatement(last) ? last.argument : void 0;
206
+ return statements.slice(0, -1).every((statement)=>__rspack_external__babel_types_6e8337dd.isVariableDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isTSTypeAliasDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isTSInterfaceDeclaration(statement)) && __rspack_external__babel_types_6e8337dd.isReturnStatement(last) ? last.argument : void 0;
241
207
  };
242
208
  const pipe = (node, check)=>__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) && !node.callee.computed && __rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property, {
243
209
  name: 'pipe'
@@ -326,7 +292,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
326
292
  if (value?.name === 'defineEffectBff' && [
327
293
  edge,
328
294
  nodeRuntime
329
- ].includes(value.specifier) || value?.name === 'assembleEffectBffRuntime' && sharedRuntime.test(value.specifier)) {
295
+ ].includes(value.specifier) || value?.name === 'assembleEffectBffRuntime' && value.specifier === assembly) {
330
296
  if (1 !== node.arguments.length) return false;
331
297
  const values = properties(node.arguments[0]);
332
298
  if (!values) return false;
@@ -349,7 +315,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
349
315
  const root = entry.file.program.body.find((statement)=>__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement));
350
316
  return root && runtime(root.declaration) ? void 0 : failure;
351
317
  } catch (cause) {
352
- if ('object' == typeof cause && null !== cause && invalidSources.has(cause)) return failure;
318
+ if (cause instanceof SourceSyntaxError) return failure;
353
319
  const message = 'object' == typeof cause && null !== cause && 'message' in cause && 'string' == typeof cause.message ? cause.message : String(cause);
354
320
  throw new Error(`Strict Effect API analysis failed: ${message}`, {
355
321
  cause
@@ -1,8 +1,3 @@
1
- /**
2
- * Only `.tsx` is JSX. Enabling JSX for a `.ts` file misreads a generic arrow
3
- * such as `const f = <T>(value: T) => value` as an unclosed JSX element.
4
- */
5
- export declare const consumerParserPlugins: (filePath: string) => ('typescript' | 'jsx')[];
6
1
  export interface MicroVerticalApiBaselineExpectation {
7
2
  /** Optional override for operation IDs outside reachable endpoint naming conventions. */
8
3
  readonly operationPaths?: Readonly<Record<string, string>>;
@@ -0,0 +1,10 @@
1
+ import { type Visitor } from '@babel/traverse';
2
+ import * as t from '@babel/types';
3
+ /** Only JSX extensions enable JSX: generic arrows in .ts must remain valid. */
4
+ export declare const consumerParserPlugins: (filePath: string) => ('typescript' | 'jsx')[];
5
+ /** Only parser/binder diagnostics are policy violations; tool failures escape. */
6
+ export declare class SourceSyntaxError extends Error {
7
+ }
8
+ export declare function parseSource(source: string, filename: string, syntaxMessage?: string): t.File;
9
+ export declare function traverseSource(file: t.File, visitor: Visitor): void;
10
+ export declare function unwrapExpression(node: t.Node, includeAssertions?: boolean): t.Node;
package/package.json CHANGED
@@ -23,7 +23,7 @@
23
23
  "engines": {
24
24
  "node": ">=20"
25
25
  },
26
- "version": "3.9.0-ultramodern.11",
26
+ "version": "3.9.0-ultramodern.13",
27
27
  "types": "./dist/types/index.d.ts",
28
28
  "main": "./dist/esm-node/index.js",
29
29
  "typesVersions": {
@@ -72,15 +72,16 @@
72
72
  "bin"
73
73
  ],
74
74
  "dependencies": {
75
- "@babel/parser": "^8.0.4",
76
- "@babel/traverse": "^8.0.4",
77
- "@babel/types": "^8.0.4",
78
- "oxlint": "1.81.0"
75
+ "@babel/parser": "^8.0.6",
76
+ "@babel/traverse": "^8.0.6",
77
+ "@babel/types": "^8.0.6",
78
+ "oxlint": "1.85.0",
79
+ "resolve.exports": "2.0.3"
79
80
  },
80
81
  "devDependencies": {
81
- "@rslib/core": "1.0.0",
82
+ "@rslib/core": "1.0.2",
82
83
  "@scripts/rstest-config": "2.66.0",
83
- "@types/node": "^26.4.1",
84
+ "@types/node": "^26.6.2",
84
85
  "@typescript/native-preview": "7.0.0-dev.20260707.2"
85
86
  },
86
87
  "sideEffects": false,