@hyperfixi/patterns-reference 2.11.1 → 3.1.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 CHANGED
@@ -7,6 +7,305 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.1.0] - 2026-09-04
11
+
12
+ ### Added
13
+
14
+ - **`hyperscript.use(frontEnd)`** — register the multilingual front-end
15
+ `compile()` consults for a non-English program (and `toLSE`/`fromLSE` for
16
+ a semantic node and a renderer). The contract is `FrontEnd` in
17
+ `@hyperfixi/core` (`parseToAST`, optional `parse`/`render`);
18
+ `@lokascript/semantic` satisfies it through
19
+ `createBridgeFrontEnd(new SemanticGrammarBridge())` from
20
+ `@hyperfixi/core/multilingual`. The full browser bundle registers it at
21
+ boot. The library entry registers nothing and, through 3.x, builds the same
22
+ bridge lazily on the first non-English compile — so nothing changes for a
23
+ consumer with `@lokascript/semantic` installed. **4.0 will remove that
24
+ default:** a non-English `compile()` with no front-end registered will
25
+ return the same result the core-parser-only path returns today, and
26
+ `@lokascript/semantic` becomes an optional peer. Engine-migration plan
27
+ Arc 1, steps 2 and 3.
28
+
29
+ ### Fixed
30
+
31
+ - **Language server: valid non-English code no longer gets a `parse-error`.**
32
+ In `lokascript`/`hyperscript-i18n` mode the server ran core's English
33
+ parser on every region regardless of what the semantic front-end said, so
34
+ `al hacer clic alternar .activo` (accepted at 0.89 confidence) was published
35
+ as `Unexpected token: hacer`. Core's errors are now suppressed for a region
36
+ the front-end accepted in a non-English language. The integration suite
37
+ that claimed to cover this had been discarding every notification; it now
38
+ records `publishDiagnostics` and asserts on it.
39
+
40
+ - **Language server: settings actually reach the server.** Neither VS Code
41
+ extension set `synchronize.configurationSection`, so the client pushed
42
+ `settings: null` and every `mode`/`language`/`maxDiagnostics` value was
43
+ ignored; `initializationOptions` was ignored too. The server now reads
44
+ `initializationOptions`, a pushed `lokascript`/`hyperscript` section, or
45
+ pulls `workspace/configuration` when the push is empty — and merges partial
46
+ objects over the defaults instead of replacing them (a bare
47
+ `{ maxDiagnostics }` used to leave `language` undefined and error on every
48
+ document). Both extensions push their section, and the LokaScript one now
49
+ contributes `lokascript.mode`.
50
+
51
+ - **Both VS Code extension server bundles build and run.** The standalone
52
+ hyperscript extension's `bundle:server` had failed since #1016 (no shim for
53
+ `@hyperfixi/core/multilingual`); the LokaScript extension externalized
54
+ `@hyperfixi/core` without shipping it, so every install ran with core
55
+ absent ("diagnostics and hover degraded"). Core is bundled in now, and CI
56
+ runs both bundle scripts.
57
+
58
+ - **`@lokascript/language-server` declares what it imports.**
59
+ `@lokascript/semantic` was an optional peer but a static import (the
60
+ server crashed at startup without it); `@lokascript/framework` was
61
+ undeclared and got inlined together with `@lokascript/intent` (261 KB) —
62
+ and without `--clean`, `dist/` shipped every previous build's chunk (three
63
+ dead ones, 63% of the tarball). Both are regular dependencies now, the
64
+ build cleans, the 20-byte `--dts` output is gone, and `npm test` rebuilds
65
+ the server it spawns.
66
+
67
+ - **Hyperscript-compat mode stops flagging portable code.** `as Int`, `Float`,
68
+ `JSON`, `Date`, `Set`, `Map` are upstream conversions (the extension list
69
+ is now `Math`/`Values`, the ones core registers and upstream lacks);
70
+ `my.x`/`your.x` evaluate upstream (only `its.` is flagged); and the
71
+ extension-command scan no longer fires inside `y.replace(`, `arr.push(`,
72
+ string literals, `:process`, `--` comments, or the upstream trailing
73
+ `… unless …` modifier.
74
+
75
+ - **Language-server false positives and misses.** A `.hs` file containing a
76
+ query literal like `<li/>` was classified as HTML and lost every feature;
77
+ possessives after `)`/`]`/`>` and apostrophes in `--` comments raised
78
+ `unmatched-quote`; the outline reported the `on` in `toggle .x on me` as a
79
+ handler and `.init` as an init block; `install Foo(args)` produced
80
+ overlapping rename edits VS Code rejects; `on draggable:start` became a
81
+ variable named `:start`; and keyword boundaries were ASCII `\b`, so
82
+ definitions and symbols were silently absent in the ten non-Latin
83
+ languages. Completion-context and document-symbol logic moved out of the
84
+ entry script into `completion-context.ts` / `document-symbols.ts` so the
85
+ unit suite tests the shipped code rather than the hand-copied twins it
86
+ used to carry.
87
+
88
+ - **MCP `get_diagnostics`, `get_completions` and `get_document_symbols` read
89
+ the AST.** The bridge guarded those paths on helper names core never
90
+ exported, so all three had only ever taken their token-based fallback.
91
+ They now read the interchange: the core parser's own errors become
92
+ diagnostics, completions inside a command offer that command's argument
93
+ shapes, symbols carry their commands as children, and a document with
94
+ several `end`-terminated handlers keeps all of them.
95
+
96
+ - **`hyperscript.process()` honours the event grammar.** The API-side DOM
97
+ processor carried its own listener installer for `on …` attributes and
98
+ silently dropped what the runtime's installer handles: `on
99
+ click[event.shiftKey]` fired on a plain click, `on mouseenter or click` never
100
+ fired on mouseenter, `on click from document` never fired. Every browser
101
+ bundle already went through the runtime; `process()` now does too, and
102
+ `config.logAll` moved into the runtime so both paths log. Pinned by
103
+ `api/dom-processor.test.ts`, which runs each case on both paths.
104
+
105
+ - **The browser bundles' attribute processor now runs the element lifecycle,
106
+ and its lazy stub only takes the shapes it can run faithfully.** Measured
107
+ side by side with `hyperscript.process()`: the bundle path never dispatched
108
+ `hyperscript:before:init` / `hyperscript:after:init` and never set
109
+ `data-hyperscript-powered` — both lived only in the API processor, so a
110
+ morph engine reading the marker saw nothing on a bundle-processed page, and
111
+ canceling `before:init` was impossible there. It also compiled every
112
+ attribute as English; it now detects the element's language (`data-lang`,
113
+ closest `lang`, the document's) the way `process()` always did. And the
114
+ `lazyParsing` stub read only the event NAME from the header, so for the
115
+ first event it ignored a filter (`on click[event.shiftKey]` fired on a
116
+ plain click), listened for the first name of an `or` list only, and never
117
+ saw `from <target>` at all; a header with a filter, `(args)`, `or`, `from`,
118
+ `elsewhere`, `queue`, `debounced`, `throttled` or `in` is now processed
119
+ eagerly. `dom/processor-parity.test.ts` runs every row on all three paths
120
+ (API, eager, lazy) and is the gate the DOM-processor collapse landed under.
121
+
122
+ - **`hyperscript.process()` and the browser bundles share one DOM
123
+ processor.** `process()` and `cleanup()` now run on the attribute
124
+ processor every bundle uses, which receives its compiler and runtime from
125
+ the API by injection instead of importing it (the `dom -> api` layering
126
+ edge is gone). What changes for `process()` callers, all of it upstream
127
+ parity: an element is initialized once however many times it is passed or
128
+ scanned (it used to re-install on every call); `<script
129
+ type="text/hyperscript">` tags inside the tree are processed, with or
130
+ without `for=`, before the elements that may `install` what they define;
131
+ each element gets a non-bubbling `load` event once its program has run,
132
+ and an unparseable attribute is reported through `console.error`, the
133
+ `hyperfixi:compile-error` event and `config.onCompileError` (it was logged
134
+ at debug level only). And `cleanup(container)` now un-marks every processed
135
+ descendant — it stripped the root's `data-hyperscript-powered` only — and
136
+ drops a lazy stub still waiting on an element, so cleanup-then-process
137
+ re-initializes the tree once. A second scan in `lazyParsing` mode no longer
138
+ registers a second stub.
139
+ The API-side processor module is deleted; the multilingual bundle's own
140
+ copy of the language walk now calls the shared one, narrowed to the
141
+ languages that bundle ships; and a subtree the `MutationObserver` picks up
142
+ takes the same entry as `process()`.
143
+
144
+ - **`set *<css-property>` writes inline style in every spelling upstream
145
+ accepts.** `set *opacity to 0.5`, `set *opacity of me to 0.5` and
146
+ `set *background-color of me to "red"` were silent no-ops and
147
+ `set the *opacity of me to 0.5` threw; only the possessive `set my *opacity`
148
+ worked. The tokenizer classes `*opacity` as a selector (for `measure`), and
149
+ `set`'s parser now re-types it into the identifier property the runtime's
150
+ style rungs already handle, so one path serves all of them — bare, `of
151
+ <target>`, `the … of <target>`, `<target>'s`. Ten rows pinned against
152
+ `_hyperscript` 0.9.93.
153
+
154
+ - **`require('@hyperfixi/core')` returned `{}`, and the subpath `require`s
155
+ threw — since `"type": "module"`, on 3.0.0 too.** Every `exports.*.require`
156
+ and `main` pointed at a `.js` file built as CommonJS, which Node reads as
157
+ ESM under that flag. The CJS outputs are `.cjs` now (main entry, the 21
158
+ subpaths, the parser modules); the `import` conditions are unchanged. The
159
+ bare-Node check in CI now `require()`s the entries as well as importing
160
+ them, which is the check that would have caught this. Found by the
161
+ end-to-end probe that landed Arc 1's build half.
162
+
163
+ ### Changed
164
+
165
+ - **`@hyperfixi/core`'s library entry no longer bundles the multilingual
166
+ front-end.** `dist/index.mjs`/`index.js` had `@lokascript/semantic`,
167
+ `@lokascript/intent` and `@lokascript/framework` inlined whole (3.33 MB, zero
168
+ dynamic imports left); they are external now (1.04 MB) and load on the first
169
+ non-English `compileAsync`, the way the source always said they would. No
170
+ API change: semantic and intent stay `dependencies`, framework stays an
171
+ optional peer — and is now genuinely one. A consumer that imported both core
172
+ and semantic no longer holds two copies of semantic. `dist/index.min.js`
173
+ (UMD, not in `exports`) stays self-contained. Gated by
174
+ `scripts/check-node-import.mjs` on the sourcemap. Engine-migration plan Arc 1
175
+ step 2, build half.
176
+
177
+ - **`@lokascript/semantic` drops its `asyncSchema`.** Core deleted the `async`
178
+ command in 3.0.0 (#1102); the semantic front-end never emitted the action
179
+ anyway — its `stripAsyncModifier` pass removes the keyword (in all 24
180
+ languages) and parses the following command, so `async fetch /api as json`
181
+ is a `fetch`. The schema, its registration, the `'async'` `ActionType`
182
+ member and the AST-builder mapper row are gone. Kept on purpose: every
183
+ profile's `keywords.async` (the stripper matches on it) and the
184
+ `'then' | 'and' | 'async'` chain type (a different concept). The IR-level
185
+ `async all/race` node lives in `@lokascript/intent` and is untouched.
186
+
187
+ ## [3.0.0] - 2026-09-03
188
+
189
+ Full notes: [GitHub Releases](https://github.com/codetalcott/hyperfixi/releases/tag/v3.0.0).
190
+
191
+ One major, two themes. `@lokascript/i18n`'s grammar transformer is gone,
192
+ because `@lokascript/semantic` overtook it on every row of the translation
193
+ corpus (#973–#1001). And the engine-migration plan
194
+ (`docs-internal/ENGINE_MIGRATION_PLAN.md`) closed its last arc, which deleted
195
+ the exported dead code it had been carrying and collapsed the browser bundle
196
+ lineup to two names (#1099–#1105). One arc is still open: Arc 1's steps 2
197
+ and 3 — `@hyperfixi/core`'s library entry still bundles the semantic
198
+ front-end (recorded in the plan's History, 2026-09-03).
199
+
200
+ ### ⚠ BREAKING
201
+
202
+ - **`@lokascript/i18n` no longer translates** (#1001). `translate()`, `toLocale()`,
203
+ `toEnglish()`, `GrammarTransformer` and `createTransformer` are deleted; the
204
+ package is per-language vocabulary and grammar profiles. Migrate to
205
+ `import { translate } from '@lokascript/semantic'` (same signature; it throws
206
+ on unparseable input where the transformer returned nonsense). The classic-i18n
207
+ browser bundle, `@hyperscript-tools/i18n` and `@lokascript/types-browser` drop
208
+ the same members (#998, #999); the patterns corpus writer is semantic-only (#1000).
209
+ - **The prebuilt browser bundles are two names** (#1105): `hyperfixi-hx.js`
210
+ (small: hybrid parser, blocks, expressions, htmx v1/v2 attributes) and
211
+ `hyperfixi.js` (everything); `hyperfixi-hx-v4.js` and
212
+ `hyperfixi-multilingual.js` stay as separate products. `hyperfixi-lite.js`,
213
+ `-lite-plus.js`, `-minimal.js`, `-standard.js` and their `./browser/*`
214
+ exports are gone — use `hyperfixi-hx.js`, `hyperfixi.js`, or the Vite
215
+ plugin, which picks the tier itself. `./browser/hybrid-complete` remains as
216
+ the plugin's internal fallback.
217
+ - **Exported dead code deleted from `@hyperfixi/core`** — none had a
218
+ production caller: the six `@deprecated` `features/` families and the
219
+ modular bundle's `features` namespace (#1099); `Lexer`/`Tokens` (#1100 —
220
+ use `tokenize`); the `unified-types` `Validator`, the `types.d.ts` shim and
221
+ `registry/multilingual` (#1101); the `async` command (#1102 — it was
222
+ unreachable from parsed hyperscript; the manifest is 58 commands);
223
+ `ContextProviderRegistry`, the context-provider `Proxy`, the registry's
224
+ `context` slot and the plugin `contextProviders` field (#1104 — set
225
+ request-scoped values as `context.locals`).
226
+
227
+ ### Fixed
228
+
229
+ - **A small bundle fails loudly on a command it lacks and names `hyperfixi.js`**
230
+ (#1103). The hybrid parser had been dropping an unrecognised word silently;
231
+ making it loud also exposed and fixed three silent mis-parses in the hybrid
232
+ bundles: unquoted `fetch /api/data` fetched `/`, `send custom:event` sent
233
+ `custom` to `me`, and `repeat forever` ran zero iterations.
234
+ - **English→foreign rendering is gated** (#931–#972, #953): 3105/3105 corpus
235
+ renders parse on the canonical engine; the bare (handler-less) surface has its
236
+ own gate. `as JSON` stays part of the value (#991); a flattened loop header
237
+ closes (#992).
238
+ - Plugins report their real versions (#926); `analyze_content` is unshadowed in
239
+ the MCP server (#927); four dependency advisories resolved; the `domain-flow`
240
+ → `server-bridge` route contract is pinned (#1000s).
241
+
242
+ ### Changed
243
+
244
+ - `hyperfixi.js` and `hyperfixi-hx-v4.js` carry the 24-language render
245
+ vocabulary (~19 KB gzip each) so `translate` works in the browser (#931).
246
+ - Deprecated `@lokascript/domain-*` references repoint to `@lokascript/domains`.
247
+
248
+ ## [2.11.1] - 2026-08-25
249
+
250
+ Patch release. Fixes the release workflow itself: the build now runs **after**
251
+ the version sync, so a published tarball carries its own version rather than
252
+ the previous one, and `version.ts` is committed (#925). Also lands the
253
+ agent-loop A/B benchmark with an isolated generator (#924).
254
+
255
+ ## [2.11.0] - 2026-08-24
256
+
257
+ Full notes: [GitHub Releases](https://github.com/codetalcott/hyperfixi/releases/tag/v2.11.0).
258
+
259
+ ### Changed
260
+
261
+ - **The domain-DSL family moved out** (#909). Twelve packages left this
262
+ monorepo: the ten publishable `@lokascript/domain-*` packages (bdd,
263
+ behaviorspec, config, flow, jsx, learn, llm, sql, todo, voice) plus the
264
+ private `domain-toolkit` and `mcp-multilingual-intent`. They now ship as
265
+ **`@lokascript/domains`**, one subpath export per domain, from the
266
+ [lokascript-domains](https://github.com/codetalcott/lokascript-domains)
267
+ repository.
268
+
269
+ All ten published names are **deprecated on npm**, each naming its
270
+ replacement subpath (`@lokascript/domain-sql` → "import from
271
+ `@lokascript/domains/sql`"). Existing installs keep working — deprecation is
272
+ a warning, not a removal — they simply stop receiving updates. The
273
+ pre-deletion tree is preserved at the `moved/domain-family` tag.
274
+
275
+ _Migration:_ replace nine dependencies with one, and change
276
+ `from '@lokascript/domain-x'` to `from '@lokascript/domains/x'`. The root
277
+ entry absorbed `domain-config` and exports `createDomainRegistry`,
278
+ `registerAllDomains` and `DOMAIN_PRIORITY`.
279
+
280
+ ### Added
281
+
282
+ - **"Show in My Language"** — LSP request plus VSCode command (#921).
283
+ - **Verified-translation badge** in the compilation service (#920).
284
+ - **`scoreFidelity` / `@lokascript/semantic/fidelity`** (#919).
285
+ - **Inert-shape warnings** (#918) and surfaced unconsumed-input warnings (#916)
286
+ in the compilation service.
287
+ - **The agent-loop benchmark** and the silent-failure finding it produced
288
+ (#915), and the MCP server's agent-era roadmap (#914).
289
+ - **Element-collection write-back** for hypermedia tables, with gallery
290
+ examples and four system fixes (#904).
291
+ - **The comprehensive Playwright tier now runs in CI** (#908) alongside
292
+ `quick`. It had never run: 122 specs, hiding four real bugs — a detached
293
+ `startViewTransition`, untracked reads of unset globals, concurrent effects
294
+ clobbering dependency capture, and `put` stringifying DocumentFragments
295
+ (#905).
296
+
297
+ ### Fixed
298
+
299
+ - **The `hyperscript-adapter` review arc** (#895–#902): whole-string
300
+ translation first, taking canonical validity from 2849/3105 to **3105/3105**
301
+ (#899); a host-parser validity gate so an invalid render falls back to the
302
+ author's text (#900); the dead split-statement fallback deleted after
303
+ measuring 0 outputs (#901); and the slim divergence set burned 6 → 1 via
304
+ schema marker data, repairing 39 body-dropping corpus rows (#902).
305
+ - **`qu take.recipient`** — the R1 tail's last `take` row (#910).
306
+ - **The release workflows** no longer reference the moved-out domain family
307
+ (#922).
308
+
10
309
  ## [2.10.0] - 2026-08-01
11
310
 
12
311
  Full notes: [GitHub Releases](https://github.com/codetalcott/hyperfixi/releases/tag/v2.10.0).
@@ -394,7 +693,8 @@ _Synchronized version release. See git history for details._
394
693
  - npm access token stored in GitHub Secrets
395
694
  - 2FA recommended for npm organization
396
695
 
397
- [Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v2.10.0...HEAD
696
+ [Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v3.1.0...HEAD
697
+ [3.1.0]: https://github.com/codetalcott/hyperfixi/compare/v3.0.0...v3.1.0
398
698
  [2.10.0]: https://github.com/codetalcott/hyperfixi/compare/v2.9.0...v2.10.0
399
699
  [2.9.0]: https://github.com/codetalcott/hyperfixi/compare/v2.8.0...v2.9.0
400
700
  [2.8.0]: https://github.com/codetalcott/hyperfixi/compare/v2.5.1...v2.8.0
@@ -5,7 +5,7 @@
5
5
  "hyperscript": "upstream _hyperscript parse with zero recovered errors (extensions loaded: socket, worker, eventsource); parse-level only",
6
6
  "html": "HTML-markup patterns: each _= / script-tag snippet verified individually; hx-live/sse-*/ws-* attributes are hyperfixi-only and block the upstream claim"
7
7
  },
8
- "lokascriptVersion": "2.9.4",
8
+ "lokascriptVersion": "2.11.1",
9
9
  "hyperscriptVersion": "0.9.93"
10
10
  },
11
11
  "engines": {
@@ -169,7 +169,7 @@
169
169
  "wait-for-event": "both",
170
170
  "wait-then": "both",
171
171
  "when-multiple-changes": "both",
172
- "when-value-changes": "lokascript",
172
+ "when-value-changes": "both",
173
173
  "window-keydown": "both",
174
174
  "window-resize": "both",
175
175
  "window-scroll": "both",
@@ -847,8 +847,7 @@
847
847
  },
848
848
  "when-value-changes": {
849
849
  "lokascript": true,
850
- "hyperscript": false,
851
- "hyperscriptError": "Unexpected value: $"
850
+ "hyperscript": true
852
851
  },
853
852
  "window-keydown": {
854
853
  "lokascript": true,
@@ -1,5 +1,5 @@
1
- export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from '../llm-BfjUr-al.mjs';
2
- import { C as ConnectionOptions, P as PatternRole, a as Pattern, R as RoleType } from '../index-1ENU9L4F.mjs';
1
+ export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from '../llm-B5nGz8V1.mjs';
2
+ import { C as ConnectionOptions, P as PatternRole, a as Pattern, R as RoleType } from '../index-6GkHj5yJ.mjs';
3
3
  import { SemanticRole } from '@lokascript/semantic';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
- export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from '../llm-FVj71n77.js';
2
- import { C as ConnectionOptions, P as PatternRole, a as Pattern, R as RoleType } from '../index-1ENU9L4F.js';
1
+ export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from '../llm-rEdJQScF.js';
2
+ import { C as ConnectionOptions, P as PatternRole, a as Pattern, R as RoleType } from '../index-6GkHj5yJ.js';
3
3
  import { SemanticRole } from '@lokascript/semantic';
4
4
 
5
5
  /**
@@ -36,7 +36,21 @@ interface ClassifiedPattern extends Pattern {
36
36
  }
37
37
  type ComplexityLevel = 'simple-command' | 'simple-event' | 'chained-command' | 'multi-line-event' | 'behavior-install' | 'behavior-def' | 'def-function' | 'complex';
38
38
  type WordOrder = 'SVO' | 'SOV' | 'VSO' | 'V2';
39
- type TranslationMethod = 'auto-generated' | 'hand-crafted' | 'verified';
39
+ /**
40
+ * How a `pattern_translations` row was produced. The first three are the
41
+ * values `sync-translations` writes today; the rest are legacy/aspirational
42
+ * labels kept so existing rows and `getTranslationPriority` keep typing.
43
+ *
44
+ * - `semantic-render`: @lokascript/semantic `render(parse_en(en), L)` chosen by
45
+ * the `best` writer (or the `semantic` writer).
46
+ * - `grammar-transform`: @lokascript/i18n GrammarTransformer — under the `best`
47
+ * writer, a row i18n still renders better than semantic (the kept-row ratchet).
48
+ * - `grammar-transform-no-reference`: `best` only — the i18n row because semantic
49
+ * cannot parse the ENGLISH source (a parser-coverage gap, not a render loss).
50
+ * - `keyword-substitute`: word-for-word fallback for a language with no grammar profile.
51
+ * - `original`: the English row; `non-translatable-identity`: markup rows copied verbatim.
52
+ */
53
+ type TranslationMethod = 'semantic-render' | 'grammar-transform' | 'grammar-transform-no-reference' | 'keyword-substitute' | 'original' | 'non-translatable-identity' | 'auto-generated' | 'hand-crafted' | 'verified';
40
54
  /**
41
55
  * A translation of a pattern to a specific language.
42
56
  */
@@ -36,7 +36,21 @@ interface ClassifiedPattern extends Pattern {
36
36
  }
37
37
  type ComplexityLevel = 'simple-command' | 'simple-event' | 'chained-command' | 'multi-line-event' | 'behavior-install' | 'behavior-def' | 'def-function' | 'complex';
38
38
  type WordOrder = 'SVO' | 'SOV' | 'VSO' | 'V2';
39
- type TranslationMethod = 'auto-generated' | 'hand-crafted' | 'verified';
39
+ /**
40
+ * How a `pattern_translations` row was produced. The first three are the
41
+ * values `sync-translations` writes today; the rest are legacy/aspirational
42
+ * labels kept so existing rows and `getTranslationPriority` keep typing.
43
+ *
44
+ * - `semantic-render`: @lokascript/semantic `render(parse_en(en), L)` chosen by
45
+ * the `best` writer (or the `semantic` writer).
46
+ * - `grammar-transform`: @lokascript/i18n GrammarTransformer — under the `best`
47
+ * writer, a row i18n still renders better than semantic (the kept-row ratchet).
48
+ * - `grammar-transform-no-reference`: `best` only — the i18n row because semantic
49
+ * cannot parse the ENGLISH source (a parser-coverage gap, not a render loss).
50
+ * - `keyword-substitute`: word-for-word fallback for a language with no grammar profile.
51
+ * - `original`: the English row; `non-translatable-identity`: markup rows copied verbatim.
52
+ */
53
+ type TranslationMethod = 'semantic-render' | 'grammar-transform' | 'grammar-transform-no-reference' | 'keyword-substitute' | 'original' | 'non-translatable-identity' | 'auto-generated' | 'hand-crafted' | 'verified';
40
54
  /**
41
55
  * A translation of a pattern to a specific language.
42
56
  */
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { C as ConnectionOptions, b as Command, E as Expression, F as Feature, K as Keyword, S as SpecialSymbol, L as LanguageDocsStats, c as LanguageElementType, d as LanguageElement, e as LLMExample, f as PatternsReference } from './index-1ENU9L4F.mjs';
2
- export { g as ClassifiedPattern, h as ComplexityLevel, D as DiscoveryResult, i as EngineCompat, j as ExpressionOperator, a as Pattern, k as PatternStats, l as SearchOptions, m as SyncOptions, n as SyncResult, T as TestOptions, o as Translation, p as TranslationMethod, V as ValidationOptions, q as ValidationResult, r as VerificationResult, W as WordOrder } from './index-1ENU9L4F.mjs';
1
+ import { C as ConnectionOptions, b as Command, E as Expression, F as Feature, K as Keyword, S as SpecialSymbol, L as LanguageDocsStats, c as LanguageElementType, d as LanguageElement, e as LLMExample, f as PatternsReference } from './index-6GkHj5yJ.mjs';
2
+ export { g as ClassifiedPattern, h as ComplexityLevel, D as DiscoveryResult, i as EngineCompat, j as ExpressionOperator, a as Pattern, k as PatternStats, l as SearchOptions, m as SyncOptions, n as SyncResult, T as TestOptions, o as Translation, p as TranslationMethod, V as ValidationOptions, q as ValidationResult, r as VerificationResult, W as WordOrder } from './index-6GkHj5yJ.mjs';
3
3
  import Database from 'better-sqlite3';
4
- export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, w as getBehaviorPatterns, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from './llm-BfjUr-al.mjs';
4
+ export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, w as getBehaviorPatterns, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from './llm-B5nGz8V1.mjs';
5
5
  export { DbStampStatus, checkDbStamp, computeDbInputHash, dbStampPath, discoverPatterns, seedLLMExamples, syncTranslations, validateAllTranslations, writeDbStamp } from './sync/index.mjs';
6
6
  import '@lokascript/semantic';
7
7
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { C as ConnectionOptions, b as Command, E as Expression, F as Feature, K as Keyword, S as SpecialSymbol, L as LanguageDocsStats, c as LanguageElementType, d as LanguageElement, e as LLMExample, f as PatternsReference } from './index-1ENU9L4F.js';
2
- export { g as ClassifiedPattern, h as ComplexityLevel, D as DiscoveryResult, i as EngineCompat, j as ExpressionOperator, a as Pattern, k as PatternStats, l as SearchOptions, m as SyncOptions, n as SyncResult, T as TestOptions, o as Translation, p as TranslationMethod, V as ValidationOptions, q as ValidationResult, r as VerificationResult, W as WordOrder } from './index-1ENU9L4F.js';
1
+ import { C as ConnectionOptions, b as Command, E as Expression, F as Feature, K as Keyword, S as SpecialSymbol, L as LanguageDocsStats, c as LanguageElementType, d as LanguageElement, e as LLMExample, f as PatternsReference } from './index-6GkHj5yJ.js';
2
+ export { g as ClassifiedPattern, h as ComplexityLevel, D as DiscoveryResult, i as EngineCompat, j as ExpressionOperator, a as Pattern, k as PatternStats, l as SearchOptions, m as SyncOptions, n as SyncResult, T as TestOptions, o as Translation, p as TranslationMethod, V as ValidationOptions, q as ValidationResult, r as VerificationResult, W as WordOrder } from './index-6GkHj5yJ.js';
3
3
  import Database from 'better-sqlite3';
4
- export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, w as getBehaviorPatterns, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from './llm-FVj71n77.js';
4
+ export { a as addLLMExample, b as buildFewShotContext, g as getAllPatterns, c as getAllTranslations, w as getBehaviorPatterns, d as getExamplesByCommand, e as getHighConfidenceTranslations, f as getHighQualityExamples, h as getLLMExamples, i as getLLMStats, j as getMostUsedExamples, k as getPatternById, l as getPatternStats, m as getPatternsByCategory, n as getPatternsByCommand, o as getTranslation, p as getTranslationStats, q as getTranslationsByLanguage, r as getVerifiedTranslations, s as getWordOrder, t as searchPatterns, u as updateQualityScore, v as verifyTranslation } from './llm-rEdJQScF.js';
5
5
  export { DbStampStatus, checkDbStamp, computeDbInputHash, dbStampPath, discoverPatterns, seedLLMExamples, syncTranslations, validateAllTranslations, writeDbStamp } from './sync/index.js';
6
6
  import '@lokascript/semantic';
7
7
 
@@ -1,4 +1,4 @@
1
- import { l as SearchOptions, C as ConnectionOptions, a as Pattern, k as PatternStats, o as Translation, W as WordOrder, r as VerificationResult, e as LLMExample } from './index-1ENU9L4F.mjs';
1
+ import { l as SearchOptions, C as ConnectionOptions, a as Pattern, k as PatternStats, o as Translation, W as WordOrder, r as VerificationResult, e as LLMExample } from './index-6GkHj5yJ.mjs';
2
2
 
3
3
  /**
4
4
  * Pattern Queries API
@@ -1,4 +1,4 @@
1
- import { l as SearchOptions, C as ConnectionOptions, a as Pattern, k as PatternStats, o as Translation, W as WordOrder, r as VerificationResult, e as LLMExample } from './index-1ENU9L4F.js';
1
+ import { l as SearchOptions, C as ConnectionOptions, a as Pattern, k as PatternStats, o as Translation, W as WordOrder, r as VerificationResult, e as LLMExample } from './index-6GkHj5yJ.js';
2
2
 
3
3
  /**
4
4
  * Pattern Queries API
@@ -1,4 +1,4 @@
1
- import { D as DiscoveryResult, m as SyncOptions, n as SyncResult, V as ValidationOptions, q as ValidationResult } from '../index-1ENU9L4F.mjs';
1
+ import { D as DiscoveryResult, m as SyncOptions, n as SyncResult, V as ValidationOptions, q as ValidationResult } from '../index-6GkHj5yJ.mjs';
2
2
  import '@lokascript/semantic';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { D as DiscoveryResult, m as SyncOptions, n as SyncResult, V as ValidationOptions, q as ValidationResult } from '../index-1ENU9L4F.js';
1
+ import { D as DiscoveryResult, m as SyncOptions, n as SyncResult, V as ValidationOptions, q as ValidationResult } from '../index-6GkHj5yJ.js';
2
2
  import '@lokascript/semantic';
3
3
 
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperfixi/patterns-reference",
3
- "version": "2.11.1",
3
+ "version": "3.1.0",
4
4
  "description": "Queryable patterns database for hyperscript with multilingual translations and LLM support",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -66,13 +66,13 @@
66
66
  },
67
67
  "homepage": "https://github.com/codetalcott/hyperfixi/tree/main/packages/patterns-reference#readme",
68
68
  "dependencies": {
69
- "@lokascript/semantic": "^2.11.1",
69
+ "@lokascript/semantic": "^3.1.0",
70
70
  "better-sqlite3": "^13.0.1"
71
71
  },
72
72
  "devDependencies": {
73
- "@hyperfixi/core": "^2.11.1",
74
- "@hyperfixi/reactivity": "^2.11.1",
75
- "@hyperfixi/realtime": "^2.11.1",
73
+ "@hyperfixi/core": "^3.1.0",
74
+ "@hyperfixi/reactivity": "^3.1.0",
75
+ "@hyperfixi/realtime": "^3.1.0",
76
76
  "@types/better-sqlite3": "^7.6.0",
77
77
  "@types/node": "^26.1.2",
78
78
  "hyperscript.org": "0.9.93",
@@ -74,6 +74,10 @@ export function computeDbInputHash(dbPath: string): string {
74
74
  const root = repoRootFromDbPath(dbPath);
75
75
  const files = dbInputFiles(dbPath);
76
76
  const h = createHash('sha256');
77
+ // The renderer used to be hashed in here, because a DB written under
78
+ // PATTERNS_RENDERER=i18n|semantic|best was a DIFFERENT corpus from the same
79
+ // source and a gate expecting the default had to see it as stale. There is one
80
+ // renderer now, so the source files alone determine the corpus.
77
81
  h.update(`files:${files.length}\0`);
78
82
  for (const f of files) {
79
83
  h.update(relative(root, f).split(sep).join('/'));
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Locate and replace the hyperscript bodies inside an HTML-markup corpus row.
3
+ *
4
+ * WHY A REGEX, HERE, DELIBERATELY
5
+ * -------------------------------
6
+ * `html-snippets.ts` warns against regex-scanning HTML — correctly, for its job
7
+ * (walking whole `.html` FILES, where a `<pre>` showing escaped source matches a
8
+ * live attribute). This module has the opposite job: it takes one known-markup
9
+ * corpus row and must splice a translated body back in **without disturbing a
10
+ * single other byte** — indentation, quote style, attribute order, inner text.
11
+ * A DOM round-trip (jsdom `innerHTML`) reserializes all of that, so it is the
12
+ * wrong tool: the corpus stores these rows verbatim and they are compared
13
+ * verbatim. Offsets + splice keeps everything outside the attribute value
14
+ * untouched by construction.
15
+ *
16
+ * Scope: the `_` attribute, double- or single-quoted, which is what the corpus's
17
+ * markup rows carry. A value may contain the OTHER quote character — hyperscript
18
+ * string literals inside a double-quoted attribute are single-quoted, which is
19
+ * how `_="set ^user to {name: 'Demo'}"` is written — so the value class excludes
20
+ * only its own delimiter. (`hx-live` bodies are hyperscript too, but every corpus row
21
+ * using one is flagged non-translatable — its attribute NAMES are resolved
22
+ * per-language by vocab modules, so translating only the body would be half a
23
+ * job.) An attribute whose value contains its own quote character cannot occur
24
+ * in well-formed HTML and is not handled.
25
+ */
26
+
27
+ /** One `_="…"` attribute value found in a markup row. */
28
+ export interface AttributeSpan {
29
+ /** Offset of the first character of the VALUE (inside the quotes). */
30
+ readonly start: number;
31
+ /** Offset one past the last character of the value. */
32
+ readonly end: number;
33
+ /** The raw attribute value — the hyperscript body. */
34
+ readonly body: string;
35
+ }
36
+
37
+ // `_` preceded by a tag-name/whitespace boundary so `data_x="…"` is not matched.
38
+ // The value excludes only its own delimiter: `[^"]*` for a double-quoted value,
39
+ // `[^']*` for a single-quoted one. (A single class excluding BOTH quotes was the
40
+ // first version and it silently skipped every attribute containing a hyperscript
41
+ // string literal — `component-with-conditional`, in all 23 languages.)
42
+ const ATTRIBUTE = /(^|[\s"'])_\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
43
+
44
+ /** Every `_="…"` body in `markup`, in source order. */
45
+ export function findHyperscriptAttributes(markup: string): AttributeSpan[] {
46
+ const spans: AttributeSpan[] = [];
47
+ ATTRIBUTE.lastIndex = 0;
48
+ let match: RegExpExecArray | null;
49
+ while ((match = ATTRIBUTE.exec(markup)) !== null) {
50
+ const body = match[2] ?? match[3];
51
+ // The value's offset: match start + everything before the body in the match.
52
+ const start = match.index + match[0].length - body.length - 1;
53
+ spans.push({ start, end: start + body.length, body });
54
+ }
55
+ return spans;
56
+ }
57
+
58
+ /**
59
+ * Replace each span's body with `replacements[i]`, leaving every other byte of
60
+ * `markup` untouched. Splices right-to-left so earlier offsets stay valid.
61
+ */
62
+ export function spliceHyperscriptAttributes(
63
+ markup: string,
64
+ spans: readonly AttributeSpan[],
65
+ replacements: readonly string[]
66
+ ): string {
67
+ if (spans.length !== replacements.length) {
68
+ throw new Error(
69
+ `spliceHyperscriptAttributes: ${spans.length} spans but ${replacements.length} replacements`
70
+ );
71
+ }
72
+ let out = markup;
73
+ for (let i = spans.length - 1; i >= 0; i--) {
74
+ out = out.slice(0, spans[i].start) + replacements[i] + out.slice(spans[i].end);
75
+ }
76
+ return out;
77
+ }
78
+
79
+ /**
80
+ * Whether a corpus row is HTML markup rather than hyperscript text.
81
+ *
82
+ * Mirrors testing-framework's `isHtmlMarkupPattern` (top-level hyperscript never
83
+ * begins with `<`; a leading selector literal like `<button/>` is an expression,
84
+ * never a statement start). Kept as its own copy rather than a dependency: this
85
+ * package is upstream of the testing framework in the build order.
86
+ */
87
+ export function isMarkupRow(code: string): boolean {
88
+ return /^\s*<[a-zA-Z!]/.test(code);
89
+ }
90
+
91
+ /**
92
+ * Whether a parse carried the whole body: every non-whitespace character of the
93
+ * source reappears in its own English re-render.
94
+ *
95
+ * This is the guard that keeps a TRUNCATING parse out of the corpus. Measured
96
+ * 2026-08-27: `set ^user to attrs.data as JSON` parses, scores "faithful"
97
+ * against itself in all 23 languages, and has silently dropped `as JSON` — the
98
+ * conversion is in no role, so every recall metric compares two equally
99
+ * truncated things. Translating such a body would ship the truncation into 23
100
+ * languages; leaving it English keeps the text intact until the parser is fixed.
101
+ *
102
+ * Whitespace is ignored because a faithful re-render legitimately re-spaces
103
+ * punctuation (`{name: 'Demo'}` → `{ name : 'Demo' }`). The known limitation:
104
+ * whitespace INSIDE a string literal is ignored too, so a body differing only
105
+ * by spaces inside quotes would be accepted — narrow, and it cannot drop
106
+ * content, only re-space it.
107
+ */
108
+ export function reRenderPreservesContent(source: string, reRenderedEnglish: string): boolean {
109
+ const strip = (s: string) => s.replace(/\s+/g, '');
110
+ return strip(source) === strip(reRenderedEnglish);
111
+ }
@@ -55,7 +55,30 @@ export type ComplexityLevel =
55
55
  // =============================================================================
56
56
 
57
57
  export type WordOrder = 'SVO' | 'SOV' | 'VSO' | 'V2';
58
- export type TranslationMethod = 'auto-generated' | 'hand-crafted' | 'verified';
58
+ /**
59
+ * How a `pattern_translations` row was produced. The first three are the
60
+ * values `sync-translations` writes today; the rest are legacy/aspirational
61
+ * labels kept so existing rows and `getTranslationPriority` keep typing.
62
+ *
63
+ * - `semantic-render`: @lokascript/semantic `render(parse_en(en), L)` chosen by
64
+ * the `best` writer (or the `semantic` writer).
65
+ * - `grammar-transform`: @lokascript/i18n GrammarTransformer — under the `best`
66
+ * writer, a row i18n still renders better than semantic (the kept-row ratchet).
67
+ * - `grammar-transform-no-reference`: `best` only — the i18n row because semantic
68
+ * cannot parse the ENGLISH source (a parser-coverage gap, not a render loss).
69
+ * - `keyword-substitute`: word-for-word fallback for a language with no grammar profile.
70
+ * - `original`: the English row; `non-translatable-identity`: markup rows copied verbatim.
71
+ */
72
+ export type TranslationMethod =
73
+ | 'semantic-render'
74
+ | 'grammar-transform'
75
+ | 'grammar-transform-no-reference'
76
+ | 'keyword-substitute'
77
+ | 'original'
78
+ | 'non-translatable-identity'
79
+ | 'auto-generated'
80
+ | 'hand-crafted'
81
+ | 'verified';
59
82
 
60
83
  /**
61
84
  * Pattern priority levels for translation selection.