@lokascript/compilation-service 3.2.0 → 4.0.0

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.
@@ -271,86 +271,6 @@ function detectFormat(input) {
271
271
  return "natural";
272
272
  }
273
273
 
274
- // src/compile/cache.ts
275
- var SemanticCache = class {
276
- constructor(maxSize = 500) {
277
- /** Cache hit/miss statistics */
278
- this.hits = 0;
279
- this.misses = 0;
280
- this.maxSize = maxSize;
281
- this.cache = /* @__PURE__ */ new Map();
282
- }
283
- /**
284
- * Get a cached response, or undefined if not cached.
285
- */
286
- get(key) {
287
- const entry = this.cache.get(key);
288
- if (entry) {
289
- this.hits++;
290
- this.cache.delete(key);
291
- this.cache.set(key, entry);
292
- return entry;
293
- }
294
- this.misses++;
295
- return void 0;
296
- }
297
- /**
298
- * Store a response in the cache.
299
- */
300
- set(key, value) {
301
- this.cache.delete(key);
302
- if (this.cache.size >= this.maxSize) {
303
- const oldest = this.cache.keys().next().value;
304
- if (oldest !== void 0) {
305
- this.cache.delete(oldest);
306
- }
307
- }
308
- this.cache.set(key, value);
309
- }
310
- /** Current cache size */
311
- get size() {
312
- return this.cache.size;
313
- }
314
- /** Clear all entries */
315
- clear() {
316
- this.cache.clear();
317
- this.hits = 0;
318
- this.misses = 0;
319
- }
320
- };
321
- function generateCacheKey(node, options) {
322
- const canonical = canonicalize(node);
323
- const optKey = `${options.optimization ?? 2}:${options.target ?? "esm"}:${options.minify ?? false}`;
324
- return `${canonical}|${optKey}`;
325
- }
326
- function canonicalize(node) {
327
- if (!node || typeof node !== "object") return String(node);
328
- const n = node;
329
- const parts = [];
330
- if (n.action) parts.push(`a:${n.action}`);
331
- if (n.kind) parts.push(`k:${n.kind}`);
332
- if (n.roles && typeof n.roles.entries === "function") {
333
- const roles = n.roles;
334
- const sortedRoles = [...roles.entries()].sort(([a], [b]) => a.localeCompare(b));
335
- for (const [role, value] of sortedRoles) {
336
- parts.push(`r:${role}=${canonicalizeValue(value)}`);
337
- }
338
- }
339
- if (Array.isArray(n.body)) {
340
- parts.push(`b:[${n.body.map(canonicalize).join(",")}]`);
341
- }
342
- if (n.eventModifiers && typeof n.eventModifiers === "object") {
343
- const mods = Object.entries(n.eventModifiers).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
344
- if (mods) parts.push(`m:${mods}`);
345
- }
346
- return parts.join(";");
347
- }
348
- function canonicalizeValue(value) {
349
- if (!value || typeof value !== "object") return String(value);
350
- const v = value;
351
- return `${v.type}:${v.value ?? v.raw ?? ""}`;
352
- }
353
-
354
274
  // src/operations/extract.ts
355
275
  function extractOperations(node) {
356
276
  const n = node;
@@ -759,16 +679,10 @@ var PlaywrightRenderer = class {
759
679
  lines.push("");
760
680
  lines.push(`test('${escapeString(testName)}', async ({ page }) => {`);
761
681
  lines.push(` await page.setContent(\`${escapeTemplate(wrapHtml(html))}\`);`);
762
- if (options.executionMode === "compiled" && options.compiledJs) {
763
- lines.push(
764
- ` await page.addScriptTag({ content: \`${escapeTemplate(options.compiledJs)}\` });`
765
- );
766
- } else {
767
- const bundlePath = options.bundlePath ?? "./node_modules/@hyperfixi/core/dist/lokascript-browser.js";
768
- lines.push(` await page.addScriptTag({ path: '${escapeString(bundlePath)}' });`);
769
- lines.push(" await page.waitForFunction(() => document.querySelector('[_]') !== null);");
770
- lines.push(" await page.waitForTimeout(100);");
771
- }
682
+ const bundlePath = options.bundlePath ?? "./node_modules/@hyperfixi/engine/dist/hyperfixi-hs.js";
683
+ lines.push(` await page.addScriptTag({ path: '${escapeString(bundlePath)}' });`);
684
+ lines.push(" await page.waitForFunction(() => document.querySelector('[_]') !== null);");
685
+ lines.push(" await page.waitForTimeout(100);");
772
686
  lines.push("");
773
687
  const triggerLocator = getTriggerLocator(spec);
774
688
  for (const op of spec.operations) {
@@ -2651,15 +2565,6 @@ function walk(node, out) {
2651
2565
  });
2652
2566
  continue;
2653
2567
  }
2654
- if (v.type === "selector" && s.startsWith("<")) {
2655
- out.push({
2656
- severity: "warning",
2657
- code: "UNSUPPORTED_QUERY_LITERAL",
2658
- message: `${role} selector "${s}" uses query-literal syntax, which this parser does not support \u2014 at runtime it is an invalid CSS selector and matches nothing.`,
2659
- suggestion: `Use a plain CSS selector instead (e.g. "${s.replace(/^<|\/?>$/g, "")}").`
2660
- });
2661
- continue;
2662
- }
2663
2568
  if (n.action === "set" && v.type === "property-path" && v.property === "text") {
2664
2569
  out.push({
2665
2570
  severity: "warning",
@@ -2750,76 +2655,19 @@ function nodeToParseResult(node) {
2750
2655
  };
2751
2656
  }
2752
2657
 
2753
- // src/compile/bridge.ts
2754
- var _bridge = null;
2755
- var _compiler = null;
2756
- function initBridge(bridge, compiler) {
2757
- _bridge = bridge;
2758
- _compiler = compiler;
2759
- }
2760
- function compileSemanticNode(node, options = {}) {
2761
- if (!_bridge || !_compiler) {
2762
- throw new Error("Bridge not initialized. Call initBridge() first.");
2763
- }
2764
- const errors = [];
2765
- const warnings = [];
2766
- let semanticAST;
2767
- try {
2768
- const builder = new _bridge.ASTBuilder();
2769
- semanticAST = builder.build(node);
2770
- warnings.push(...builder.warnings);
2771
- } catch (error) {
2772
- errors.push(`AST build failed: ${error instanceof Error ? error.message : String(error)}`);
2773
- return { success: false, helpers: [], warnings, errors };
2774
- }
2775
- let interchangeAST;
2776
- try {
2777
- interchangeAST = _bridge.fromSemanticAST(semanticAST);
2778
- } catch (error) {
2779
- errors.push(
2780
- `Interchange conversion failed: ${error instanceof Error ? error.message : String(error)}`
2781
- );
2782
- return { success: false, helpers: [], warnings, errors };
2783
- }
2784
- try {
2785
- const result = _compiler.compileAST(interchangeAST, {
2786
- language: options.language ?? "en",
2787
- optimizationLevel: options.optimization ?? 2,
2788
- codegen: {
2789
- mode: options.target ?? "esm",
2790
- minify: options.minify ?? false
2791
- }
2792
- });
2793
- if (!result.success) {
2794
- return {
2795
- success: false,
2796
- helpers: [],
2797
- warnings: [...warnings, ...result.warnings],
2798
- errors: [...errors, ...result.errors ?? ["Compilation failed"]]
2799
- };
2800
- }
2801
- return {
2802
- success: true,
2803
- code: result.code,
2804
- helpers: result.metadata.runtimeHelpers,
2805
- warnings: [...warnings, ...result.warnings],
2806
- errors
2807
- };
2808
- } catch (error) {
2809
- errors.push(
2810
- `AOT compilation failed: ${error instanceof Error ? error.message : String(error)}`
2811
- );
2812
- return { success: false, helpers: [], warnings, errors };
2813
- }
2814
- }
2815
-
2816
2658
  // src/service.ts
2817
2659
  var CompilationService = class _CompilationService {
2818
2660
  constructor(options = {}) {
2819
2661
  this.translateFn = null;
2820
2662
  this.translationCollisionsFn = null;
2663
+ /** semantic's `render`, for the English a translation runs as. */
2664
+ this.renderFn = null;
2665
+ /**
2666
+ * `@hyperfixi/engine`'s first parse error for a source (null: it parses). Set by create()
2667
+ * when the engine is installed (an optional peer); without it there is no engine check.
2668
+ */
2669
+ this.engineError = null;
2821
2670
  this.confidenceThreshold = options.confidenceThreshold ?? 0.7;
2822
- this.cache = new SemanticCache(options.cacheSize ?? 500);
2823
2671
  this.testRenderers = new Map(
2824
2672
  Object.entries(options.testRenderers ?? { playwright: new PlaywrightRenderer() })
2825
2673
  );
@@ -2848,8 +2696,8 @@ var CompilationService = class _CompilationService {
2848
2696
  /**
2849
2697
  * Create a CompilationService by dynamically importing dependencies.
2850
2698
  *
2851
- * This async factory resolves @lokascript/semantic and @hyperfixi/aot-compiler
2852
- * at runtime, following the same pattern as createMultilingualCompiler().
2699
+ * This async factory resolves @lokascript/semantic (and, when it is installed,
2700
+ * @hyperfixi/engine) at runtime.
2853
2701
  */
2854
2702
  static async create(options = {}) {
2855
2703
  const service = new _CompilationService(options);
@@ -2862,92 +2710,57 @@ var CompilationService = class _CompilationService {
2862
2710
  initValidation({
2863
2711
  validateSemanticResult: semantic.validateSemanticResult
2864
2712
  });
2865
- const aot = await import("./dist-BDTQTYGC.js");
2866
- const compiler = aot.createCompiler();
2867
- initBridge(
2868
- {
2869
- ASTBuilder: semantic.ASTBuilder,
2870
- fromSemanticAST: semantic.fromSemanticAST
2871
- },
2872
- compiler
2873
- );
2874
2713
  service.translateFn = semantic.translate;
2875
2714
  service.translationCollisionsFn = semantic.findTranslationCollisions;
2715
+ service.renderFn = semantic.render;
2716
+ try {
2717
+ const engine = await import("@hyperfixi/engine");
2718
+ engine.register(...engine.everything);
2719
+ service.engineError = (source) => {
2720
+ try {
2721
+ return engine.api.parse(source).errors[0]?.message.split("\n")[0] ?? null;
2722
+ } catch (e) {
2723
+ return (e instanceof Error ? e.message : String(e)).split("\n")[0] ?? "parse error";
2724
+ }
2725
+ };
2726
+ } catch {
2727
+ }
2876
2728
  return service;
2877
2729
  }
2878
- // ===========================================================================
2879
- // Public API
2880
- // ===========================================================================
2881
2730
  /**
2882
- * Compile hyperscript to JavaScript.
2883
- *
2884
- * Accepts any of three input formats:
2885
- * - `code` + `language`: Natural language hyperscript
2886
- * - `explicit`: Bracket syntax [command role:value ...]
2887
- * - `semantic`: LLM JSON { action, roles, trigger }
2888
- *
2889
- * Returns compiled JS, semantic representation, and diagnostics.
2731
+ * The engine's verdict on natural-language input: its parse of the English the page would
2732
+ * run — the source as written, or a translation's English render (what
2733
+ * `@lokascript/hyperscript-adapter` hands the engine). Null when it parses, or when there is
2734
+ * nothing to check (explicit / JSON input, or no engine installed).
2890
2735
  */
2891
- compile(request) {
2892
- const diagnostics = [];
2893
- const normalized = normalize(request);
2894
- diagnostics.push(...normalized.diagnostics);
2895
- if (!normalized.node) {
2896
- return { ok: false, diagnostics };
2897
- }
2898
- const threshold = request.confidence ?? this.confidenceThreshold;
2899
- const gateResult = runValidationGates(normalized.node, normalized.confidence, threshold);
2900
- diagnostics.push(...gateResult.diagnostics);
2901
- if (!gateResult.pass) {
2902
- return {
2903
- ok: false,
2904
- confidence: gateResult.adjustedConfidence,
2905
- diagnostics
2906
- };
2907
- }
2908
- const cacheKey = generateCacheKey(normalized.node, {
2909
- optimization: request.optimization,
2910
- target: request.target,
2911
- minify: request.minify
2912
- });
2913
- const cached = this.cache.get(cacheKey);
2914
- if (cached) {
2915
- return {
2916
- ...cached,
2917
- confidence: gateResult.adjustedConfidence,
2918
- diagnostics: [...diagnostics, ...cached.diagnostics.filter((d) => d.severity !== "error")]
2919
- };
2920
- }
2921
- const result = compileSemanticNode(normalized.node, {
2922
- language: request.language,
2923
- optimization: request.optimization,
2924
- target: request.target,
2925
- minify: request.minify
2926
- });
2927
- for (const warning of result.warnings) {
2928
- diagnostics.push({ severity: "warning", code: "COMPILE_WARNING", message: warning });
2929
- }
2930
- for (const error of result.errors) {
2931
- diagnostics.push({ severity: "error", code: "COMPILE_ERROR", message: error });
2932
- }
2933
- if (!result.success) {
2934
- return { ok: false, diagnostics };
2736
+ engineDiagnostic(request, normalized) {
2737
+ if (!this.engineError || normalized.format !== "natural" || !request.code) return null;
2738
+ const language = request.language ?? "en";
2739
+ const english = language === "en" || language.startsWith("en-");
2740
+ let source = request.code;
2741
+ if (!english) {
2742
+ if (!this.renderFn || !normalized.node) return null;
2743
+ try {
2744
+ source = this.renderFn(normalized.node, "en");
2745
+ } catch {
2746
+ return null;
2747
+ }
2935
2748
  }
2936
- const semanticJSON = nodeToSemanticJSON(normalized.node);
2937
- const response = {
2938
- ok: true,
2939
- js: result.code,
2940
- helpers: result.helpers,
2941
- size: result.code ? new TextEncoder().encode(result.code).length : 0,
2942
- semantic: semanticJSON,
2943
- confidence: gateResult.adjustedConfidence,
2944
- diagnostics
2749
+ const error = this.engineError(source);
2750
+ if (!error) return null;
2751
+ return {
2752
+ severity: "error",
2753
+ code: "ENGINE_PARSE_ERROR",
2754
+ message: english ? `@hyperfixi/engine cannot read this: ${error}` : `@hyperfixi/engine cannot read its English (${source}): ${error}`,
2755
+ suggestion: "The engine reads upstream _hyperscript's grammar: check the command's syntax (get_command_docs) and rewrite the part the error names."
2945
2756
  };
2946
- this.cache.set(cacheKey, response);
2947
- return response;
2948
2757
  }
2758
+ // ===========================================================================
2759
+ // Public API
2760
+ // ===========================================================================
2949
2761
  /**
2950
- * Validate input without compiling.
2762
+ * Validate input: semantic's parse and schemas, and (for natural-language input) the
2763
+ * engine's parse of the English the page would run.
2951
2764
  * Returns semantic representation and diagnostics.
2952
2765
  */
2953
2766
  validate(request) {
@@ -2960,9 +2773,12 @@ var CompilationService = class _CompilationService {
2960
2773
  const threshold = request.confidence ?? this.confidenceThreshold;
2961
2774
  const gateResult = runValidationGates(normalized.node, normalized.confidence, threshold);
2962
2775
  diagnostics.push(...gateResult.diagnostics);
2963
- const semanticJSON = gateResult.pass ? nodeToSemanticJSON(normalized.node) : void 0;
2776
+ const engineRejects = gateResult.pass ? this.engineDiagnostic(request, normalized) : null;
2777
+ if (engineRejects) diagnostics.push(engineRejects);
2778
+ const ok = gateResult.pass && !engineRejects;
2779
+ const semanticJSON = ok ? nodeToSemanticJSON(normalized.node) : void 0;
2964
2780
  return {
2965
- ok: gateResult.pass,
2781
+ ok,
2966
2782
  semantic: semanticJSON,
2967
2783
  confidence: gateResult.adjustedConfidence,
2968
2784
  diagnostics
@@ -3073,24 +2889,10 @@ var CompilationService = class _CompilationService {
3073
2889
  });
3074
2890
  return { ok: false, tests: [], operations: [], diagnostics };
3075
2891
  }
3076
- let compiledJs;
3077
- if (request.executionMode === "compiled") {
3078
- const compileResult = this.compile({
3079
- code: request.code,
3080
- explicit: request.explicit,
3081
- semantic: request.semantic,
3082
- language: request.language
3083
- });
3084
- if (compileResult.ok && compileResult.js) {
3085
- compiledJs = compileResult.js;
3086
- }
3087
- }
3088
2892
  const generated = renderer.render(spec, {
3089
2893
  testName: request.testName,
3090
- executionMode: request.executionMode,
3091
2894
  bundlePath: request.bundlePath,
3092
- hyperscript: request.code ?? request.explicit,
3093
- compiledJs
2895
+ hyperscript: request.code ?? request.explicit
3094
2896
  });
3095
2897
  const semanticJSON = nodeToSemanticJSON(normalized.node);
3096
2898
  return {
@@ -3295,15 +3097,14 @@ var CompilationService = class _CompilationService {
3295
3097
  * Accepts LSE produced by an LLM (via `lse_generate_with_correction` MCP tool),
3296
3098
  * validates it, and renders to the requested target format.
3297
3099
  *
3298
- * Targets:
3299
- * - 'js' — compiled JavaScript (default)
3100
+ * Targets (`target` is required):
3300
3101
  * - 'react' — React component
3301
3102
  * - 'vue' — Vue component
3302
3103
  * - 'svelte' — Svelte component
3303
3104
  * - 'intent-element' — HTML snippet with embedded `<lse-intent>` JSON
3304
3105
  */
3305
3106
  async generate(request) {
3306
- const { lse, target = "js", task } = request;
3107
+ const { lse, target, task } = request;
3307
3108
  const diagnostics = [];
3308
3109
  const isJSON = lse.trimStart().startsWith("{");
3309
3110
  if (target === "intent-element") {
@@ -3389,44 +3190,28 @@ var CompilationService = class _CompilationService {
3389
3190
  const componentTargets = ["react", "vue", "svelte"];
3390
3191
  if (componentTargets.includes(target)) {
3391
3192
  const componentRequest = isJSON ? { semantic: JSON.parse(lse) } : { explicit: lse };
3392
- const result2 = this.generateComponent({
3193
+ const result = this.generateComponent({
3393
3194
  ...componentRequest,
3394
3195
  framework: target
3395
3196
  });
3396
3197
  return {
3397
- ok: result2.ok,
3398
- output: result2.component?.code,
3399
- protocol: result2.semantic,
3400
- diagnostics: result2.diagnostics
3198
+ ok: result.ok,
3199
+ output: result.component?.code,
3200
+ protocol: result.semantic,
3201
+ diagnostics: result.diagnostics
3401
3202
  };
3402
3203
  }
3403
- const compileRequest = isJSON ? { semantic: JSON.parse(lse) } : { explicit: lse };
3404
- const result = this.compile(compileRequest);
3405
3204
  return {
3406
- ok: result.ok,
3407
- output: result.js,
3408
- protocol: result.semantic,
3409
- diagnostics: result.diagnostics
3410
- };
3411
- }
3412
- /**
3413
- * Get cache statistics.
3414
- */
3415
- getCacheStats() {
3416
- const total = this.cache.hits + this.cache.misses;
3417
- return {
3418
- size: this.cache.size,
3419
- hits: this.cache.hits,
3420
- misses: this.cache.misses,
3421
- hitRate: total > 0 ? this.cache.hits / total : 0
3205
+ ok: false,
3206
+ diagnostics: [
3207
+ {
3208
+ severity: "error",
3209
+ code: "UNKNOWN_TARGET",
3210
+ message: `Unknown target '${String(target)}'. Available: react, vue, svelte, intent-element`
3211
+ }
3212
+ ]
3422
3213
  };
3423
3214
  }
3424
- /**
3425
- * Clear the compilation cache.
3426
- */
3427
- clearCache() {
3428
- this.cache.clear();
3429
- }
3430
3215
  };
3431
3216
  function extractSemanticValueString(v) {
3432
3217
  if (!v || typeof v !== "object") return "";
@@ -3494,8 +3279,6 @@ export {
3494
3279
  canonicalizeOp,
3495
3280
  scoreNodes,
3496
3281
  detectFormat,
3497
- SemanticCache,
3498
- generateCacheKey,
3499
3282
  extractOperations,
3500
3283
  PlaywrightRenderer,
3501
3284
  ReactRenderer,
@@ -3503,4 +3286,4 @@ export {
3503
3286
  SvelteRenderer,
3504
3287
  CompilationService
3505
3288
  };
3506
- //# sourceMappingURL=chunk-XOAPC6M5.js.map
3289
+ //# sourceMappingURL=chunk-GWYYJAZY.js.map