@lokascript/compilation-service 3.3.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.
- package/CHANGELOG.md +162 -0
- package/dist/{chunk-AMTQUPKJ.js → chunk-GWYYJAZY.js} +76 -293
- package/dist/chunk-GWYYJAZY.js.map +1 -0
- package/dist/http.cjs +147 -33187
- package/dist/http.cjs.map +1 -1
- package/dist/http.d.cts +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.js +2 -22
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +141 -33166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -45
- package/dist/index.d.ts +3 -45
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/{service-Da54XrEI.d.cts → service-2lEnO5hA.d.cts} +35 -73
- package/dist/{service-Da54XrEI.d.ts → service-2lEnO5hA.d.ts} +35 -73
- package/package.json +11 -7
- package/dist/chunk-AMTQUPKJ.js.map +0 -1
- package/dist/chunk-GBPMR66W.js +0 -23
- package/dist/chunk-GBPMR66W.js.map +0 -1
- package/dist/chunk-IFSCZ7JT.js +0 -85
- package/dist/chunk-IFSCZ7JT.js.map +0 -1
- package/dist/chunk-PEL4Z7TK.js +0 -58
- package/dist/chunk-PEL4Z7TK.js.map +0 -1
- package/dist/core-parser-adapter-W5UPZR4X-QSTMJ42V.js +0 -12
- package/dist/core-parser-adapter-W5UPZR4X-QSTMJ42V.js.map +0 -1
- package/dist/dist-6EMM5HXE.js +0 -4463
- package/dist/dist-6EMM5HXE.js.map +0 -1
- package/dist/dist-OK2I5ODH.js +0 -27680
- package/dist/dist-OK2I5ODH.js.map +0 -1
- package/dist/multilingual-TCFUREGW.js +0 -485
- package/dist/multilingual-TCFUREGW.js.map +0 -1
- package/dist/semantic-adapter-JAAALHRO-WXAFQY4T.js +0 -12
- package/dist/semantic-adapter-JAAALHRO-WXAFQY4T.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,168 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.0.0] - 2026-10-04
|
|
11
|
+
|
|
12
|
+
`@hyperfixi/core`'s own hyperscript engine is gone: its root re-exports `@hyperfixi/engine`, and
|
|
13
|
+
its `hyperfixi.js` is the engine's 34 KB script-tag bundle where core's ~352 KB bundle used to be.
|
|
14
|
+
The engine follows upstream `_hyperscript`'s grammar and is gated by upstream's own test suite, so
|
|
15
|
+
scripts in core-only syntax need upstream's spelling (two additions stay: `new X()` and
|
|
16
|
+
`toggle <element>`). Core's other bundles, its htmx layer, the core-era plugin packages and the
|
|
17
|
+
AOT compiler retire: htmx 4 or fixi supply hypermedia attributes beside the engine, and
|
|
18
|
+
`@lokascript/hyperscript-adapter` runs hyperscript written in other languages. Every package moves
|
|
19
|
+
to 4.0.0 together. Everything under **Removed** is breaking, as is each **Changed** item marked ⚠;
|
|
20
|
+
[MIGRATION.md](MIGRATION.md) names each replacement.
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
|
|
24
|
+
- **Core's engine and its API** (#1368). These root exports had no engine counterpart:
|
|
25
|
+
`hyperscript` (`compile`, `compileSync`, `compileAsync`, `eval`, `validate`), `lokascript`,
|
|
26
|
+
`Runtime`, `RuntimeBase`, `createContext` and the element-scope helpers, `installPlugin`,
|
|
27
|
+
`getParserExtensionRegistry`, `setGlobal`, `DebugController`, `validatePartialContent` and its
|
|
28
|
+
helpers, `fromCoreAST` / `toCoreAST`. `parse` is now the engine's. The root is ESM-only, like the
|
|
29
|
+
engine: `dist/index.cjs` and the UMD `dist/index.min.js` (`LokaScriptCore`) are gone, though the
|
|
30
|
+
kept subpaths still ship CommonJS.
|
|
31
|
+
- **21 of core's 29 entry points**: `/commands`, `/expressions`, `/parser/*`, `/behaviors`,
|
|
32
|
+
`/bundle-generator`, `/registry/*`, `/lse`, and `/browser/` `hybrid-complete`, `hybrid-hx`,
|
|
33
|
+
`hybrid-hx-v4`, `multilingual`, `modular` (#1343–#1368). Instead of `/commands`, import the
|
|
34
|
+
grammar modules from the root: `import { toggle } from '@hyperfixi/core'`.
|
|
35
|
+
- **Core's browser bundles** (#1348, #1350, #1352, #1368): `hyperfixi-hx.js`,
|
|
36
|
+
`hyperfixi-hx-v4.js`, `hyperfixi-hybrid-complete.js`, `hyperfixi-multilingual.js`,
|
|
37
|
+
`hyperfixi-classic-i18n.js` and the code-split `hyperfixi.mjs`, plus every alias
|
|
38
|
+
(`hyperfixi-browser.js`, `hyperfixi-hybrid-hx.js`, each `lokascript-*.js`). Only
|
|
39
|
+
`hyperfixi.js` remains. CDN URLs pinned to 3.x keep working.
|
|
40
|
+
- **Core's embedded htmx layer** (#1342, #1348, #1350): `hx-*` and fixi `fx-*` on core's runtime,
|
|
41
|
+
`hx-live`, `sse-connect` / `sse-swap`, `ws-connect` / `ws-send`. Load htmx 4 or fixi beside the
|
|
42
|
+
engine. `hx-live` becomes the engine's `live` block, and SSE and WebSockets are htmx 4's
|
|
43
|
+
`hx-sse` / `hx-ws`.
|
|
44
|
+
- **Core-only members of `window.hyperfixi`**: `compile`, `compileSync`, `execute`, `setLocale`,
|
|
45
|
+
`evalLSENode`, `debugControl`, `semanticDebug`, the `hyperfixi:semantic-parse` event,
|
|
46
|
+
`hyperfixi:debug` logging, `_hyperscript.behaviors.resolve`, and the deprecated
|
|
47
|
+
`window.lokascript` (#1352, #1355).
|
|
48
|
+
- **Core-only hyperscript syntax** (#1346, #1363, #1365). The engine rejects `has` / `have`,
|
|
49
|
+
`prepend`, `copy`, `push url` / `replace url`, `process partials`, the `pseudo` keyword, prefix
|
|
50
|
+
`unless`, bare `beep`, `a ? b : c`, core's `swap <strategy> of X with Y`, `set @a to v on X`,
|
|
51
|
+
`.debounce(n)` / `.throttle(n)`, `my?.x` and `tell X to …`. A few forms still parse but no longer
|
|
52
|
+
do what they did, such as `toggle .a on this` and `go forward`. MIGRATION.md gives each form's
|
|
53
|
+
upstream spelling. The multilingual reader still accepts them all.
|
|
54
|
+
- **`@hyperfixi/core/multilingual`'s class API** (#1345, #1368): `MultilingualHyperscript` (with
|
|
55
|
+
`parseToAST`, `getAllTranslations`, …), `getMultilingual`, `multilingual`, `LanguageInfo`,
|
|
56
|
+
`schemaRoleInferrer`. The entry is three async functions: `parse`, `render`, `translate`.
|
|
57
|
+
- **The AOT compiler** (#1364). Its output ran only on a runtime that was never published.
|
|
58
|
+
`@lokascript/compilation-service` loses `compile()`, `POST /compile`, `CompileResponse`, the
|
|
59
|
+
compile cache (`SemanticCache`, `getCacheStats`, `clearCache`, `/cache`), `generateTests`'
|
|
60
|
+
`executionMode` and `generate()`'s `'js'` target. `generate()` now requires `target`
|
|
61
|
+
(`react` | `vue` | `svelte` | `intent-element`). `@hyperfixi/mcp-server` loses
|
|
62
|
+
`compile_hyperscript`, `execute_lse`, `lse_to_hyperscript`'s `compile` option and
|
|
63
|
+
`generate_tests`' `executionMode`, leaving 106 tools. The loop is now `validate_and_compile`
|
|
64
|
+
(name kept), then repair, then the hyperscript as is in `_="…"`.
|
|
65
|
+
- **`@hyperfixi/vite-plugin`'s compile mode** (#1360). `mode: 'compile'` warns and builds the
|
|
66
|
+
engine bundle. The exports `CompiledHandler`, `CompileOptions` and
|
|
67
|
+
`set/clear/hasSemanticParser` are removed.
|
|
68
|
+
- **Smaller removals.** `@hyperfixi/core/reference` loses `availability`, `BundleAvailability`
|
|
69
|
+
and `getCommandsByAvailability` (#1363). `@hyperfixi/speech` loses `speechPlugin`,
|
|
70
|
+
`speakCommand`, `askCommand` and `answerCommand`; the engine has `ask` / `answer` (#1358).
|
|
71
|
+
`@hyperfixi/intent-element` loses its `evalLSENode` fallback (#1356).
|
|
72
|
+
`@hyperfixi/patterns-reference` loses four rows that held only retired htmx attributes, and
|
|
73
|
+
`hx-live-with-mutator` is renamed `live-with-handler` (#1348). Of the never-published VS Code
|
|
74
|
+
extensions, the LokaScript one loses its debugger and the standalone `_hyperscript` one is
|
|
75
|
+
retired (#1361).
|
|
76
|
+
|
|
77
|
+
### Deprecated
|
|
78
|
+
|
|
79
|
+
- **`@hyperfixi/reactivity`, `@hyperfixi/realtime`, `@hyperfixi/components`**: deprecated on npm,
|
|
80
|
+
last version 3.3.0 (#1358). Use the engine's built-in `live` / `when` / `bind`, htmx 4's
|
|
81
|
+
`hx-sse` / `hx-ws`, and upstream's component extension.
|
|
82
|
+
- **`@hyperfixi/types-browser`'s `LokaScriptCoreAPI`**: a deprecated alias of `HyperfixiAPI`
|
|
83
|
+
(#1356).
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- ⚠ **`@hyperfixi/core` is the engine plus tooling** (#1368). The root is
|
|
88
|
+
`export * from '@hyperfixi/engine'` and `VERSION`: `register`, `everything` and each grammar
|
|
89
|
+
module, `api`, `parse`, `evaluate`, `processNode`, `boot`, and the AST types. The engine stays
|
|
90
|
+
external, so both packages share one grammar. Kept: `/multilingual`, `/ast-utils`,
|
|
91
|
+
`/reference`, `/metadata`, `/lsp-metadata`, `/browser`.
|
|
92
|
+
- ⚠ **`hyperfixi.js` (`@hyperfixi/core/browser`) is the engine's `hyperfixi-hs.js`**, byte for
|
|
93
|
+
byte: 34 KB gzipped instead of ~352 KB (#1355). `window.hyperfixi` is `window._hyperscript`,
|
|
94
|
+
upstream-shaped (`evaluate`, `parse`, `process`, `use`, `config`, `addSourceTransform`). Parse
|
|
95
|
+
errors are a `hyperscript:parse-error` event on the element plus a `console.error` (#1354).
|
|
96
|
+
- ⚠ **Runtime semantics follow upstream** wherever core's differed. `swap #a with #b` exchanges
|
|
97
|
+
the elements; `show` / `hide … with *opacity` honour the strategy; `tell` binds `you`, not `me`;
|
|
98
|
+
`set` on a selector sets every match; and `no ""` is true. MIGRATION.md has the full list.
|
|
99
|
+
- ⚠ **`@hyperfixi/vite-plugin` emits a bundle on `@hyperfixi/engine`** (#1343). There is one
|
|
100
|
+
tier: the grammar modules the scan finds. It weighs 17.9 KB gzipped for three commands (3.x lite:
|
|
101
|
+
3.9 KB) and 34.4 KB for everything; 3.x fell back to 352 KB on `fetch`. htmx is not bundled.
|
|
102
|
+
Non-English scripts are translated as the engine reads them. `devFallback: 'everything'` is new,
|
|
103
|
+
and `'full'` / `'hybrid-complete'` mean the same. The plugin no longer depends on core.
|
|
104
|
+
- ⚠ **`@hyperfixi/behaviors` runs on the engine** (peer `@hyperfixi/engine`; upstream works too).
|
|
105
|
+
Each behavior is defined with `host.evaluate(source)` and written in upstream's idioms.
|
|
106
|
+
Toggleable's `target` parameter is now `targetEl`. `LokaScriptInstance` / `LokaScriptWindow`
|
|
107
|
+
are now `HyperscriptHost` / `HyperscriptWindow` (#1338).
|
|
108
|
+
- ⚠ **`@hyperfixi/speech` is an engine module** (#1358). Install it with
|
|
109
|
+
`register(...everything, speak)`. It is ESM-only and peers on the engine. `speak` takes
|
|
110
|
+
upstream's syntax (`speak <text> [with voice|rate|pitch|volume <x>]…`) and waits for the
|
|
111
|
+
utterance to end.
|
|
112
|
+
- ⚠ **The localized htmx vocabulary moved** from `@hyperfixi/core/vocab/htmx/{lang}.js` to
|
|
113
|
+
`@lokascript/htmx-adapter/vocab/{lang}.js`; the data is unchanged (#1349).
|
|
114
|
+
- **`@hyperfixi/intent-element`** renders an intent to English with the page's semantic bundle
|
|
115
|
+
and runs it through the host's `evaluate` (`NO_RENDERER` without one). It peers on
|
|
116
|
+
`@hyperfixi/engine` and `@lokascript/semantic` (#1339, #1356).
|
|
117
|
+
- **`@lokascript/semantic` writes upstream's spelling in English** for core-only forms (#1346).
|
|
118
|
+
For example, `has` becomes `matches`, `prepend` becomes `put … at start of`, and swap strategies
|
|
119
|
+
become `put … into` / `before` / `after`. This reaches `translate(…, 'en')`,
|
|
120
|
+
`render(node, 'en')`, both adapters and MCP `translate_to_english`. In `fromSemanticAST`, a
|
|
121
|
+
handler's `then` chain is now its body (#1359).
|
|
122
|
+
- **The language tools read the engine's parse** (#1356, #1359, #1365).
|
|
123
|
+
`@lokascript/language-server` and MCP `validate_hyperscript` report the engine's errors
|
|
124
|
+
(`source: 'engine'`). Hover and symbols come from `@lokascript/semantic`, and some long handlers
|
|
125
|
+
show fewer commands than in 3.x. Hyperscript mode flags exactly `new X()` and `toggle <element>`.
|
|
126
|
+
`@lokascript/compilation-service` rejects natural-language input the engine cannot read
|
|
127
|
+
(`ENGINE_PARSE_ERROR`), and its generated Playwright tests load `hyperfixi-hs.js`. The language
|
|
128
|
+
server, the MCP server and the compilation service now peer on `@hyperfixi/engine`.
|
|
129
|
+
- **MCP**: `get_bundle_config` recommends `hyperfixi-hs.js`, plus an `adapter` field for
|
|
130
|
+
non-English. `analyze_complexity` / `analyze_metrics` no longer count every comparison as a
|
|
131
|
+
decision. The `debug_*` tools point to `log` and `breakpoint`. `UNSUPPORTED_QUERY_LITERAL` is
|
|
132
|
+
gone (#1350, #1352, #1359, #1361, #1364).
|
|
133
|
+
- **`@hyperfixi/core/reference`, `/lsp-metadata`, `/metadata` describe the engine** (#1355,
|
|
134
|
+
#1363). They cover its 53 commands, adding `for`, `ask`, `answer` and `beep!`. Examples are in
|
|
135
|
+
upstream spelling and each is checked to parse; descriptions of core-only semantics (`pick`,
|
|
136
|
+
`beep`, `swap`) are fixed. `FEATURE_KEYWORDS` gains `install`, `when`, `live` and `bind`, and
|
|
137
|
+
`bundleInfo` holds one row.
|
|
138
|
+
- **Dependencies.** `@lokascript/i18n`, `@hyperfixi/testing-framework`, `@hyperfixi/vite-plugin`,
|
|
139
|
+
`@hyperfixi/behaviors`, `@hyperfixi/speech` and `@hyperfixi/intent-element` no longer depend
|
|
140
|
+
on core. Core drops `@lokascript/intent`, morphlex, tslib and its optional peers. Versions are
|
|
141
|
+
lockstep, so upgrade `@hyperfixi/*` and `@lokascript/*` together. `@hyperfixi/mcp-server`,
|
|
142
|
+
`@lokascript/framework` (tests) and `@hyperfixi/server-bridge` (tests) range on
|
|
143
|
+
`@lokascript/domains ^3.0.1`, the first domains release whose peers accept framework,
|
|
144
|
+
semantic and intent 4.x (`^3.1.0 || ^4.0.0`).
|
|
145
|
+
|
|
146
|
+
### Added
|
|
147
|
+
|
|
148
|
+
- **`@hyperfixi/engine` exports `expr`**, so a grammar module can live in another package
|
|
149
|
+
(#1358).
|
|
150
|
+
- **`@hyperfixi/core/ast-utils`'s `withEnginePositions`** gives interchange nodes the source
|
|
151
|
+
spans of the engine's parse (#1359).
|
|
152
|
+
|
|
153
|
+
### Fixed
|
|
154
|
+
|
|
155
|
+
- **`@hyperfixi/engine`** accepts upstream's `beep!` command (`beep! a, b`); 3.3.0 rejected it
|
|
156
|
+
(#1362).
|
|
157
|
+
- **`@hyperfixi/types-browser`** types `window.hyperfixi` / `window._hyperscript` as the engine's
|
|
158
|
+
`HyperfixiAPI`. 3.3.0 re-exported a `./globals` it never emitted, so `window.hyperfixi` was
|
|
159
|
+
untyped (#1356).
|
|
160
|
+
- **Semantic browser bundles** (#1351). Single-language and regional bundles (es, ja,
|
|
161
|
+
east-asian, …) registered no English, so the hyperscript adapter left scripts untranslated. They
|
|
162
|
+
now register it (+~2.2 KB gzipped) and export `translate`. The lite adapter finds any
|
|
163
|
+
`LokaScriptSemantic*` global.
|
|
164
|
+
- **`@lokascript/semantic`** (#1338, #1343, #1347). `/core` plus a language module no longer
|
|
165
|
+
throws "No patterns registered". `on click from (x or me)` parses in th and zh. A `js … end`
|
|
166
|
+
block no longer ends its handler in ja / ko / hi / tr / qu / bn. An options object's `}` is no
|
|
167
|
+
longer read as an SOV event name.
|
|
168
|
+
- **Language server and MCP** (#1359, #1363, #1365). `beep!` has hover docs. `set X to`
|
|
169
|
+
completions offer values. Hyperscript-mode errors land on the form, not line 0. Hover no longer
|
|
170
|
+
shows a second `on click`.
|
|
171
|
+
|
|
10
172
|
## [3.3.0] - 2026-10-02
|
|
11
173
|
|
|
12
174
|
The first publication of `@hyperfixi/engine`, the hyperscript engine meant to replace the one
|
|
@@ -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
|
-
|
|
763
|
-
|
|
764
|
-
|
|
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
|
|
2852
|
-
* at runtime
|
|
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-6EMM5HXE.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
|
-
*
|
|
2883
|
-
*
|
|
2884
|
-
*
|
|
2885
|
-
*
|
|
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
|
-
|
|
2892
|
-
|
|
2893
|
-
const
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
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
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3193
|
+
const result = this.generateComponent({
|
|
3393
3194
|
...componentRequest,
|
|
3394
3195
|
framework: target
|
|
3395
3196
|
});
|
|
3396
3197
|
return {
|
|
3397
|
-
ok:
|
|
3398
|
-
output:
|
|
3399
|
-
protocol:
|
|
3400
|
-
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:
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
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-
|
|
3289
|
+
//# sourceMappingURL=chunk-GWYYJAZY.js.map
|