@hyperfixi/testing-framework 3.3.0 → 4.0.1
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 +199 -1
- package/package.json +9 -10
- package/src/agent-bench/README.md +48 -1
- package/src/agent-bench/agent-bench.test.ts +7 -0
- package/src/agent-bench/harness.ts +39 -50
- package/src/multilingual/README.md +30 -11
- package/src/multilingual/bare-render-fidelity.ts +1 -1
- package/src/multilingual/cli.ts +5 -3
- package/src/multilingual/direct-path-shapes-gate.ts +56 -0
- package/src/multilingual/direct-path-shapes.1.test.ts +12 -0
- package/src/multilingual/direct-path-shapes.2.test.ts +12 -0
- package/src/multilingual/direct-path-shapes.3.test.ts +12 -0
- package/src/multilingual/direct-path-shapes.cases.json +2621 -0
- package/src/multilingual/direct-path-shapes.ts +459 -0
- package/src/multilingual/orchestrator.ts +2 -1
- package/src/multilingual/render-fidelity.ts +1 -1
- package/src/multilingual/shipped-examples-execution.test.ts +32 -96
- package/src/multilingual/shipped-examples-execution.ts +31 -121
- package/src/multilingual/shipped-sources-engine.test.ts +19 -19
- package/src/multilingual/shipped-sources-localized.test.ts +87 -0
- package/src/multilingual/shipped-sources-validity.ts +87 -99
- package/src/multilingual/validators/execution-validator.test.ts +81 -23
- package/src/multilingual/validators/execution-validator.ts +135 -91
- package/src/multilingual/validators/parse-validator.ts +7 -20
- package/src/multilingual/value-matrix.accepted.test.ts +8 -14
- package/src/multilingual/value-matrix.isolation.test.ts +7 -6
- package/src/multilingual/value-matrix.ts +32 -84
- package/src/multilingual/shipped-sources-validity.test.ts +0 -95
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,202 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.0.1] - 2026-10-05
|
|
11
|
+
|
|
12
|
+
Fixes for 4.0.0: the per-language adapter bundles read every language again, and the MCP server's
|
|
13
|
+
documentation and core's metadata describe the 4.0 engine.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **`@lokascript/hyperscript-adapter`: the self-contained per-language and regional bundles read
|
|
18
|
+
German, French, Quechua and Chinese again.** `hyperscript-i18n-<lang>.global.js` and the regional
|
|
19
|
+
bundles installed a generate-only pattern generator over the one `@lokascript/semantic/core`
|
|
20
|
+
provides, discarding its hand-crafted patterns: in de, fr, qu and zh even
|
|
21
|
+
`on click toggle .active` stayed untranslated (the host then rejected it), and other languages
|
|
22
|
+
lost shapes such as `toggle … on #target`; 1106 of the corpus's 3772 translations read
|
|
23
|
+
differently from the full package. Published 3.x and 4.0.0 bundles alike; the all-languages
|
|
24
|
+
`hyperscript-i18n.global.js` was unaffected. The bundles now keep `/core`'s generator (sizes
|
|
25
|
+
unchanged: the ~23 KB 4.0.0 added was this generator, unused until now). Their English renderer
|
|
26
|
+
also drops only an implicit `me` now, so `put "x" into me` no longer renders as the
|
|
27
|
+
engine-invalid `put "x"` in every language. New gate: every built bundle runs translated
|
|
28
|
+
handlers on the engine (`test/adapter-iife.test.ts`).
|
|
29
|
+
- **`@hyperfixi/mcp-server`'s documentation resources teach upstream's spelling.**
|
|
30
|
+
`hyperscript://docs/commands`, `/expressions`, `/events` and `hyperscript://examples/common` still
|
|
31
|
+
taught core 3.x: `on submit.prevent`, `on input.debounce(300ms)`, `on keydown.enter`,
|
|
32
|
+
`swap #t innerHTML`, `while …`, a bare `[attr]` selector, a `parent` keyword, `you` as the event
|
|
33
|
+
target. The engine rejects those, or reads them as something else (`on click.prevent` listens for
|
|
34
|
+
an event named `click.prevent`). A test now parses every example on the engine. The repo's
|
|
35
|
+
`hyperfixi-developer` skill, generated from them, follows (`generate:skills` had been writing to
|
|
36
|
+
the wrong directory).
|
|
37
|
+
- **`@hyperfixi/core/metadata` describes 4.0.** `packageInfo.description` still said "Modern
|
|
38
|
+
hyperscript engine with fixi/htmx integration" and `compatibility` "~85% official _hyperscript",
|
|
39
|
+
both 3.x claims. `packageInfo.upstreamSuite` (`{ version, passed, total }`) now publishes the
|
|
40
|
+
engine's result on upstream _hyperscript's own test suite (1401 of 1467 in 0.9.93), and
|
|
41
|
+
`compatibility` is derived from it; `verify:reference` fails when it disagrees with
|
|
42
|
+
`packages/engine/upstream-suite/known-failures.json`. `ecosystem` drops "212 LLM examples".
|
|
43
|
+
|
|
44
|
+
## [4.0.0] - 2026-10-04
|
|
45
|
+
|
|
46
|
+
`@hyperfixi/core`'s own hyperscript engine is gone: its root re-exports `@hyperfixi/engine`, and
|
|
47
|
+
its `hyperfixi.js` is the engine's 34 KB script-tag bundle where core's ~352 KB bundle used to be.
|
|
48
|
+
The engine follows upstream `_hyperscript`'s grammar and is gated by upstream's own test suite, so
|
|
49
|
+
scripts in core-only syntax need upstream's spelling (two additions stay: `new X()` and
|
|
50
|
+
`toggle <element>`). Core's other bundles, its htmx layer, the core-era plugin packages and the
|
|
51
|
+
AOT compiler retire: htmx 4 or fixi supply hypermedia attributes beside the engine, and
|
|
52
|
+
`@lokascript/hyperscript-adapter` runs hyperscript written in other languages. Every package moves
|
|
53
|
+
to 4.0.0 together. Everything under **Removed** is breaking, as is each **Changed** item marked ⚠;
|
|
54
|
+
[MIGRATION.md](MIGRATION.md) names each replacement.
|
|
55
|
+
|
|
56
|
+
### Removed
|
|
57
|
+
|
|
58
|
+
- **Core's engine and its API** (#1368). These root exports had no engine counterpart:
|
|
59
|
+
`hyperscript` (`compile`, `compileSync`, `compileAsync`, `eval`, `validate`), `lokascript`,
|
|
60
|
+
`Runtime`, `RuntimeBase`, `createContext` and the element-scope helpers, `installPlugin`,
|
|
61
|
+
`getParserExtensionRegistry`, `setGlobal`, `DebugController`, `validatePartialContent` and its
|
|
62
|
+
helpers, `fromCoreAST` / `toCoreAST`. `parse` is now the engine's. The root is ESM-only, like the
|
|
63
|
+
engine: `dist/index.cjs` and the UMD `dist/index.min.js` (`LokaScriptCore`) are gone, though the
|
|
64
|
+
kept subpaths still ship CommonJS.
|
|
65
|
+
- **21 of core's 29 entry points**: `/commands`, `/expressions`, `/parser/*`, `/behaviors`,
|
|
66
|
+
`/bundle-generator`, `/registry/*`, `/lse`, and `/browser/` `hybrid-complete`, `hybrid-hx`,
|
|
67
|
+
`hybrid-hx-v4`, `multilingual`, `modular` (#1343–#1368). Instead of `/commands`, import the
|
|
68
|
+
grammar modules from the root: `import { toggle } from '@hyperfixi/core'`.
|
|
69
|
+
- **Core's browser bundles** (#1348, #1350, #1352, #1368): `hyperfixi-hx.js`,
|
|
70
|
+
`hyperfixi-hx-v4.js`, `hyperfixi-hybrid-complete.js`, `hyperfixi-multilingual.js`,
|
|
71
|
+
`hyperfixi-classic-i18n.js` and the code-split `hyperfixi.mjs`, plus every alias
|
|
72
|
+
(`hyperfixi-browser.js`, `hyperfixi-hybrid-hx.js`, each `lokascript-*.js`). Only
|
|
73
|
+
`hyperfixi.js` remains. CDN URLs pinned to 3.x keep working.
|
|
74
|
+
- **Core's embedded htmx layer** (#1342, #1348, #1350): `hx-*` and fixi `fx-*` on core's runtime,
|
|
75
|
+
`hx-live`, `sse-connect` / `sse-swap`, `ws-connect` / `ws-send`. Load htmx 4 or fixi beside the
|
|
76
|
+
engine. `hx-live` becomes the engine's `live` block, and SSE and WebSockets are htmx 4's
|
|
77
|
+
`hx-sse` / `hx-ws`.
|
|
78
|
+
- **Core-only members of `window.hyperfixi`**: `compile`, `compileSync`, `execute`, `setLocale`,
|
|
79
|
+
`evalLSENode`, `debugControl`, `semanticDebug`, the `hyperfixi:semantic-parse` event,
|
|
80
|
+
`hyperfixi:debug` logging, `_hyperscript.behaviors.resolve`, and the deprecated
|
|
81
|
+
`window.lokascript` (#1352, #1355).
|
|
82
|
+
- **Core-only hyperscript syntax** (#1346, #1363, #1365). The engine rejects `has` / `have`,
|
|
83
|
+
`prepend`, `copy`, `push url` / `replace url`, `process partials`, the `pseudo` keyword, prefix
|
|
84
|
+
`unless`, bare `beep`, `a ? b : c`, core's `swap <strategy> of X with Y`, `set @a to v on X`,
|
|
85
|
+
`.debounce(n)` / `.throttle(n)`, `my?.x` and `tell X to …`. A few forms still parse but no longer
|
|
86
|
+
do what they did, such as `toggle .a on this` and `go forward`. MIGRATION.md gives each form's
|
|
87
|
+
upstream spelling. The multilingual reader still accepts them all.
|
|
88
|
+
- **`@hyperfixi/core/multilingual`'s class API** (#1345, #1368): `MultilingualHyperscript` (with
|
|
89
|
+
`parseToAST`, `getAllTranslations`, …), `getMultilingual`, `multilingual`, `LanguageInfo`,
|
|
90
|
+
`schemaRoleInferrer`. The entry is three async functions: `parse`, `render`, `translate`.
|
|
91
|
+
- **The AOT compiler** (#1364). Its output ran only on a runtime that was never published.
|
|
92
|
+
`@lokascript/compilation-service` loses `compile()`, `POST /compile`, `CompileResponse`, the
|
|
93
|
+
compile cache (`SemanticCache`, `getCacheStats`, `clearCache`, `/cache`), `generateTests`'
|
|
94
|
+
`executionMode` and `generate()`'s `'js'` target. `generate()` now requires `target`
|
|
95
|
+
(`react` | `vue` | `svelte` | `intent-element`). `@hyperfixi/mcp-server` loses
|
|
96
|
+
`compile_hyperscript`, `execute_lse`, `lse_to_hyperscript`'s `compile` option and
|
|
97
|
+
`generate_tests`' `executionMode`, leaving 106 tools. The loop is now `validate_and_compile`
|
|
98
|
+
(name kept), then repair, then the hyperscript as is in `_="…"`.
|
|
99
|
+
- **`@hyperfixi/vite-plugin`'s compile mode** (#1360). `mode: 'compile'` warns and builds the
|
|
100
|
+
engine bundle. The exports `CompiledHandler`, `CompileOptions` and
|
|
101
|
+
`set/clear/hasSemanticParser` are removed.
|
|
102
|
+
- **Smaller removals.** `@hyperfixi/core/reference` loses `availability`, `BundleAvailability`
|
|
103
|
+
and `getCommandsByAvailability` (#1363). `@hyperfixi/speech` loses `speechPlugin`,
|
|
104
|
+
`speakCommand`, `askCommand` and `answerCommand`; the engine has `ask` / `answer` (#1358).
|
|
105
|
+
`@hyperfixi/intent-element` loses its `evalLSENode` fallback (#1356).
|
|
106
|
+
`@hyperfixi/patterns-reference` loses four rows that held only retired htmx attributes, and
|
|
107
|
+
`hx-live-with-mutator` is renamed `live-with-handler` (#1348). Of the never-published VS Code
|
|
108
|
+
extensions, the LokaScript one loses its debugger and the standalone `_hyperscript` one is
|
|
109
|
+
retired (#1361).
|
|
110
|
+
|
|
111
|
+
### Deprecated
|
|
112
|
+
|
|
113
|
+
- **`@hyperfixi/reactivity`, `@hyperfixi/realtime`, `@hyperfixi/components`**: deprecated on npm,
|
|
114
|
+
last version 3.3.0 (#1358). Use the engine's built-in `live` / `when` / `bind`, htmx 4's
|
|
115
|
+
`hx-sse` / `hx-ws`, and upstream's component extension.
|
|
116
|
+
- **`@hyperfixi/types-browser`'s `LokaScriptCoreAPI`**: a deprecated alias of `HyperfixiAPI`
|
|
117
|
+
(#1356).
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
|
|
121
|
+
- ⚠ **`@hyperfixi/core` is the engine plus tooling** (#1368). The root is
|
|
122
|
+
`export * from '@hyperfixi/engine'` and `VERSION`: `register`, `everything` and each grammar
|
|
123
|
+
module, `api`, `parse`, `evaluate`, `processNode`, `boot`, and the AST types. The engine stays
|
|
124
|
+
external, so both packages share one grammar. Kept: `/multilingual`, `/ast-utils`,
|
|
125
|
+
`/reference`, `/metadata`, `/lsp-metadata`, `/browser`.
|
|
126
|
+
- ⚠ **`hyperfixi.js` (`@hyperfixi/core/browser`) is the engine's `hyperfixi-hs.js`**, byte for
|
|
127
|
+
byte: 34 KB gzipped instead of ~352 KB (#1355). `window.hyperfixi` is `window._hyperscript`,
|
|
128
|
+
upstream-shaped (`evaluate`, `parse`, `process`, `use`, `config`, `addSourceTransform`). Parse
|
|
129
|
+
errors are a `hyperscript:parse-error` event on the element plus a `console.error` (#1354).
|
|
130
|
+
- ⚠ **Runtime semantics follow upstream** wherever core's differed. `swap #a with #b` exchanges
|
|
131
|
+
the elements; `show` / `hide … with *opacity` honour the strategy; `tell` binds `you`, not `me`;
|
|
132
|
+
`set` on a selector sets every match; and `no ""` is true. MIGRATION.md has the full list.
|
|
133
|
+
- ⚠ **`@hyperfixi/vite-plugin` emits a bundle on `@hyperfixi/engine`** (#1343). There is one
|
|
134
|
+
tier: the grammar modules the scan finds. It weighs 17.9 KB gzipped for three commands (3.x lite:
|
|
135
|
+
3.9 KB) and 34.4 KB for everything; 3.x fell back to 352 KB on `fetch`. htmx is not bundled.
|
|
136
|
+
Non-English scripts are translated as the engine reads them. `devFallback: 'everything'` is new,
|
|
137
|
+
and `'full'` / `'hybrid-complete'` mean the same. The plugin no longer depends on core.
|
|
138
|
+
- ⚠ **`@hyperfixi/behaviors` runs on the engine** (peer `@hyperfixi/engine`; upstream works too).
|
|
139
|
+
Each behavior is defined with `host.evaluate(source)` and written in upstream's idioms.
|
|
140
|
+
Toggleable's `target` parameter is now `targetEl`. `LokaScriptInstance` / `LokaScriptWindow`
|
|
141
|
+
are now `HyperscriptHost` / `HyperscriptWindow` (#1338).
|
|
142
|
+
- ⚠ **`@hyperfixi/speech` is an engine module** (#1358). Install it with
|
|
143
|
+
`register(...everything, speak)`. It is ESM-only and peers on the engine. `speak` takes
|
|
144
|
+
upstream's syntax (`speak <text> [with voice|rate|pitch|volume <x>]…`) and waits for the
|
|
145
|
+
utterance to end.
|
|
146
|
+
- ⚠ **The localized htmx vocabulary moved** from `@hyperfixi/core/vocab/htmx/{lang}.js` to
|
|
147
|
+
`@lokascript/htmx-adapter/vocab/{lang}.js`; the data is unchanged (#1349).
|
|
148
|
+
- **`@hyperfixi/intent-element`** renders an intent to English with the page's semantic bundle
|
|
149
|
+
and runs it through the host's `evaluate` (`NO_RENDERER` without one). It peers on
|
|
150
|
+
`@hyperfixi/engine` and `@lokascript/semantic` (#1339, #1356).
|
|
151
|
+
- **`@lokascript/semantic` writes upstream's spelling in English** for core-only forms (#1346).
|
|
152
|
+
For example, `has` becomes `matches`, `prepend` becomes `put … at start of`, and swap strategies
|
|
153
|
+
become `put … into` / `before` / `after`. This reaches `translate(…, 'en')`,
|
|
154
|
+
`render(node, 'en')`, both adapters and MCP `translate_to_english`. In `fromSemanticAST`, a
|
|
155
|
+
handler's `then` chain is now its body (#1359).
|
|
156
|
+
- **The language tools read the engine's parse** (#1356, #1359, #1365).
|
|
157
|
+
`@lokascript/language-server` and MCP `validate_hyperscript` report the engine's errors
|
|
158
|
+
(`source: 'engine'`). Hover and symbols come from `@lokascript/semantic`, and some long handlers
|
|
159
|
+
show fewer commands than in 3.x. Hyperscript mode flags exactly `new X()` and `toggle <element>`.
|
|
160
|
+
`@lokascript/compilation-service` rejects natural-language input the engine cannot read
|
|
161
|
+
(`ENGINE_PARSE_ERROR`), and its generated Playwright tests load `hyperfixi-hs.js`. The language
|
|
162
|
+
server, the MCP server and the compilation service now peer on `@hyperfixi/engine`.
|
|
163
|
+
- **MCP**: `get_bundle_config` recommends `hyperfixi-hs.js`, plus an `adapter` field for
|
|
164
|
+
non-English. `analyze_complexity` / `analyze_metrics` no longer count every comparison as a
|
|
165
|
+
decision. The `debug_*` tools point to `log` and `breakpoint`. `UNSUPPORTED_QUERY_LITERAL` is
|
|
166
|
+
gone (#1350, #1352, #1359, #1361, #1364).
|
|
167
|
+
- **`@hyperfixi/core/reference`, `/lsp-metadata`, `/metadata` describe the engine** (#1355,
|
|
168
|
+
#1363). They cover its 53 commands, adding `for`, `ask`, `answer` and `beep!`. Examples are in
|
|
169
|
+
upstream spelling and each is checked to parse; descriptions of core-only semantics (`pick`,
|
|
170
|
+
`beep`, `swap`) are fixed. `FEATURE_KEYWORDS` gains `install`, `when`, `live` and `bind`, and
|
|
171
|
+
`bundleInfo` holds one row.
|
|
172
|
+
- **Dependencies.** `@lokascript/i18n`, `@hyperfixi/testing-framework`, `@hyperfixi/vite-plugin`,
|
|
173
|
+
`@hyperfixi/behaviors`, `@hyperfixi/speech` and `@hyperfixi/intent-element` no longer depend
|
|
174
|
+
on core. Core drops `@lokascript/intent`, morphlex, tslib and its optional peers. Versions are
|
|
175
|
+
lockstep, so upgrade `@hyperfixi/*` and `@lokascript/*` together. `@hyperfixi/mcp-server`,
|
|
176
|
+
`@lokascript/framework` (tests) and `@hyperfixi/server-bridge` (tests) range on
|
|
177
|
+
`@lokascript/domains ^3.0.1`, the first domains release whose peers accept framework,
|
|
178
|
+
semantic and intent 4.x (`^3.1.0 || ^4.0.0`).
|
|
179
|
+
|
|
180
|
+
### Added
|
|
181
|
+
|
|
182
|
+
- **`@hyperfixi/engine` exports `expr`**, so a grammar module can live in another package
|
|
183
|
+
(#1358).
|
|
184
|
+
- **`@hyperfixi/core/ast-utils`'s `withEnginePositions`** gives interchange nodes the source
|
|
185
|
+
spans of the engine's parse (#1359).
|
|
186
|
+
|
|
187
|
+
### Fixed
|
|
188
|
+
|
|
189
|
+
- **`@hyperfixi/engine`** accepts upstream's `beep!` command (`beep! a, b`); 3.3.0 rejected it
|
|
190
|
+
(#1362).
|
|
191
|
+
- **`@hyperfixi/types-browser`** types `window.hyperfixi` / `window._hyperscript` as the engine's
|
|
192
|
+
`HyperfixiAPI`. 3.3.0 re-exported a `./globals` it never emitted, so `window.hyperfixi` was
|
|
193
|
+
untyped (#1356).
|
|
194
|
+
- **Semantic browser bundles** (#1351). Single-language and regional bundles (es, ja,
|
|
195
|
+
east-asian, …) registered no English, so the hyperscript adapter left scripts untranslated. They
|
|
196
|
+
now register it (+~2.2 KB gzipped) and export `translate`. The lite adapter finds any
|
|
197
|
+
`LokaScriptSemantic*` global.
|
|
198
|
+
- **`@lokascript/semantic`** (#1338, #1343, #1347). `/core` plus a language module no longer
|
|
199
|
+
throws "No patterns registered". `on click from (x or me)` parses in th and zh. A `js … end`
|
|
200
|
+
block no longer ends its handler in ja / ko / hi / tr / qu / bn. An options object's `}` is no
|
|
201
|
+
longer read as an SOV event name.
|
|
202
|
+
- **Language server and MCP** (#1359, #1363, #1365). `beep!` has hover docs. `set X to`
|
|
203
|
+
completions offer values. Hyperscript-mode errors land on the form, not line 0. Hover no longer
|
|
204
|
+
shows a second `on click`.
|
|
205
|
+
|
|
10
206
|
## [3.3.0] - 2026-10-02
|
|
11
207
|
|
|
12
208
|
The first publication of `@hyperfixi/engine`, the hyperscript engine meant to replace the one
|
|
@@ -899,7 +1095,9 @@ _Synchronized version release. See git history for details._
|
|
|
899
1095
|
- npm access token stored in GitHub Secrets
|
|
900
1096
|
- 2FA recommended for npm organization
|
|
901
1097
|
|
|
902
|
-
[Unreleased]: https://github.com/codetalcott/hyperfixi/compare/
|
|
1098
|
+
[Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v4.0.1...HEAD
|
|
1099
|
+
[4.0.1]: https://github.com/codetalcott/hyperfixi/compare/v4.0.0...v4.0.1
|
|
1100
|
+
[4.0.0]: https://github.com/codetalcott/hyperfixi/compare/v3.3.0...v4.0.0
|
|
903
1101
|
[3.3.0]: https://github.com/codetalcott/hyperfixi/compare/v3.2.0...v3.3.0
|
|
904
1102
|
[3.2.0]: https://github.com/codetalcott/hyperfixi/compare/v3.1.0...v3.2.0
|
|
905
1103
|
[3.1.0]: https://github.com/codetalcott/hyperfixi/compare/v3.0.0...v3.1.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperfixi/testing-framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Cross-platform behavior testing suite for LokaScript applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsup",
|
|
31
31
|
"dev": "tsup --watch",
|
|
32
|
-
"pretest": "../../scripts/ensure-fresh.sh ../intent ../framework ../semantic ../hyperscript-adapter ../engine ../patterns-reference ../
|
|
32
|
+
"pretest": "../../scripts/ensure-fresh.sh ../intent ../framework ../semantic ../hyperscript-adapter ../engine ../patterns-reference ../compilation-service",
|
|
33
33
|
"test": "vitest run",
|
|
34
34
|
"test:watch": "vitest",
|
|
35
35
|
"test:coverage": "vitest run --coverage",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
42
|
"test:check": "VITEST_TIMEOUT=240 VITEST_QUIET=1 bash ../../scripts/vitest-run.sh --reporter=dot",
|
|
43
43
|
"test:canonical": "FOREIGN_CANONICAL_VALIDITY=1 VITEST_TIMEOUT=300 bash ../../scripts/vitest-run.sh --reporter=dot src/multilingual/canonical-validity.test.ts src/multilingual/foreign-canonical-validity.test.ts src/multilingual/render-fidelity.test.ts src/multilingual/bare-render-fidelity.test.ts src/multilingual/en-reference-preservation.test.ts",
|
|
44
|
-
"test:shipped-sources": "VITEST_TIMEOUT=300 bash ../../scripts/vitest-run.sh --reporter=dot src/multilingual/shipped-sources-
|
|
44
|
+
"test:shipped-sources": "VITEST_TIMEOUT=300 bash ../../scripts/vitest-run.sh --reporter=dot src/multilingual/shipped-sources-engine.test.ts src/multilingual/shipped-sources-localized.test.ts src/multilingual/shipped-examples-execution.test.ts"
|
|
45
45
|
},
|
|
46
46
|
"keywords": [
|
|
47
47
|
"hyperscript",
|
|
@@ -57,11 +57,10 @@
|
|
|
57
57
|
"author": "LokaScript Contributors",
|
|
58
58
|
"license": "MIT",
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@hyperfixi/
|
|
61
|
-
"@
|
|
62
|
-
"@lokascript/
|
|
63
|
-
"@lokascript/
|
|
64
|
-
"@lokascript/semantic": "^3.3.0",
|
|
60
|
+
"@hyperfixi/patterns-reference": "^4.0.1",
|
|
61
|
+
"@lokascript/compilation-service": "^4.0.1",
|
|
62
|
+
"@lokascript/i18n": "^4.0.1",
|
|
63
|
+
"@lokascript/semantic": "^4.0.1",
|
|
65
64
|
"diff": "^8.0.3",
|
|
66
65
|
"esbuild": "^0.25.12",
|
|
67
66
|
"happy-dom": "^20.14.5",
|
|
@@ -73,8 +72,8 @@
|
|
|
73
72
|
"vite": "^8.3.1"
|
|
74
73
|
},
|
|
75
74
|
"devDependencies": {
|
|
76
|
-
"@hyperfixi/engine": "^
|
|
77
|
-
"@lokascript/hyperscript-adapter": "^
|
|
75
|
+
"@hyperfixi/engine": "^4.0.1",
|
|
76
|
+
"@lokascript/hyperscript-adapter": "^4.0.1",
|
|
78
77
|
"@types/diff": "^5.0.0",
|
|
79
78
|
"@types/node": "^26.6.3",
|
|
80
79
|
"@vitest/coverage-v8": "^5.0.0",
|
|
@@ -138,7 +138,9 @@ class selector (`#box .has class .danger`) so the condition is always falsy;
|
|
|
138
138
|
compilation-service validation pipeline (`validation/inert-shapes.ts`) matches
|
|
139
139
|
those fingerprints and warns (`INERT_QUANTIFIER_TARGET`,
|
|
140
140
|
`HALF_PARSED_CONDITION`, `UNSUPPORTED_QUERY_LITERAL`, `INERT_PROPERTY_WRITE`)
|
|
141
|
-
— warnings only, still no parser change.
|
|
141
|
+
— warnings only, still no parser change. (`UNSUPPORTED_QUERY_LITERAL` retired with
|
|
142
|
+
the AOT compiler in 4.0: the engine reads query literals, and this benchmark,
|
|
143
|
+
which executes on it since C4, scores `<body/>` correct.)
|
|
142
144
|
|
|
143
145
|
| | pre-3b | after slice 1 | after slice 2 |
|
|
144
146
|
| ------------------------------------- | ----------- | ------------- | ---------------------- |
|
|
@@ -153,6 +155,51 @@ parser-gap silent band is now zero.**
|
|
|
153
155
|
Bands are computed by `harness.bandOf` — one function shared by the probe, the
|
|
154
156
|
committed baseline, and the ratchet test, so they cannot drift apart.
|
|
155
157
|
|
|
158
|
+
### Phase C4b: executed on the engine (2026-10-04)
|
|
159
|
+
|
|
160
|
+
Execution moved from `@hyperfixi/core`'s runtime (fed semantic's parse through
|
|
161
|
+
`buildAST`) to `@hyperfixi/engine`, the engine the product ships: the candidate
|
|
162
|
+
is the button's `_` attribute, as on a page. Validation is unchanged
|
|
163
|
+
(`CompilationService.validate`, semantic).
|
|
164
|
+
|
|
165
|
+
| | core runtime | engine |
|
|
166
|
+
| ------------------------------------- | ------------ | -------------- |
|
|
167
|
+
| parse | 36/37 | 36/37 |
|
|
168
|
+
| behave correctly | 20/37 | **17/37** |
|
|
169
|
+
| wrong but **warned** (loop can react) | 14/37 | 12/37 |
|
|
170
|
+
| wrong and **silent** | 2/37 (5%) | **8/37 (22%)** |
|
|
171
|
+
|
|
172
|
+
Four rows got better: core misbehaved on `add @aria-expanded="true" to`,
|
|
173
|
+
`set the innerHTML of`, `set the style.backgroundColor of`, and `on "refresh"`
|
|
174
|
+
(a quoted event name), all of which the engine runs as upstream does. Six got
|
|
175
|
+
worse, and all six are the same kind: phrasings only core's lenient parser
|
|
176
|
+
read — `and` between commands (two rows: `… and put …`, `remove … and add …`),
|
|
177
|
+
`add .x on #item`, `put "x" in #el`, `toggle .active on this`, `toggle class
|
|
178
|
+
.active` (and `, put …` warns). The engine rejects each, as upstream does, while
|
|
179
|
+
the validator still says `ok`: they are silent because validation reads
|
|
180
|
+
semantic's grammar, not the engine's. Phase C4d moves the diagnostics onto the
|
|
181
|
+
engine's parse (owner decision 5), which should make all six visible.
|
|
182
|
+
|
|
183
|
+
### Phase C4d: validated on the engine (2026-10-04)
|
|
184
|
+
|
|
185
|
+
`CompilationService.validate` now also puts natural-language input to `@hyperfixi/engine`'s
|
|
186
|
+
parser (the English as written, or a translation's English render), and a rejection is an
|
|
187
|
+
error (`ENGINE_PARSE_ERROR`). Behavior is unchanged; what the loop can SEE changes:
|
|
188
|
+
|
|
189
|
+
| | C4b (semantic validator) | engine validator |
|
|
190
|
+
| ------------------------------------- | ------------------------ | ---------------- |
|
|
191
|
+
| parse (validator says ok) | 36/37 | **20/37** |
|
|
192
|
+
| behave correctly | 17/37 | 17/37 |
|
|
193
|
+
| wrong but **warned** | 12/37 | 1/37 |
|
|
194
|
+
| wrong and **rejected** (loop repairs) | 0/37 | **16/37** |
|
|
195
|
+
| wrong and **silent** | 8/37 | **3/37** |
|
|
196
|
+
|
|
197
|
+
Sixteen rows moved to `rejected`: eleven that only warned (the engine rejects what the
|
|
198
|
+
inert-shape and unconsumed-input warnings described) and five of the six silent ones C4b
|
|
199
|
+
measured. The three silent rows left are code the engine reads: `toggle .active on this`
|
|
200
|
+
(`this` is a variable there, null at run time), and the valid-code-different-intent pair
|
|
201
|
+
(`add .hidden to #menu`, `on mouseover`).
|
|
202
|
+
|
|
156
203
|
## Adding a task
|
|
157
204
|
|
|
158
205
|
Append to [`tasks.ts`](./tasks.ts): a prompt with no hyperscript in it, a
|
|
@@ -21,6 +21,13 @@
|
|
|
21
21
|
* Deterministic and generator-free: no LLM runs here, so this IS a legitimate
|
|
22
22
|
* CI gate (the A/B run in README.md, which needs a generator, deliberately is
|
|
23
23
|
* not). Full sweep measures ~6s.
|
|
24
|
+
*
|
|
25
|
+
* @vitest-environment node
|
|
26
|
+
* Required, not a preference: candidates execute on @hyperfixi/engine in jsdom,
|
|
27
|
+
* and under the suite default (happy-dom) the DOM constructors already exist on
|
|
28
|
+
* globalThis, so the engine binds happy-dom's and every jsdom element fails its
|
|
29
|
+
* instanceof checks (measured: every reference produced no effect). Same reason
|
|
30
|
+
* as shipped-examples-execution.test.ts.
|
|
24
31
|
*/
|
|
25
32
|
|
|
26
33
|
import { describe, it, expect, beforeAll } from 'vitest';
|
|
@@ -18,15 +18,19 @@
|
|
|
18
18
|
* catch, so `parseRate` and `behaviorRate` are reported side by side and their
|
|
19
19
|
* GAP is a headline number in its own right.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Execution is `@hyperfixi/engine`, the product's engine: the candidate is the
|
|
22
|
+
* button's `_` attribute, as on a page. (Until Phase C4 it was `@hyperfixi/core`'s
|
|
23
|
+
* runtime, fed semantic's parse through `buildAST`.)
|
|
24
|
+
*
|
|
25
|
+
* Determinism: a fresh JSDOM per execution, no network, no timers beyond a
|
|
26
|
+
* fixed settle window. Effect-signature primitives are imported from
|
|
23
27
|
* ../multilingual/effect-signature so this harness and the R2 ratchet can never
|
|
24
28
|
* disagree about what a DOM effect is.
|
|
25
29
|
*/
|
|
26
30
|
|
|
27
31
|
import { JSDOM } from 'jsdom';
|
|
28
|
-
import { parseSemantic, buildAST } from '@lokascript/semantic';
|
|
29
32
|
import { snapshot, diffSnapshots } from '../multilingual/effect-signature.js';
|
|
33
|
+
import { installGlobals } from '../multilingual/shipped-examples-execution.js';
|
|
30
34
|
import { SHARED_FIXTURE, type BenchTask } from './tasks.js';
|
|
31
35
|
|
|
32
36
|
/** Settle window for the dispatched handler. No task waits/fetches. */
|
|
@@ -156,43 +160,29 @@ function buildDocument(task: BenchTask): JSDOM {
|
|
|
156
160
|
return new JSDOM(`<!DOCTYPE html><html><body>${SHARED_FIXTURE}${task.fixture}</body></html>`);
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
g.Event = dom.window.Event;
|
|
164
|
-
g.CustomEvent = dom.window.CustomEvent;
|
|
165
|
-
g.HTMLElement = dom.window.HTMLElement;
|
|
166
|
-
g.Element = dom.window.Element;
|
|
167
|
-
g.Node = dom.window.Node;
|
|
168
|
-
g.MutationObserver = dom.window.MutationObserver;
|
|
169
|
-
g.getComputedStyle = dom.window.getComputedStyle;
|
|
163
|
+
/** The engine's public surface this harness uses. */
|
|
164
|
+
interface ScriptHost {
|
|
165
|
+
parse(source: string): { errors: Array<{ message: string }> };
|
|
166
|
+
processNode(node: unknown): void;
|
|
170
167
|
}
|
|
171
168
|
|
|
172
|
-
let
|
|
173
|
-
Runtime: new () => { execute(ast: unknown, ctx: unknown): Promise<unknown> };
|
|
174
|
-
createContext: (el: HTMLElement) => unknown;
|
|
175
|
-
} | null = null;
|
|
176
|
-
|
|
177
|
-
let listenerErrors: string[] = [];
|
|
178
|
-
let trapInstalled = false;
|
|
169
|
+
let engine: ScriptHost | null = null;
|
|
179
170
|
|
|
180
|
-
/** Bootstraps jsdom globals
|
|
171
|
+
/** Bootstraps jsdom globals, then loads the engine with every module registered. */
|
|
181
172
|
export async function initialize(): Promise<void> {
|
|
182
|
-
if (
|
|
173
|
+
if (engine) return;
|
|
183
174
|
installGlobals(new JSDOM('<!DOCTYPE html><html><body></body></html>'));
|
|
184
|
-
const mod =
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
trapInstalled = true;
|
|
175
|
+
const mod = await import('@hyperfixi/engine');
|
|
176
|
+
mod.register(...mod.everything);
|
|
177
|
+
engine = mod.api;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The engine's first parse error for a source, first line; undefined when it parses. */
|
|
181
|
+
function parseError(code: string): string | undefined {
|
|
182
|
+
try {
|
|
183
|
+
return engine!.parse(code).errors[0]?.message.split('\n')[0];
|
|
184
|
+
} catch (e: unknown) {
|
|
185
|
+
return (e instanceof Error ? e.message : String(e)).split('\n')[0];
|
|
196
186
|
}
|
|
197
187
|
}
|
|
198
188
|
|
|
@@ -203,22 +193,21 @@ async function executeInner(task: BenchTask, code: string): Promise<ExecutionOut
|
|
|
203
193
|
const document = dom.window.document;
|
|
204
194
|
task.setup?.(document);
|
|
205
195
|
const btn = document.getElementById('btn')!;
|
|
206
|
-
listenerErrors = [];
|
|
207
196
|
|
|
208
|
-
// The
|
|
197
|
+
// The engine reports a failing handler on the console (`hyperscript errors were
|
|
198
|
+
// found…`): record those, and drop the rest of the noise.
|
|
199
|
+
const runtimeErrors: string[] = [];
|
|
209
200
|
const saved = { log: console.log, warn: console.warn, error: console.error };
|
|
210
|
-
console.log = console.warn =
|
|
201
|
+
console.log = console.warn = () => {};
|
|
202
|
+
console.error = (...args: unknown[]) => {
|
|
203
|
+
const error = args.find(a => a instanceof Error);
|
|
204
|
+
runtimeErrors.push(error instanceof Error ? error.message : String(args[0]));
|
|
205
|
+
};
|
|
211
206
|
try {
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const built = buildAST(parsed.node as never) as { ast?: unknown };
|
|
217
|
-
if (!built.ast) return { effects: [], error: 'buildAST returned no AST' };
|
|
218
|
-
|
|
219
|
-
const runtime = new core!.Runtime();
|
|
220
|
-
const ctx = core!.createContext(btn as unknown as HTMLElement);
|
|
221
|
-
await runtime.execute(built.ast, ctx);
|
|
207
|
+
const rejected = parseError(code);
|
|
208
|
+
if (rejected !== undefined) return { effects: [], error: `parse failed (engine: ${rejected})` };
|
|
209
|
+
btn.setAttribute('_', code);
|
|
210
|
+
engine!.processNode(btn);
|
|
222
211
|
|
|
223
212
|
const before = snapshot(document);
|
|
224
213
|
const trig = task.trigger ?? { event: 'click' };
|
|
@@ -232,8 +221,8 @@ async function executeInner(task: BenchTask, code: string): Promise<ExecutionOut
|
|
|
232
221
|
await new Promise(r => setTimeout(r, SETTLE_MS));
|
|
233
222
|
|
|
234
223
|
const effects = diffSnapshots(before, snapshot(document));
|
|
235
|
-
return
|
|
236
|
-
? { effects, error: `runtime: ${
|
|
224
|
+
return runtimeErrors.length > 0
|
|
225
|
+
? { effects, error: `runtime: ${runtimeErrors.join('; ')}` }
|
|
237
226
|
: { effects };
|
|
238
227
|
} catch (e: unknown) {
|
|
239
228
|
return { effects: [], error: e instanceof Error ? e.message : String(e) };
|
|
@@ -256,17 +256,22 @@ Add to `.github/workflows/test.yml`:
|
|
|
256
256
|
(literal, variable, selector, possessive, `of`, dotted, call, array, parens), the
|
|
257
257
|
operators, and five positions (`put` and `set` values, `if` and `repeat while`
|
|
258
258
|
conditions, `increment … by`). Each cell's English runs on the real
|
|
259
|
-
`hyperscript.org` engine, which is the oracle, and then in
|
|
260
|
-
|
|
261
|
-
| Lane
|
|
262
|
-
|
|
|
263
|
-
| `en`
|
|
264
|
-
| `en-rt`
|
|
265
|
-
|
|
|
266
|
-
| `<L>/up`
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
259
|
+
`hyperscript.org` engine, which is the oracle, and then in 49 lanes:
|
|
260
|
+
|
|
261
|
+
| Lane | What runs |
|
|
262
|
+
| --------- | ---------------------------------------------------------------------------- |
|
|
263
|
+
| `en` | hyperfixi's English path (core's parser and runtime) |
|
|
264
|
+
| `en-rt` | semantic's English parse, rendered back to English, on upstream |
|
|
265
|
+
| `eng` | the English source on `@hyperfixi/engine` |
|
|
266
|
+
| `<L>/up` | each of 23 languages, through `@lokascript/hyperscript-adapter`, on upstream |
|
|
267
|
+
| `<L>/eng` | the same adapter English on `@hyperfixi/engine` |
|
|
268
|
+
|
|
269
|
+
The `<L>` lane (each language on core's direct path) retired in Phase C2 of the
|
|
270
|
+
engine cutover, when text became the multilingual interchange; no cell failed
|
|
271
|
+
`<L>/eng` while passing `<L>`.
|
|
272
|
+
|
|
273
|
+
`baselines/value-matrix.json` lists every failing (cell, lane) pair; `*up` and
|
|
274
|
+
`*eng` stand for all 23 lanes of each. The gate runs in fifteen shards,
|
|
270
275
|
`value-matrix.<position>.test.ts`, and fails on a failing pair the baseline does not
|
|
271
276
|
list and on a listed pair that passes, so the list only shrinks.
|
|
272
277
|
|
|
@@ -289,6 +294,20 @@ npx tsx tools/regen-value-matrix-baseline.ts --dry-run --results /tmp/matrix.jso
|
|
|
289
294
|
A lane that fails in `en-rt` fails in nearly every translation: semantic's English
|
|
290
295
|
parse lost the value, and every translation is rendered from it. Fix that first.
|
|
291
296
|
|
|
297
|
+
## Direct-path shapes on the text path
|
|
298
|
+
|
|
299
|
+
Core's `src/multilingual/*-direct-path.test.ts` files pin shapes core's direct path ran
|
|
300
|
+
(an English source rendered into 23 languages, compiled with `{ language }` on core,
|
|
301
|
+
installed in a fixture, triggered). That path retires with core's parser, so
|
|
302
|
+
`direct-path-shapes.ts` runs the same 287 cases — extracted once into
|
|
303
|
+
`direct-path-shapes.cases.json` — the way a page now runs a translation: the foreign
|
|
304
|
+
text as written on `@hyperfixi/engine` with the adapter's plugin and `lang` on `<html>`.
|
|
305
|
+
Upstream running the English is the oracle; where upstream rejects a core-only English
|
|
306
|
+
source, upstream running the adapter's English is. Signatures are the body diff (text
|
|
307
|
+
nodes normalized) plus a log of `fetch` / `history` / `window.open` / `scrollIntoView`
|
|
308
|
+
calls. `KNOWN` lists the 4 failing cases with their reasons and only shrinks; the gate
|
|
309
|
+
runs in three shards, `direct-path-shapes.<n>.test.ts`.
|
|
310
|
+
|
|
292
311
|
## Troubleshooting
|
|
293
312
|
|
|
294
313
|
### Bundle not found
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* It matters because the bare form is a first-class public surface: MCP
|
|
16
16
|
* `translate_code`, `hyperfixi.translate`/`getAllTranslations`, core's
|
|
17
|
-
* `
|
|
17
|
+
* `multilingual` entry and the VS Code "Show in my language" badge are all
|
|
18
18
|
* routinely handed a single command with no handler around it.
|
|
19
19
|
*
|
|
20
20
|
* WHAT IT ASSERTS
|
package/src/multilingual/cli.ts
CHANGED
|
@@ -25,7 +25,9 @@ const DIST_GUARD_PACKAGES = [
|
|
|
25
25
|
'semantic',
|
|
26
26
|
'i18n',
|
|
27
27
|
'patterns-reference',
|
|
28
|
-
'
|
|
28
|
+
'hyperscript-adapter',
|
|
29
|
+
// The R2 execution validator's host since Phase C2 (core's left in C4).
|
|
30
|
+
'engine',
|
|
29
31
|
];
|
|
30
32
|
|
|
31
33
|
/** True if any .ts under dir is newer than builtAt (early-exit walk). */
|
|
@@ -54,8 +56,8 @@ function findStaleDists(): string[] {
|
|
|
54
56
|
for (const name of DIST_GUARD_PACKAGES) {
|
|
55
57
|
const pkg = path.join(packagesRoot, name);
|
|
56
58
|
const srcDir = path.join(pkg, 'src');
|
|
57
|
-
// Whichever entry the build emits: `.js` for most packages, `.mjs`
|
|
58
|
-
//
|
|
59
|
+
// Whichever entry the build emits: `.js` for most packages, `.mjs` / `.cjs`
|
|
60
|
+
// for a dual build (see scripts/ensure-fresh.sh).
|
|
59
61
|
const marker = ['index.js', 'index.mjs', 'index.cjs']
|
|
60
62
|
.map(f => path.join(pkg, 'dist', f))
|
|
61
63
|
.filter(f => fs.existsSync(f))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The direct-path shapes gate (see direct-path-shapes.ts), in shards —
|
|
3
|
+
* `direct-path-shapes.<n>.test.ts` — so vitest runs them in parallel.
|
|
4
|
+
*
|
|
5
|
+
* Assertions, per shard:
|
|
6
|
+
* 1. every case not in KNOWN holds on the text path: upstream runs its
|
|
7
|
+
* English, the engine runs that English the same way, and every
|
|
8
|
+
* language's translation runs on the engine as upstream runs the English;
|
|
9
|
+
* 2. every KNOWN case still fails — prune one that passes, so the list only
|
|
10
|
+
* shrinks;
|
|
11
|
+
* 3. (shard 0) every KNOWN id names a case.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
15
|
+
import {
|
|
16
|
+
KNOWN,
|
|
17
|
+
caseFails,
|
|
18
|
+
describeFailure,
|
|
19
|
+
loadDirectPathCases,
|
|
20
|
+
runDirectPathShard,
|
|
21
|
+
type DirectPathResult,
|
|
22
|
+
} from './direct-path-shapes';
|
|
23
|
+
|
|
24
|
+
export const DIRECT_PATH_SHARDS = 3;
|
|
25
|
+
|
|
26
|
+
export function describeDirectPathShard(shard: number): void {
|
|
27
|
+
describe(`direct-path shapes on the text path [${shard + 1}/${DIRECT_PATH_SHARDS}]`, () => {
|
|
28
|
+
let results: DirectPathResult[] = [];
|
|
29
|
+
|
|
30
|
+
beforeAll(async () => {
|
|
31
|
+
results = await runDirectPathShard(shard, DIRECT_PATH_SHARDS);
|
|
32
|
+
const pairs = results.reduce((n, r) => n + r.pairs.length, 0);
|
|
33
|
+
const failing = results.filter(caseFails).length;
|
|
34
|
+
console.log(
|
|
35
|
+
`direct-path shapes [${shard + 1}/${DIRECT_PATH_SHARDS}]: ${results.length} cases, ${pairs} translations, ${failing} failing case(s)`
|
|
36
|
+
);
|
|
37
|
+
}, 600_000);
|
|
38
|
+
|
|
39
|
+
it('every case not in KNOWN holds on the text path', () => {
|
|
40
|
+
expect(results.length).toBeGreaterThan(50);
|
|
41
|
+
const failing = results.filter(r => caseFails(r) && !(r.id in KNOWN));
|
|
42
|
+
expect(failing.map(describeFailure)).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('every KNOWN case still fails (prune one that passes)', () => {
|
|
46
|
+
const fixed = results.filter(r => r.id in KNOWN && !caseFails(r)).map(r => r.id);
|
|
47
|
+
expect(fixed).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('every KNOWN id names a case', () => {
|
|
51
|
+
if (shard !== 0) return;
|
|
52
|
+
const ids = new Set(loadDirectPathCases().map(c => c.id));
|
|
53
|
+
expect(Object.keys(KNOWN).filter(id => !ids.has(id))).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct-path shapes gate, shard 1 of 3. See direct-path-shapes.ts for what it
|
|
3
|
+
* runs and direct-path-shapes-gate.ts for the assertions.
|
|
4
|
+
*
|
|
5
|
+
* @vitest-environment node
|
|
6
|
+
* Required, not a preference: under happy-dom the engines bind happy-dom's
|
|
7
|
+
* DOM constructors, not the jsdom window's (see
|
|
8
|
+
* shipped-examples-execution.test.ts).
|
|
9
|
+
*/
|
|
10
|
+
import { describeDirectPathShard } from './direct-path-shapes-gate';
|
|
11
|
+
|
|
12
|
+
describeDirectPathShard(0);
|