@kernlang/test 3.4.2 → 3.4.3-canary.9.1.62aecb74

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,9 +1,10 @@
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';
5
5
  import { inspect, isDeepStrictEqual } from 'util';
6
6
  import { createContext, Script } from 'vm';
7
+ import { isRuntimeBindingName } from './generated/safety-checks.js';
7
8
  const DISCOVERY_SKIP_DIRS = new Set([
8
9
  '.git',
9
10
  '.next',
@@ -426,6 +427,39 @@ function handlerText(node) {
426
427
  .filter(Boolean)
427
428
  .join('\n');
428
429
  }
430
+ // `handlerText` returns the verbatim `props.code` from each handler child,
431
+ // which is what most callers (reach analysis, length checks, regex sniffing)
432
+ // want. The runtime-eval path is different: a handler with `lang="kern"`
433
+ // stores raw KERN source in `props.code`, not JS. Feeding that to V8 yields
434
+ // ReferenceErrors when tests reference symbols whose body is kern-native.
435
+ // `runtimeHandlerSource` lowers each kern handler through the same emitter
436
+ // the codegen path uses (`emitNativeKernBodyTS`), giving the runner a
437
+ // JS body it can wrap and execute.
438
+ //
439
+ // On lowering failure we warn and inject a `throw new Error(...)` body so
440
+ // the binding compiles cleanly but blows up at invocation with the actual
441
+ // emitter error — instead of degrading to an empty body that surfaces as a
442
+ // misleading downstream ReferenceError.
443
+ function runtimeHandlerSource(node) {
444
+ return getChildren(node, 'handler')
445
+ .map((handler) => {
446
+ if (str(getProps(handler).lang) === 'kern') {
447
+ try {
448
+ return emitNativeKernBodyTS(handler);
449
+ }
450
+ catch (error) {
451
+ const message = error instanceof Error ? error.message : String(error);
452
+ const ownerName = str(getProps(node).name) || node.type;
453
+ console.warn(`[kern-test] kern handler lowering failed for '${ownerName}': ${message}`);
454
+ const literal = JSON.stringify(`kern handler lowering failed for '${ownerName}': ${message}`);
455
+ return `throw new Error(${literal});`;
456
+ }
457
+ }
458
+ return str(getProps(handler).code);
459
+ })
460
+ .filter(Boolean)
461
+ .join('\n');
462
+ }
429
463
  function collectNamedHandlerBodies(root) {
430
464
  const bodies = new Map();
431
465
  for (const fn of collectNodes(root, 'fn')) {
@@ -2069,8 +2103,35 @@ function evaluateImportAssertion(node, context) {
2069
2103
  }
2070
2104
  return { passed: true };
2071
2105
  }
2072
- const RUNTIME_EXPR_TIMEOUT_MS = 100;
2073
- const RUNTIME_ASYNC_PROCESS_TIMEOUT_MS = 1500;
2106
+ function readPositiveIntEnv(name, fallback) {
2107
+ const raw = process.env[name];
2108
+ if (raw === undefined || raw === '')
2109
+ return fallback;
2110
+ const parsed = Number.parseInt(raw, 10);
2111
+ if (!Number.isFinite(parsed) || parsed <= 0)
2112
+ return fallback;
2113
+ return parsed;
2114
+ }
2115
+ // V8 enforces wall-clock time on `script.runInContext`. Under CPU contention
2116
+ // cold-compile + JIT can blow tight budgets — a 100ms default produced flaky
2117
+ // non-deterministic failures across runs. 1s is the new floor; override via
2118
+ // `KERN_TEST_RUNTIME_TIMEOUT_MS` for slow CI or expensive evals.
2119
+ const RUNTIME_EXPR_TIMEOUT_MS = readPositiveIntEnv('KERN_TEST_RUNTIME_TIMEOUT_MS', 1000);
2120
+ // Outer process timeout for the async eval path — must comfortably exceed
2121
+ // `RUNTIME_EXPR_TIMEOUT_MS` plus Node spawn cost (~200-400ms on busy machines).
2122
+ // We enforce a 2s safety margin even when the user overrides the env var: a
2123
+ // shorter outer timeout would kill the worker before V8 can report a real
2124
+ // inner timeout, surfacing as an opaque "process timed out" instead of the
2125
+ // actionable script-execution-timed-out error.
2126
+ const RUNTIME_ASYNC_PROCESS_FLOOR_MS = Math.max(5000, RUNTIME_EXPR_TIMEOUT_MS + 2000);
2127
+ const RUNTIME_ASYNC_PROCESS_TIMEOUT_MS = (() => {
2128
+ const requested = readPositiveIntEnv('KERN_TEST_ASYNC_PROCESS_TIMEOUT_MS', RUNTIME_ASYNC_PROCESS_FLOOR_MS);
2129
+ if (requested < RUNTIME_ASYNC_PROCESS_FLOOR_MS) {
2130
+ 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.`);
2131
+ return RUNTIME_ASYNC_PROCESS_FLOOR_MS;
2132
+ }
2133
+ return requested;
2134
+ })();
2074
2135
  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
2136
  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
2137
  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/;
@@ -2122,9 +2183,9 @@ function unsafeRuntimeWorkflowReason(source) {
2122
2183
  return `unsupported token '${unsafeToken}'`;
2123
2184
  return undefined;
2124
2185
  }
2125
- function isRuntimeBindingName(value) {
2126
- return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
2127
- }
2186
+ // First slice of self-hosting: `isRuntimeBindingName` lives in
2187
+ // `src/kern/safety-checks.kern`. Edit the .kern file, then run
2188
+ // `pnpm --filter @kernlang/test kern:compile` to regenerate the facade.
2128
2189
  function transformRuntimeCodeSegments(source, transform) {
2129
2190
  let output = '';
2130
2191
  let segmentStart = 0;
@@ -2187,7 +2248,7 @@ function runtimeJsSource(source) {
2187
2248
  .replace(/\bfunction\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*:\s*[^{]+{/g, 'function $1($2) {'));
2188
2249
  }
2189
2250
  function runtimeConstHandlerExpr(node) {
2190
- const code = runtimeJsSource(handlerText(node).trim());
2251
+ const code = runtimeJsSource(runtimeHandlerSource(node).trim());
2191
2252
  if (!code)
2192
2253
  return '';
2193
2254
  if (/^\s*(?:return|const|let|var|if|for|while|try|throw)\b/.test(code) || /;\s*$/.test(code)) {
@@ -2238,7 +2299,7 @@ function runtimeParamNames(node) {
2238
2299
  return parseLegacyParamNames(str(getProps(node).params));
2239
2300
  }
2240
2301
  function runtimeFunctionExpr(node) {
2241
- const code = runtimeJsSource(handlerText(node));
2302
+ const code = runtimeJsSource(runtimeHandlerSource(node));
2242
2303
  if (!code)
2243
2304
  return '';
2244
2305
  const params = runtimeParamNames(node);
@@ -2249,7 +2310,7 @@ function runtimeFunctionExpr(node) {
2249
2310
  }
2250
2311
  function runtimeHandlerLines(node, spaces = 4) {
2251
2312
  const prefix = ' '.repeat(spaces);
2252
- const code = runtimeJsSource(handlerText(node).trim());
2313
+ const code = runtimeJsSource(runtimeHandlerSource(node).trim());
2253
2314
  if (!code)
2254
2315
  return [];
2255
2316
  return code.split('\n').map((line) => `${prefix}${line}`);