@hyperfixi/patterns-reference 3.1.1 → 3.2.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +47 -36
  3. package/data/engine-verification.json +40 -38
  4. package/data/patterns.db +0 -0
  5. package/data/patterns.db.stamp +1 -0
  6. package/dist/api/index.d.mts +2 -2
  7. package/dist/api/index.d.ts +2 -2
  8. package/dist/api/index.js +198 -98
  9. package/dist/api/index.mjs +197 -100
  10. package/dist/{index-6GkHj5yJ.d.mts → index-CoDUfq2P.d.mts} +39 -3
  11. package/dist/{index-6GkHj5yJ.d.ts → index-CoDUfq2P.d.ts} +39 -3
  12. package/dist/index.d.mts +87 -13
  13. package/dist/index.d.ts +87 -13
  14. package/dist/index.js +261 -144
  15. package/dist/index.mjs +258 -146
  16. package/dist/{llm-B5nGz8V1.d.mts → llm-CCCSw-yp.d.ts} +21 -10
  17. package/dist/{llm-rEdJQScF.d.ts → llm-PxnriEZ_.d.mts} +21 -10
  18. package/dist/sync/index.d.mts +1 -1
  19. package/dist/sync/index.d.ts +1 -1
  20. package/dist/sync/index.js +4 -1
  21. package/dist/sync/index.mjs +4 -1
  22. package/package.json +16 -16
  23. package/src/adapters/llm-adapter.ts +64 -42
  24. package/src/api/engine-filter.ts +37 -0
  25. package/src/api/llm.ts +54 -64
  26. package/src/api/patterns.ts +92 -30
  27. package/src/api/roles.ts +6 -4
  28. package/src/api/translations.ts +34 -27
  29. package/src/database/connection.ts +16 -4
  30. package/src/html-snippets.ts +39 -10
  31. package/src/index.ts +12 -2
  32. package/src/registry/patterns-provider.ts +3 -1
  33. package/src/sync/db-stamp.ts +7 -4
  34. package/src/sync/markup-attributes.ts +15 -0
  35. package/src/sync/verify-parses.ts +42 -0
  36. package/src/types/better-sqlite3.d.ts +2 -0
  37. package/src/types/index.ts +44 -2
  38. package/src/sync/span-mask.ts +0 -166
  39. package/src/sync/translation-checks.ts +0 -282
@@ -1,9 +1,85 @@
1
+ import { dirname, join } from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { canParse } from '@lokascript/semantic';
1
4
  import Database from 'better-sqlite3';
2
5
  import { statSync } from 'fs';
3
- import { fileURLToPath } from 'url';
4
- import { dirname, join } from 'path';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropNames = Object.getOwnPropertyNames;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var init_esm_shims = __esm({
17
+ "../../node_modules/tsup/assets/esm_shims.js"() {
18
+ }
19
+ });
20
+
21
+ // src/sync/markup-attributes.ts
22
+ function findHyperscriptAttributes(markup) {
23
+ const spans = [];
24
+ ATTRIBUTE.lastIndex = 0;
25
+ let match;
26
+ while ((match = ATTRIBUTE.exec(markup)) !== null) {
27
+ const body = match[2] ?? match[3];
28
+ const start = match.index + match[0].length - body.length - 1;
29
+ spans.push({ start, end: start + body.length, body });
30
+ }
31
+ return spans;
32
+ }
33
+ function isMarkupRow(code) {
34
+ return /^\s*<[a-zA-Z!]/.test(code);
35
+ }
36
+ function hyperscriptBodies(code) {
37
+ if (!isMarkupRow(code)) return [code];
38
+ const bodies = findHyperscriptAttributes(code).map((span) => span.body);
39
+ for (const match of code.matchAll(HX_LIVE_ATTRIBUTE)) bodies.push(match[2] ?? match[3] ?? "");
40
+ return bodies.filter((body) => body.trim().length > 0);
41
+ }
42
+ var ATTRIBUTE, HX_LIVE_ATTRIBUTE;
43
+ var init_markup_attributes = __esm({
44
+ "src/sync/markup-attributes.ts"() {
45
+ init_esm_shims();
46
+ ATTRIBUTE = /(^|[\s"'])_\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
47
+ HX_LIVE_ATTRIBUTE = /(^|[\s"'])hx-live\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
48
+ }
49
+ });
50
+
51
+ // src/sync/verify-parses.ts
52
+ var verify_parses_exports = {};
53
+ __export(verify_parses_exports, {
54
+ verifyParses: () => verifyParses
55
+ });
56
+ function verifyParses(code, language, translatable = true) {
57
+ const bodies = hyperscriptBodies(code);
58
+ if (bodies.length === 0) return false;
59
+ const parseLanguage = translatable ? language : "en";
60
+ return bodies.every((body) => {
61
+ try {
62
+ return canParse(body, parseLanguage);
63
+ } catch {
64
+ return false;
65
+ }
66
+ });
67
+ }
68
+ var init_verify_parses = __esm({
69
+ "src/sync/verify-parses.ts"() {
70
+ init_esm_shims();
71
+ init_markup_attributes();
72
+ }
73
+ });
74
+
75
+ // src/api/index.ts
76
+ init_esm_shims();
77
+
78
+ // src/api/patterns.ts
79
+ init_esm_shims();
5
80
 
6
81
  // src/database/connection.ts
82
+ init_esm_shims();
7
83
  var __filename$1 = fileURLToPath(import.meta.url);
8
84
  var __dirname$1 = dirname(__filename$1);
9
85
  var DEFAULT_DB_PATH = process.env.LSP_DB_PATH || process.env.HYPERSCRIPT_LSP_DB || join(__dirname$1, "../data/patterns.db");
@@ -11,10 +87,11 @@ var dbInstance = null;
11
87
  var currentDbPath = null;
12
88
  var openFileId = null;
13
89
  var lastIdentityCheckAt = 0;
90
+ var openReadonly = false;
14
91
  var IDENTITY_TTL_MS = 1e3;
15
- function fileIdentity(path) {
92
+ function fileIdentity(path2) {
16
93
  try {
17
- const s = statSync(path);
94
+ const s = statSync(path2);
18
95
  return `${s.dev}:${s.ino}:${s.size}:${s.mtimeMs}`;
19
96
  } catch {
20
97
  return null;
@@ -22,7 +99,9 @@ function fileIdentity(path) {
22
99
  }
23
100
  function getDatabase(options = {}) {
24
101
  const dbPath = options.dbPath ?? DEFAULT_DB_PATH;
25
- if (dbInstance !== null && currentDbPath === dbPath) {
102
+ const readonly = options.readonly ?? false;
103
+ const canServe = !openReadonly || readonly;
104
+ if (dbInstance !== null && currentDbPath === dbPath && canServe) {
26
105
  const now = Date.now();
27
106
  if (now - lastIdentityCheckAt < IDENTITY_TTL_MS) {
28
107
  return dbInstance;
@@ -38,9 +117,8 @@ function getDatabase(options = {}) {
38
117
  if (dbInstance !== null) {
39
118
  dbInstance.close();
40
119
  }
41
- dbInstance = new Database(dbPath, {
42
- readonly: options.readonly ?? false
43
- });
120
+ dbInstance = new Database(dbPath, { readonly });
121
+ openReadonly = readonly;
44
122
  dbInstance.pragma("foreign_keys = ON");
45
123
  currentDbPath = dbPath;
46
124
  openFileId = fileIdentity(dbPath);
@@ -48,12 +126,26 @@ function getDatabase(options = {}) {
48
126
  return dbInstance;
49
127
  }
50
128
 
129
+ // src/api/patterns.ts
130
+ init_markup_attributes();
131
+
132
+ // src/api/engine-filter.ts
133
+ init_esm_shims();
134
+ function runsOn(column, engine) {
135
+ if (engine === null) return { sql: `${column} IS NULL`, params: [] };
136
+ if (engine === "both") return { sql: `${column} = 'both'`, params: [] };
137
+ return { sql: `${column} IN ('both', ?)`, params: [engine] };
138
+ }
139
+ function exampleCondition(column, engine) {
140
+ return engine === void 0 ? { sql: `${column} IS NOT NULL`, params: [] } : runsOn(column, engine);
141
+ }
142
+
51
143
  // src/api/patterns.ts
52
144
  async function getPatternById(id, options) {
53
145
  const db = getDatabase({ ...options, readonly: true });
54
146
  const row = db.prepare(
55
147
  `
56
- SELECT id, title, raw_code, description, feature, engine, created_at
148
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
57
149
  FROM code_examples
58
150
  WHERE id = ?
59
151
  `
@@ -64,7 +156,7 @@ async function getPatternsByCategory(category, options) {
64
156
  const db = getDatabase({ ...options, readonly: true });
65
157
  const rows = db.prepare(
66
158
  `
67
- SELECT id, title, raw_code, description, feature, engine, created_at
159
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
68
160
  FROM code_examples
69
161
  WHERE feature = ?
70
162
  ORDER BY title
@@ -76,7 +168,7 @@ async function getPatternsByCommand(command, options) {
76
168
  const db = getDatabase({ ...options, readonly: true });
77
169
  const rows = db.prepare(
78
170
  `
79
- SELECT id, title, raw_code, description, feature, engine, created_at
171
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
80
172
  FROM code_examples
81
173
  WHERE raw_code LIKE ?
82
174
  ORDER BY title
@@ -84,32 +176,45 @@ async function getPatternsByCommand(command, options) {
84
176
  ).all(`%${command}%`);
85
177
  return rows.filter((row) => new RegExp(`\\b${command}\\b`, "i").test(row.raw_code)).map(mapRowToPattern);
86
178
  }
87
- async function searchPatterns(query, searchOptions = {}, connOptions) {
88
- const db = getDatabase({ ...connOptions, readonly: true });
89
- const { limit = 50, offset = 0 } = searchOptions;
179
+ function engineFilter(searchOptions) {
180
+ return searchOptions.engine === void 0 ? { sql: "1 = 1", params: [] } : runsOn("engine", searchOptions.engine);
181
+ }
182
+ function usableIn(db, language) {
90
183
  const rows = db.prepare(
91
- `
92
- SELECT id, title, raw_code, description, feature, engine, created_at
93
- FROM code_examples
94
- WHERE title LIKE ? OR raw_code LIKE ? OR description LIKE ?
95
- ORDER BY title
96
- LIMIT ? OFFSET ?
97
- `
98
- ).all(`%${query}%`, `%${query}%`, `%${query}%`, limit, offset);
99
- return rows.map(mapRowToPattern);
184
+ "SELECT code_example_id AS id FROM pattern_translations WHERE language = ? AND verified_parses = 1"
185
+ ).all(language);
186
+ const verified = new Set(rows.map((row) => row.id));
187
+ return (pattern) => verified.has(pattern.id) || hyperscriptBodies(pattern.rawCode).length === 0;
100
188
  }
101
- async function getAllPatterns(searchOptions = {}, connOptions) {
189
+ function queryPatterns(searchOptions, defaultLimit, connOptions, match = { sql: "1 = 1", params: [] }) {
102
190
  const db = getDatabase({ ...connOptions, readonly: true });
103
- const { limit = 1e3, offset = 0 } = searchOptions;
191
+ const { limit = defaultLimit, offset = 0, category, difficulty, language } = searchOptions;
192
+ const runs = engineFilter(searchOptions);
193
+ const inCategory = category === void 0 ? { sql: "1 = 1", params: [] } : { sql: "feature = ?", params: [category] };
104
194
  const rows = db.prepare(
105
195
  `
106
- SELECT id, title, raw_code, description, feature, engine, created_at
196
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
107
197
  FROM code_examples
198
+ WHERE (${match.sql}) AND ${runs.sql} AND ${inCategory.sql}
108
199
  ORDER BY title
109
- LIMIT ? OFFSET ?
110
200
  `
111
- ).all(limit, offset);
112
- return rows.map(mapRowToPattern);
201
+ ).all(...match.params, ...runs.params, ...inCategory.params);
202
+ const inLanguage = language === void 0 ? null : usableIn(db, language);
203
+ return rows.map(mapRowToPattern).filter((pattern) => difficulty === void 0 || pattern.difficulty === difficulty).filter((pattern) => inLanguage === null || inLanguage(pattern)).slice(offset, offset + limit);
204
+ }
205
+ async function searchPatterns(query, searchOptions = {}, connOptions) {
206
+ const like = `%${query}%`;
207
+ const inTranslation = searchOptions.language === void 0 ? { sql: "", params: [] } : {
208
+ sql: " OR id IN (SELECT code_example_id FROM pattern_translations WHERE language = ? AND hyperscript LIKE ?)",
209
+ params: [searchOptions.language, like]
210
+ };
211
+ return queryPatterns(searchOptions, 50, connOptions, {
212
+ sql: `title LIKE ? OR raw_code LIKE ? OR description LIKE ?${inTranslation.sql}`,
213
+ params: [like, like, like, ...inTranslation.params]
214
+ });
215
+ }
216
+ async function getAllPatterns(searchOptions = {}, connOptions) {
217
+ return queryPatterns(searchOptions, 1e3, connOptions);
113
218
  }
114
219
  async function getPatternStats(connOptions) {
115
220
  const db = getDatabase({ ...connOptions, readonly: true });
@@ -164,6 +269,7 @@ function mapRowToPattern(row) {
164
269
  tags: extractTags(row.raw_code),
165
270
  difficulty: inferDifficulty(row.raw_code),
166
271
  engine: row.engine || null,
272
+ translatable: row.translatable !== 0,
167
273
  createdAt: new Date(row.created_at)
168
274
  };
169
275
  }
@@ -188,6 +294,7 @@ function inferDifficulty(code) {
188
294
  }
189
295
 
190
296
  // src/api/translations.ts
297
+ init_esm_shims();
191
298
  async function getTranslation(patternId, language, options) {
192
299
  const db = getDatabase({ ...options, readonly: true });
193
300
  const row = db.prepare(
@@ -249,32 +356,35 @@ async function verifyTranslation(translation, options) {
249
356
  let parseSuccess = false;
250
357
  let errorMessage = null;
251
358
  let confidence = translation.confidence;
359
+ const db = getDatabase(options);
360
+ const example = db.prepare("SELECT translatable FROM code_examples WHERE id = ?").get(translation.codeExampleId);
252
361
  try {
253
- const { canParse, parse } = await import('@lokascript/semantic');
254
- if (canParse(translation.hyperscript, translation.language)) {
255
- parse(translation.hyperscript, translation.language);
256
- parseSuccess = true;
257
- } else {
258
- errorMessage = "canParse returned false";
259
- }
362
+ const { verifyParses: verifyParses2 } = await Promise.resolve().then(() => (init_verify_parses(), verify_parses_exports));
363
+ parseSuccess = verifyParses2(
364
+ translation.hyperscript,
365
+ translation.language,
366
+ example?.translatable !== 0
367
+ );
368
+ if (!parseSuccess) errorMessage = "the semantic parser rejected it (or it has no hyperscript)";
260
369
  } catch (e) {
261
370
  errorMessage = e.message;
262
371
  }
263
- const db = getDatabase(options);
264
- db.prepare(
372
+ db.transaction(() => {
373
+ db.prepare(
374
+ `
375
+ UPDATE pattern_translations
376
+ SET verified_parses = ?, updated_at = datetime('now')
377
+ WHERE id = ?
265
378
  `
266
- UPDATE pattern_translations
267
- SET verified_parses = ?, updated_at = datetime('now')
268
- WHERE id = ?
269
- `
270
- ).run(parseSuccess ? 1 : 0, translation.id);
271
- db.prepare(
379
+ ).run(parseSuccess ? 1 : 0, translation.id);
380
+ db.prepare(
381
+ `
382
+ INSERT INTO pattern_tests
383
+ (code_example_id, language, test_type, success, error_message, test_date)
384
+ VALUES (?, ?, 'parse', ?, ?, datetime('now'))
272
385
  `
273
- INSERT INTO pattern_tests
274
- (code_example_id, language, test_date, parse_success, error_message)
275
- VALUES (?, ?, datetime('now'), ?, ?)
276
- `
277
- ).run(translation.codeExampleId, translation.language, parseSuccess ? 1 : 0, errorMessage);
386
+ ).run(translation.codeExampleId, translation.language, parseSuccess ? 1 : 0, errorMessage);
387
+ })();
278
388
  return {
279
389
  translation,
280
390
  parseSuccess,
@@ -344,74 +454,70 @@ function getWordOrder(language) {
344
454
  }
345
455
 
346
456
  // src/api/llm.ts
457
+ init_esm_shims();
458
+ var EXAMPLES_FROM = `
459
+ SELECT le.*, ce.engine AS engine
460
+ FROM llm_examples le
461
+ JOIN code_examples ce ON ce.id = le.code_example_id`;
347
462
  async function getLLMExamples(prompt, language = "en", limit = 5, options) {
348
463
  const db = getDatabase({ ...options, readonly: true });
464
+ const runs = exampleCondition("ce.engine", options?.engine);
349
465
  const keywords = extractKeywords(prompt);
350
466
  if (keywords.length === 0) {
351
467
  const rows2 = db.prepare(
352
- `
353
- SELECT * FROM llm_examples
354
- WHERE language = ?
355
- ORDER BY quality_score DESC, usage_count DESC
468
+ `${EXAMPLES_FROM}
469
+ WHERE le.language = ? AND ${runs.sql}
470
+ ORDER BY le.quality_score DESC, le.usage_count DESC
356
471
  LIMIT ?
357
472
  `
358
- ).all(language, limit);
359
- trackUsage(
360
- db,
361
- rows2.map((r) => r.id)
362
- );
473
+ ).all(language, ...runs.params, limit);
363
474
  return rows2.map(mapRowToLLMExample);
364
475
  }
365
- const likeClauses = keywords.map(() => "(prompt LIKE ? OR completion LIKE ?)").join(" OR ");
476
+ const likeClauses = keywords.map(() => "(le.prompt LIKE ? OR le.completion LIKE ?)").join(" OR ");
366
477
  const params = keywords.flatMap((k) => [`%${k}%`, `%${k}%`]);
367
478
  const rows = db.prepare(
368
- `
369
- SELECT * FROM llm_examples
370
- WHERE language = ? AND (${likeClauses})
371
- ORDER BY quality_score DESC
479
+ `${EXAMPLES_FROM}
480
+ WHERE le.language = ? AND ${runs.sql} AND (${likeClauses})
481
+ ORDER BY le.quality_score DESC
372
482
  LIMIT ?
373
483
  `
374
- ).all(language, ...params, limit);
375
- trackUsage(
376
- db,
377
- rows.map((r) => r.id)
378
- );
484
+ ).all(language, ...runs.params, ...params, limit);
379
485
  return rows.map(mapRowToLLMExample);
380
486
  }
381
487
  async function getExamplesByCommand(command, language = "en", limit = 5, options) {
382
488
  const db = getDatabase({ ...options, readonly: true });
489
+ const runs = exampleCondition("ce.engine", options?.engine);
383
490
  const rows = db.prepare(
384
- `
385
- SELECT * FROM llm_examples
386
- WHERE language = ? AND completion LIKE ?
387
- ORDER BY quality_score DESC
491
+ `${EXAMPLES_FROM}
492
+ WHERE le.language = ? AND ${runs.sql} AND le.completion LIKE ?
493
+ ORDER BY le.quality_score DESC
388
494
  LIMIT ?
389
495
  `
390
- ).all(language, `%${command}%`, limit);
496
+ ).all(language, ...runs.params, `%${command}%`, limit);
391
497
  return rows.map(mapRowToLLMExample);
392
498
  }
393
499
  async function getHighQualityExamples(language = "en", minQuality = 0.8, limit = 10, options) {
394
500
  const db = getDatabase({ ...options, readonly: true });
501
+ const runs = exampleCondition("ce.engine", options?.engine);
395
502
  const rows = db.prepare(
396
- `
397
- SELECT * FROM llm_examples
398
- WHERE language = ? AND quality_score >= ?
399
- ORDER BY quality_score DESC, usage_count DESC
503
+ `${EXAMPLES_FROM}
504
+ WHERE le.language = ? AND ${runs.sql} AND le.quality_score >= ?
505
+ ORDER BY le.quality_score DESC, le.usage_count DESC
400
506
  LIMIT ?
401
507
  `
402
- ).all(language, minQuality, limit);
508
+ ).all(language, ...runs.params, minQuality, limit);
403
509
  return rows.map(mapRowToLLMExample);
404
510
  }
405
511
  async function getMostUsedExamples(language = "en", limit = 10, options) {
406
512
  const db = getDatabase({ ...options, readonly: true });
513
+ const runs = exampleCondition("ce.engine", options?.engine);
407
514
  const rows = db.prepare(
408
- `
409
- SELECT * FROM llm_examples
410
- WHERE language = ?
411
- ORDER BY usage_count DESC, quality_score DESC
515
+ `${EXAMPLES_FROM}
516
+ WHERE le.language = ? AND ${runs.sql}
517
+ ORDER BY le.usage_count DESC, le.quality_score DESC
412
518
  LIMIT ?
413
519
  `
414
- ).all(language, limit);
520
+ ).all(language, ...runs.params, limit);
415
521
  return rows.map(mapRowToLLMExample);
416
522
  }
417
523
  async function buildFewShotContext(prompt, language = "en", numExamples = 3, options) {
@@ -509,18 +615,6 @@ function extractKeywords(prompt) {
509
615
  ]);
510
616
  return prompt.toLowerCase().split(/\W+/).filter((word) => word.length > 2 && !stopWords.has(word));
511
617
  }
512
- function trackUsage(db, ids) {
513
- if (ids.length === 0) return;
514
- try {
515
- const stmt = db.prepare(`
516
- UPDATE llm_examples SET usage_count = usage_count + 1 WHERE id = ?
517
- `);
518
- for (const id of ids) {
519
- stmt.run(id);
520
- }
521
- } catch {
522
- }
523
- }
524
618
  function mapRowToLLMExample(row) {
525
619
  return {
526
620
  id: row.id,
@@ -530,11 +624,13 @@ function mapRowToLLMExample(row) {
530
624
  completion: row.completion,
531
625
  qualityScore: row.quality_score,
532
626
  usageCount: row.usage_count,
533
- createdAt: new Date(row.created_at)
627
+ createdAt: new Date(row.created_at),
628
+ engine: row.engine ?? null
534
629
  };
535
630
  }
536
631
 
537
632
  // src/api/roles.ts
633
+ init_esm_shims();
538
634
  async function getPatternRoles(patternId, options) {
539
635
  const db = getDatabase({ ...options, readonly: true });
540
636
  const rows = db.prepare(
@@ -551,7 +647,7 @@ async function getPatternsByRole(role, options) {
551
647
  const db = getDatabase({ ...options, readonly: true });
552
648
  const rows = db.prepare(
553
649
  `
554
- SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
650
+ SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
555
651
  FROM code_examples ce
556
652
  INNER JOIN pattern_roles pr ON ce.id = pr.code_example_id
557
653
  WHERE pr.role = ?
@@ -567,7 +663,7 @@ async function getPatternsByRoles(roles, matchMode = "any", options) {
567
663
  const placeholders = roles.map(() => "?").join(", ");
568
664
  const rows = db.prepare(
569
665
  `
570
- SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
666
+ SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
571
667
  FROM code_examples ce
572
668
  INNER JOIN pattern_roles pr ON ce.id = pr.code_example_id
573
669
  WHERE pr.role IN (${placeholders})
@@ -579,7 +675,7 @@ async function getPatternsByRoles(roles, matchMode = "any", options) {
579
675
  const placeholders = roles.map(() => "?").join(", ");
580
676
  const rows = db.prepare(
581
677
  `
582
- SELECT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
678
+ SELECT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
583
679
  FROM code_examples ce
584
680
  WHERE (
585
681
  SELECT COUNT(DISTINCT pr.role)
@@ -596,7 +692,7 @@ async function getPatternsByRoleValue(role, value, options) {
596
692
  const db = getDatabase({ ...options, readonly: true });
597
693
  const rows = db.prepare(
598
694
  `
599
- SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
695
+ SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
600
696
  FROM code_examples ce
601
697
  INNER JOIN pattern_roles pr ON ce.id = pr.code_example_id
602
698
  WHERE pr.role = ? AND pr.role_value LIKE ?
@@ -729,6 +825,7 @@ function mapRowToPattern2(row) {
729
825
  tags: extractTags2(row.raw_code),
730
826
  difficulty: inferDifficulty2(row.raw_code),
731
827
  engine: row.engine || null,
828
+ translatable: row.translatable !== 0,
732
829
  createdAt: new Date(row.created_at)
733
830
  };
734
831
  }
@@ -24,6 +24,12 @@ interface Pattern {
24
24
  tags: string[];
25
25
  difficulty: 'beginner' | 'intermediate' | 'advanced';
26
26
  engine: EngineCompat | null;
27
+ /**
28
+ * Whether the corpus writer translates this row. `false` rows are copied
29
+ * verbatim into every language (markup whose attribute names are resolved by
30
+ * vocab modules, or markup with no hyperscript at all).
31
+ */
32
+ translatable: boolean;
27
33
  createdAt: Date;
28
34
  }
29
35
  /**
@@ -48,7 +54,8 @@ type WordOrder = 'SVO' | 'SOV' | 'VSO' | 'V2';
48
54
  * - `grammar-transform-no-reference`: `best` only — the i18n row because semantic
49
55
  * cannot parse the ENGLISH source (a parser-coverage gap, not a render loss).
50
56
  * - `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.
57
+ * - `original`: the English row; `non-translatable-identity`: a non-translatable row copied
58
+ * verbatim (the markup rows, and intercept-cache-strategies).
52
59
  */
53
60
  type TranslationMethod = 'semantic-render' | 'grammar-transform' | 'grammar-transform-no-reference' | 'keyword-substitute' | 'original' | 'non-translatable-identity' | 'auto-generated' | 'hand-crafted' | 'verified';
54
61
  /**
@@ -89,8 +96,24 @@ interface LLMExample {
89
96
  prompt: string;
90
97
  completion: string;
91
98
  qualityScore: number;
99
+ /** @deprecated 0 unless something calls trackExampleUsage() (see getMostUsedExamples). */
92
100
  usageCount: number;
93
101
  createdAt: Date;
102
+ /**
103
+ * The engine(s) verified to run this example's pattern
104
+ * (`code_examples.engine`). Never null in results: an example no engine
105
+ * runs is not served.
106
+ */
107
+ engine: EngineCompat | null;
108
+ }
109
+ /**
110
+ * Options for the LLM-example getters. `engine` narrows to examples whose
111
+ * pattern runs on that engine — 'hyperscript' = both + upstream-only,
112
+ * 'lokascript' = both + hyperfixi-only, 'both' = both. With or without it,
113
+ * an example whose pattern NO engine runs is never returned.
114
+ */
115
+ interface ExampleOptions extends ConnectionOptions {
116
+ engine?: EngineCompat;
94
117
  }
95
118
 
96
119
  /**
@@ -109,10 +132,22 @@ interface PatternRole {
109
132
  roleType: RoleType | null;
110
133
  required: boolean;
111
134
  }
135
+ /** Honoured by searchPatterns and getAllPatterns; the page is taken after every filter. */
112
136
  interface SearchOptions {
137
+ /**
138
+ * Patterns usable in this language: a translation there that parses
139
+ * (`verified_parses`), or no hyperscript at all to translate. searchPatterns
140
+ * also matches its query against that translation.
141
+ */
113
142
  language?: string;
143
+ /** The pattern's category (`Pattern.category`). */
114
144
  category?: string;
145
+ /** As inferred from the code (`Pattern.difficulty`). */
115
146
  difficulty?: 'beginner' | 'intermediate' | 'advanced';
147
+ /**
148
+ * Patterns that run on this engine ('hyperscript' / 'lokascript' include
149
+ * 'both'); `null` = the patterns no engine runs; omitted = no filter.
150
+ */
116
151
  engine?: EngineCompat | null;
117
152
  limit?: number;
118
153
  offset?: number;
@@ -302,9 +337,10 @@ interface PatternsReference {
302
337
  getTranslation(patternId: string, language: string): Promise<Translation | null>;
303
338
  getAllTranslations(patternId: string): Promise<Translation[]>;
304
339
  verifyTranslation(translation: Translation): Promise<VerificationResult>;
305
- getLLMExamples(prompt: string, language?: string, limit?: number): Promise<LLMExample[]>;
340
+ /** Never returns an example no engine runs; `engine` narrows further (see ExampleOptions). */
341
+ getLLMExamples(prompt: string, language?: string, limit?: number, engine?: EngineCompat): Promise<LLMExample[]>;
306
342
  getStats(): Promise<PatternStats>;
307
343
  close(): void;
308
344
  }
309
345
 
310
- export type { ConnectionOptions as C, DiscoveryResult as D, Expression as E, Feature as F, Keyword as K, LanguageDocsStats as L, PatternRole as P, RoleType as R, SpecialSymbol as S, TestOptions as T, ValidationOptions as V, WordOrder as W, Pattern as a, Command as b, LanguageElementType as c, LanguageElement as d, LLMExample as e, PatternsReference as f, ClassifiedPattern as g, ComplexityLevel as h, EngineCompat as i, ExpressionOperator as j, PatternStats as k, SearchOptions as l, SyncOptions as m, SyncResult as n, Translation as o, TranslationMethod as p, ValidationResult as q, VerificationResult as r };
346
+ export type { ConnectionOptions as C, DiscoveryResult as D, Expression as E, Feature as F, Keyword as K, LanguageDocsStats as L, PatternRole as P, RoleType as R, SpecialSymbol as S, TestOptions as T, ValidationOptions as V, WordOrder as W, Pattern as a, Command as b, LanguageElementType as c, LanguageElement as d, EngineCompat as e, LLMExample as f, PatternsReference as g, ClassifiedPattern as h, ComplexityLevel as i, ExampleOptions as j, ExpressionOperator as k, PatternStats as l, SearchOptions as m, SyncOptions as n, SyncResult as o, Translation as p, TranslationMethod as q, ValidationResult as r, VerificationResult as s };
@@ -24,6 +24,12 @@ interface Pattern {
24
24
  tags: string[];
25
25
  difficulty: 'beginner' | 'intermediate' | 'advanced';
26
26
  engine: EngineCompat | null;
27
+ /**
28
+ * Whether the corpus writer translates this row. `false` rows are copied
29
+ * verbatim into every language (markup whose attribute names are resolved by
30
+ * vocab modules, or markup with no hyperscript at all).
31
+ */
32
+ translatable: boolean;
27
33
  createdAt: Date;
28
34
  }
29
35
  /**
@@ -48,7 +54,8 @@ type WordOrder = 'SVO' | 'SOV' | 'VSO' | 'V2';
48
54
  * - `grammar-transform-no-reference`: `best` only — the i18n row because semantic
49
55
  * cannot parse the ENGLISH source (a parser-coverage gap, not a render loss).
50
56
  * - `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.
57
+ * - `original`: the English row; `non-translatable-identity`: a non-translatable row copied
58
+ * verbatim (the markup rows, and intercept-cache-strategies).
52
59
  */
53
60
  type TranslationMethod = 'semantic-render' | 'grammar-transform' | 'grammar-transform-no-reference' | 'keyword-substitute' | 'original' | 'non-translatable-identity' | 'auto-generated' | 'hand-crafted' | 'verified';
54
61
  /**
@@ -89,8 +96,24 @@ interface LLMExample {
89
96
  prompt: string;
90
97
  completion: string;
91
98
  qualityScore: number;
99
+ /** @deprecated 0 unless something calls trackExampleUsage() (see getMostUsedExamples). */
92
100
  usageCount: number;
93
101
  createdAt: Date;
102
+ /**
103
+ * The engine(s) verified to run this example's pattern
104
+ * (`code_examples.engine`). Never null in results: an example no engine
105
+ * runs is not served.
106
+ */
107
+ engine: EngineCompat | null;
108
+ }
109
+ /**
110
+ * Options for the LLM-example getters. `engine` narrows to examples whose
111
+ * pattern runs on that engine — 'hyperscript' = both + upstream-only,
112
+ * 'lokascript' = both + hyperfixi-only, 'both' = both. With or without it,
113
+ * an example whose pattern NO engine runs is never returned.
114
+ */
115
+ interface ExampleOptions extends ConnectionOptions {
116
+ engine?: EngineCompat;
94
117
  }
95
118
 
96
119
  /**
@@ -109,10 +132,22 @@ interface PatternRole {
109
132
  roleType: RoleType | null;
110
133
  required: boolean;
111
134
  }
135
+ /** Honoured by searchPatterns and getAllPatterns; the page is taken after every filter. */
112
136
  interface SearchOptions {
137
+ /**
138
+ * Patterns usable in this language: a translation there that parses
139
+ * (`verified_parses`), or no hyperscript at all to translate. searchPatterns
140
+ * also matches its query against that translation.
141
+ */
113
142
  language?: string;
143
+ /** The pattern's category (`Pattern.category`). */
114
144
  category?: string;
145
+ /** As inferred from the code (`Pattern.difficulty`). */
115
146
  difficulty?: 'beginner' | 'intermediate' | 'advanced';
147
+ /**
148
+ * Patterns that run on this engine ('hyperscript' / 'lokascript' include
149
+ * 'both'); `null` = the patterns no engine runs; omitted = no filter.
150
+ */
116
151
  engine?: EngineCompat | null;
117
152
  limit?: number;
118
153
  offset?: number;
@@ -302,9 +337,10 @@ interface PatternsReference {
302
337
  getTranslation(patternId: string, language: string): Promise<Translation | null>;
303
338
  getAllTranslations(patternId: string): Promise<Translation[]>;
304
339
  verifyTranslation(translation: Translation): Promise<VerificationResult>;
305
- getLLMExamples(prompt: string, language?: string, limit?: number): Promise<LLMExample[]>;
340
+ /** Never returns an example no engine runs; `engine` narrows further (see ExampleOptions). */
341
+ getLLMExamples(prompt: string, language?: string, limit?: number, engine?: EngineCompat): Promise<LLMExample[]>;
306
342
  getStats(): Promise<PatternStats>;
307
343
  close(): void;
308
344
  }
309
345
 
310
- export type { ConnectionOptions as C, DiscoveryResult as D, Expression as E, Feature as F, Keyword as K, LanguageDocsStats as L, PatternRole as P, RoleType as R, SpecialSymbol as S, TestOptions as T, ValidationOptions as V, WordOrder as W, Pattern as a, Command as b, LanguageElementType as c, LanguageElement as d, LLMExample as e, PatternsReference as f, ClassifiedPattern as g, ComplexityLevel as h, EngineCompat as i, ExpressionOperator as j, PatternStats as k, SearchOptions as l, SyncOptions as m, SyncResult as n, Translation as o, TranslationMethod as p, ValidationResult as q, VerificationResult as r };
346
+ export type { ConnectionOptions as C, DiscoveryResult as D, Expression as E, Feature as F, Keyword as K, LanguageDocsStats as L, PatternRole as P, RoleType as R, SpecialSymbol as S, TestOptions as T, ValidationOptions as V, WordOrder as W, Pattern as a, Command as b, LanguageElementType as c, LanguageElement as d, EngineCompat as e, LLMExample as f, PatternsReference as g, ClassifiedPattern as h, ComplexityLevel as i, ExampleOptions as j, ExpressionOperator as k, PatternStats as l, SearchOptions as m, SyncOptions as n, SyncResult as o, Translation as p, TranslationMethod as q, ValidationResult as r, VerificationResult as s };