@kernlang/test 3.4.2 → 3.4.3-canary.10.1.eb490d3e

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.
@@ -0,0 +1,2 @@
1
+ export declare const BINDING_NAME_RE: RegExp;
2
+ export declare function isRuntimeBindingName(value: string): boolean;
@@ -0,0 +1,7 @@
1
+ // @kern-source: safety-checks:1
2
+ export const BINDING_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3
+ // @kern-source: safety-checks:3
4
+ export function isRuntimeBindingName(value) {
5
+ return BINDING_NAME_RE.test(value);
6
+ }
7
+ //# sourceMappingURL=safety-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safety-checks.js","sourceRoot":"","sources":["../../src/generated/safety-checks.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,MAAM,CAAC,MAAM,eAAe,GAAW,4BAA4B,CAAC;AAEpE,gCAAgC;AAChC,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { decompile, generateCoreNode, importTypeScript, parseDocumentWithDiagnostics, validateSchema, validateSemantics, } from '@kernlang/core';
1
+ import { decompile, emitNativeKernBodyTS, generateCoreNode, importTypeScript, parseDocumentWithDiagnostics, validateSchema, validateSemantics, } from '@kernlang/core';
2
2
  import { execFileSync } from 'child_process';
3
3
  import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
4
4
  import { dirname, join, relative, resolve } from 'path';
@@ -426,6 +426,39 @@ function handlerText(node) {
426
426
  .filter(Boolean)
427
427
  .join('\n');
428
428
  }
429
+ // `handlerText` returns the verbatim `props.code` from each handler child,
430
+ // which is what most callers (reach analysis, length checks, regex sniffing)
431
+ // want. The runtime-eval path is different: a handler with `lang="kern"`
432
+ // stores raw KERN source in `props.code`, not JS. Feeding that to V8 yields
433
+ // ReferenceErrors when tests reference symbols whose body is kern-native.
434
+ // `runtimeHandlerSource` lowers each kern handler through the same emitter
435
+ // the codegen path uses (`emitNativeKernBodyTS`), giving the runner a
436
+ // JS body it can wrap and execute.
437
+ //
438
+ // On lowering failure we warn and inject a `throw new Error(...)` body so
439
+ // the binding compiles cleanly but blows up at invocation with the actual
440
+ // emitter error — instead of degrading to an empty body that surfaces as a
441
+ // misleading downstream ReferenceError.
442
+ function runtimeHandlerSource(node) {
443
+ return getChildren(node, 'handler')
444
+ .map((handler) => {
445
+ if (str(getProps(handler).lang) === 'kern') {
446
+ try {
447
+ return emitNativeKernBodyTS(handler);
448
+ }
449
+ catch (error) {
450
+ const message = error instanceof Error ? error.message : String(error);
451
+ const ownerName = str(getProps(node).name) || node.type;
452
+ console.warn(`[kern-test] kern handler lowering failed for '${ownerName}': ${message}`);
453
+ const literal = JSON.stringify(`kern handler lowering failed for '${ownerName}': ${message}`);
454
+ return `throw new Error(${literal});`;
455
+ }
456
+ }
457
+ return str(getProps(handler).code);
458
+ })
459
+ .filter(Boolean)
460
+ .join('\n');
461
+ }
429
462
  function collectNamedHandlerBodies(root) {
430
463
  const bodies = new Map();
431
464
  for (const fn of collectNodes(root, 'fn')) {
@@ -1980,7 +2013,13 @@ function evaluateImportedKernRoundtrip(kern, options = {}) {
1980
2013
  message: `Imported KERN does not reparse at ${parseError.line}:${parseError.col}: ${parseError.message}`,
1981
2014
  };
1982
2015
  }
1983
- const parseWarning = reparsed.diagnostics.find((diagnostic) => diagnostic.severity === 'warning');
2016
+ // Slice α-4: NATIVE_KERN_ELIGIBLE is an opt-in suggestion, not a roundtrip
2017
+ // defect. Imported KERN with raw `<<<…>>>` handler bodies that the
2018
+ // classifier accepts will trigger this code at warning level — that is the
2019
+ // intended IDE/LSP signal, not a contract violation. Filter it here the
2020
+ // same way `shared.ts`, `review/src/index.ts`, and `import.ts` do at their
2021
+ // consumer sites.
2022
+ const parseWarning = reparsed.diagnostics.find((diagnostic) => diagnostic.severity === 'warning' && diagnostic.code !== 'NATIVE_KERN_ELIGIBLE');
1984
2023
  if (parseWarning && !options.allowWarnings) {
1985
2024
  return {
1986
2025
  passed: false,
@@ -2069,8 +2108,35 @@ function evaluateImportAssertion(node, context) {
2069
2108
  }
2070
2109
  return { passed: true };
2071
2110
  }
2072
- const RUNTIME_EXPR_TIMEOUT_MS = 100;
2073
- const RUNTIME_ASYNC_PROCESS_TIMEOUT_MS = 1500;
2111
+ function readPositiveIntEnv(name, fallback) {
2112
+ const raw = process.env[name];
2113
+ if (raw === undefined || raw === '')
2114
+ return fallback;
2115
+ const parsed = Number.parseInt(raw, 10);
2116
+ if (!Number.isFinite(parsed) || parsed <= 0)
2117
+ return fallback;
2118
+ return parsed;
2119
+ }
2120
+ // V8 enforces wall-clock time on `script.runInContext`. Under CPU contention
2121
+ // cold-compile + JIT can blow tight budgets — a 100ms default produced flaky
2122
+ // non-deterministic failures across runs. 1s is the new floor; override via
2123
+ // `KERN_TEST_RUNTIME_TIMEOUT_MS` for slow CI or expensive evals.
2124
+ const RUNTIME_EXPR_TIMEOUT_MS = readPositiveIntEnv('KERN_TEST_RUNTIME_TIMEOUT_MS', 1000);
2125
+ // Outer process timeout for the async eval path — must comfortably exceed
2126
+ // `RUNTIME_EXPR_TIMEOUT_MS` plus Node spawn cost (~200-400ms on busy machines).
2127
+ // We enforce a 2s safety margin even when the user overrides the env var: a
2128
+ // shorter outer timeout would kill the worker before V8 can report a real
2129
+ // inner timeout, surfacing as an opaque "process timed out" instead of the
2130
+ // actionable script-execution-timed-out error.
2131
+ const RUNTIME_ASYNC_PROCESS_FLOOR_MS = Math.max(5000, RUNTIME_EXPR_TIMEOUT_MS + 2000);
2132
+ const RUNTIME_ASYNC_PROCESS_TIMEOUT_MS = (() => {
2133
+ const requested = readPositiveIntEnv('KERN_TEST_ASYNC_PROCESS_TIMEOUT_MS', RUNTIME_ASYNC_PROCESS_FLOOR_MS);
2134
+ if (requested < RUNTIME_ASYNC_PROCESS_FLOOR_MS) {
2135
+ console.warn(`[kern-test] KERN_TEST_ASYNC_PROCESS_TIMEOUT_MS=${requested} is below the required floor (${RUNTIME_ASYNC_PROCESS_FLOOR_MS}ms = max(5000, RUNTIME_EXPR_TIMEOUT_MS+2000)); using the floor instead.`);
2136
+ return RUNTIME_ASYNC_PROCESS_FLOOR_MS;
2137
+ }
2138
+ return requested;
2139
+ })();
2074
2140
  const RUNTIME_EXPR_UNSAFE_TOKEN = /\b(?:async|class|constructor|Date|delete|do|eval|fetch|for|Function|global|globalThis|import|process|prototype|require|setInterval|setTimeout|switch|this|throw|try|while|with|WebSocket|XMLHttpRequest|__proto__)\b/;
2075
2141
  const RUNTIME_FN_UNSAFE_TOKEN = /\b(?:class|constructor|Date|delete|do|eval|fetch|Function|global|globalThis|import|process|prototype|require|setInterval|setTimeout|switch|this|with|WebSocket|XMLHttpRequest|__proto__)\b/;
2076
2142
  const RUNTIME_CLASS_UNSAFE_TOKEN = /\b(?:Date|delete|do|eval|fetch|Function|global|globalThis|import|process|prototype|require|setInterval|setTimeout|switch|with|WebSocket|XMLHttpRequest|__proto__)\b/;
@@ -2187,7 +2253,7 @@ function runtimeJsSource(source) {
2187
2253
  .replace(/\bfunction\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*:\s*[^{]+{/g, 'function $1($2) {'));
2188
2254
  }
2189
2255
  function runtimeConstHandlerExpr(node) {
2190
- const code = runtimeJsSource(handlerText(node).trim());
2256
+ const code = runtimeJsSource(runtimeHandlerSource(node).trim());
2191
2257
  if (!code)
2192
2258
  return '';
2193
2259
  if (/^\s*(?:return|const|let|var|if|for|while|try|throw)\b/.test(code) || /;\s*$/.test(code)) {
@@ -2238,7 +2304,7 @@ function runtimeParamNames(node) {
2238
2304
  return parseLegacyParamNames(str(getProps(node).params));
2239
2305
  }
2240
2306
  function runtimeFunctionExpr(node) {
2241
- const code = runtimeJsSource(handlerText(node));
2307
+ const code = runtimeJsSource(runtimeHandlerSource(node));
2242
2308
  if (!code)
2243
2309
  return '';
2244
2310
  const params = runtimeParamNames(node);
@@ -2249,7 +2315,7 @@ function runtimeFunctionExpr(node) {
2249
2315
  }
2250
2316
  function runtimeHandlerLines(node, spaces = 4) {
2251
2317
  const prefix = ' '.repeat(spaces);
2252
- const code = runtimeJsSource(handlerText(node).trim());
2318
+ const code = runtimeJsSource(runtimeHandlerSource(node).trim());
2253
2319
  if (!code)
2254
2320
  return [];
2255
2321
  return code.split('\n').map((line) => `${prefix}${line}`);