@hyperfixi/patterns-reference 3.1.1 → 3.3.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 +186 -1
  2. package/README.md +47 -36
  3. package/data/engine-verification.json +58 -65
  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
package/dist/api/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var semantic = require('@lokascript/semantic');
3
4
  var Database = require('better-sqlite3');
4
5
  var fs = require('fs');
5
6
  var url = require('url');
@@ -9,9 +10,87 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
10
11
  var Database__default = /*#__PURE__*/_interopDefault(Database);
11
12
 
13
+ var __defProp = Object.defineProperty;
14
+ var __getOwnPropNames = Object.getOwnPropertyNames;
15
+ var __esm = (fn, res) => function __init() {
16
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
17
+ };
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, { get: all[name], enumerable: true });
21
+ };
22
+
12
23
  // ../../node_modules/tsup/assets/cjs_shims.js
13
- var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
14
- var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
24
+ var getImportMetaUrl, importMetaUrl;
25
+ var init_cjs_shims = __esm({
26
+ "../../node_modules/tsup/assets/cjs_shims.js"() {
27
+ getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
28
+ importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+ }
30
+ });
31
+
32
+ // src/sync/markup-attributes.ts
33
+ function findHyperscriptAttributes(markup) {
34
+ const spans = [];
35
+ ATTRIBUTE.lastIndex = 0;
36
+ let match;
37
+ while ((match = ATTRIBUTE.exec(markup)) !== null) {
38
+ const body = match[2] ?? match[3];
39
+ const start = match.index + match[0].length - body.length - 1;
40
+ spans.push({ start, end: start + body.length, body });
41
+ }
42
+ return spans;
43
+ }
44
+ function isMarkupRow(code) {
45
+ return /^\s*<[a-zA-Z!]/.test(code);
46
+ }
47
+ function hyperscriptBodies(code) {
48
+ if (!isMarkupRow(code)) return [code];
49
+ const bodies = findHyperscriptAttributes(code).map((span) => span.body);
50
+ for (const match of code.matchAll(HX_LIVE_ATTRIBUTE)) bodies.push(match[2] ?? match[3] ?? "");
51
+ return bodies.filter((body) => body.trim().length > 0);
52
+ }
53
+ var ATTRIBUTE, HX_LIVE_ATTRIBUTE;
54
+ var init_markup_attributes = __esm({
55
+ "src/sync/markup-attributes.ts"() {
56
+ init_cjs_shims();
57
+ ATTRIBUTE = /(^|[\s"'])_\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
58
+ HX_LIVE_ATTRIBUTE = /(^|[\s"'])hx-live\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
59
+ }
60
+ });
61
+
62
+ // src/sync/verify-parses.ts
63
+ var verify_parses_exports = {};
64
+ __export(verify_parses_exports, {
65
+ verifyParses: () => verifyParses
66
+ });
67
+ function verifyParses(code, language, translatable = true) {
68
+ const bodies = hyperscriptBodies(code);
69
+ if (bodies.length === 0) return false;
70
+ const parseLanguage = translatable ? language : "en";
71
+ return bodies.every((body) => {
72
+ try {
73
+ return semantic.canParse(body, parseLanguage);
74
+ } catch {
75
+ return false;
76
+ }
77
+ });
78
+ }
79
+ var init_verify_parses = __esm({
80
+ "src/sync/verify-parses.ts"() {
81
+ init_cjs_shims();
82
+ init_markup_attributes();
83
+ }
84
+ });
85
+
86
+ // src/api/index.ts
87
+ init_cjs_shims();
88
+
89
+ // src/api/patterns.ts
90
+ init_cjs_shims();
91
+
92
+ // src/database/connection.ts
93
+ init_cjs_shims();
15
94
  var __filename2 = url.fileURLToPath(importMetaUrl);
16
95
  var __dirname$1 = path.dirname(__filename2);
17
96
  var DEFAULT_DB_PATH = process.env.LSP_DB_PATH || process.env.HYPERSCRIPT_LSP_DB || path.join(__dirname$1, "../data/patterns.db");
@@ -19,6 +98,7 @@ var dbInstance = null;
19
98
  var currentDbPath = null;
20
99
  var openFileId = null;
21
100
  var lastIdentityCheckAt = 0;
101
+ var openReadonly = false;
22
102
  var IDENTITY_TTL_MS = 1e3;
23
103
  function fileIdentity(path) {
24
104
  try {
@@ -30,7 +110,9 @@ function fileIdentity(path) {
30
110
  }
31
111
  function getDatabase(options = {}) {
32
112
  const dbPath = options.dbPath ?? DEFAULT_DB_PATH;
33
- if (dbInstance !== null && currentDbPath === dbPath) {
113
+ const readonly = options.readonly ?? false;
114
+ const canServe = !openReadonly || readonly;
115
+ if (dbInstance !== null && currentDbPath === dbPath && canServe) {
34
116
  const now = Date.now();
35
117
  if (now - lastIdentityCheckAt < IDENTITY_TTL_MS) {
36
118
  return dbInstance;
@@ -46,9 +128,8 @@ function getDatabase(options = {}) {
46
128
  if (dbInstance !== null) {
47
129
  dbInstance.close();
48
130
  }
49
- dbInstance = new Database__default.default(dbPath, {
50
- readonly: options.readonly ?? false
51
- });
131
+ dbInstance = new Database__default.default(dbPath, { readonly });
132
+ openReadonly = readonly;
52
133
  dbInstance.pragma("foreign_keys = ON");
53
134
  currentDbPath = dbPath;
54
135
  openFileId = fileIdentity(dbPath);
@@ -56,12 +137,26 @@ function getDatabase(options = {}) {
56
137
  return dbInstance;
57
138
  }
58
139
 
140
+ // src/api/patterns.ts
141
+ init_markup_attributes();
142
+
143
+ // src/api/engine-filter.ts
144
+ init_cjs_shims();
145
+ function runsOn(column, engine) {
146
+ if (engine === null) return { sql: `${column} IS NULL`, params: [] };
147
+ if (engine === "both") return { sql: `${column} = 'both'`, params: [] };
148
+ return { sql: `${column} IN ('both', ?)`, params: [engine] };
149
+ }
150
+ function exampleCondition(column, engine) {
151
+ return engine === void 0 ? { sql: `${column} IS NOT NULL`, params: [] } : runsOn(column, engine);
152
+ }
153
+
59
154
  // src/api/patterns.ts
60
155
  async function getPatternById(id, options) {
61
156
  const db = getDatabase({ ...options, readonly: true });
62
157
  const row = db.prepare(
63
158
  `
64
- SELECT id, title, raw_code, description, feature, engine, created_at
159
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
65
160
  FROM code_examples
66
161
  WHERE id = ?
67
162
  `
@@ -72,7 +167,7 @@ async function getPatternsByCategory(category, options) {
72
167
  const db = getDatabase({ ...options, readonly: true });
73
168
  const rows = db.prepare(
74
169
  `
75
- SELECT id, title, raw_code, description, feature, engine, created_at
170
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
76
171
  FROM code_examples
77
172
  WHERE feature = ?
78
173
  ORDER BY title
@@ -84,7 +179,7 @@ async function getPatternsByCommand(command, options) {
84
179
  const db = getDatabase({ ...options, readonly: true });
85
180
  const rows = db.prepare(
86
181
  `
87
- SELECT id, title, raw_code, description, feature, engine, created_at
182
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
88
183
  FROM code_examples
89
184
  WHERE raw_code LIKE ?
90
185
  ORDER BY title
@@ -92,32 +187,45 @@ async function getPatternsByCommand(command, options) {
92
187
  ).all(`%${command}%`);
93
188
  return rows.filter((row) => new RegExp(`\\b${command}\\b`, "i").test(row.raw_code)).map(mapRowToPattern);
94
189
  }
95
- async function searchPatterns(query, searchOptions = {}, connOptions) {
96
- const db = getDatabase({ ...connOptions, readonly: true });
97
- const { limit = 50, offset = 0 } = searchOptions;
190
+ function engineFilter(searchOptions) {
191
+ return searchOptions.engine === void 0 ? { sql: "1 = 1", params: [] } : runsOn("engine", searchOptions.engine);
192
+ }
193
+ function usableIn(db, language) {
98
194
  const rows = db.prepare(
99
- `
100
- SELECT id, title, raw_code, description, feature, engine, created_at
101
- FROM code_examples
102
- WHERE title LIKE ? OR raw_code LIKE ? OR description LIKE ?
103
- ORDER BY title
104
- LIMIT ? OFFSET ?
105
- `
106
- ).all(`%${query}%`, `%${query}%`, `%${query}%`, limit, offset);
107
- return rows.map(mapRowToPattern);
195
+ "SELECT code_example_id AS id FROM pattern_translations WHERE language = ? AND verified_parses = 1"
196
+ ).all(language);
197
+ const verified = new Set(rows.map((row) => row.id));
198
+ return (pattern) => verified.has(pattern.id) || hyperscriptBodies(pattern.rawCode).length === 0;
108
199
  }
109
- async function getAllPatterns(searchOptions = {}, connOptions) {
200
+ function queryPatterns(searchOptions, defaultLimit, connOptions, match = { sql: "1 = 1", params: [] }) {
110
201
  const db = getDatabase({ ...connOptions, readonly: true });
111
- const { limit = 1e3, offset = 0 } = searchOptions;
202
+ const { limit = defaultLimit, offset = 0, category, difficulty, language } = searchOptions;
203
+ const runs = engineFilter(searchOptions);
204
+ const inCategory = category === void 0 ? { sql: "1 = 1", params: [] } : { sql: "feature = ?", params: [category] };
112
205
  const rows = db.prepare(
113
206
  `
114
- SELECT id, title, raw_code, description, feature, engine, created_at
207
+ SELECT id, title, raw_code, description, feature, engine, translatable, created_at
115
208
  FROM code_examples
209
+ WHERE (${match.sql}) AND ${runs.sql} AND ${inCategory.sql}
116
210
  ORDER BY title
117
- LIMIT ? OFFSET ?
118
211
  `
119
- ).all(limit, offset);
120
- return rows.map(mapRowToPattern);
212
+ ).all(...match.params, ...runs.params, ...inCategory.params);
213
+ const inLanguage = language === void 0 ? null : usableIn(db, language);
214
+ return rows.map(mapRowToPattern).filter((pattern) => difficulty === void 0 || pattern.difficulty === difficulty).filter((pattern) => inLanguage === null || inLanguage(pattern)).slice(offset, offset + limit);
215
+ }
216
+ async function searchPatterns(query, searchOptions = {}, connOptions) {
217
+ const like = `%${query}%`;
218
+ const inTranslation = searchOptions.language === void 0 ? { sql: "", params: [] } : {
219
+ sql: " OR id IN (SELECT code_example_id FROM pattern_translations WHERE language = ? AND hyperscript LIKE ?)",
220
+ params: [searchOptions.language, like]
221
+ };
222
+ return queryPatterns(searchOptions, 50, connOptions, {
223
+ sql: `title LIKE ? OR raw_code LIKE ? OR description LIKE ?${inTranslation.sql}`,
224
+ params: [like, like, like, ...inTranslation.params]
225
+ });
226
+ }
227
+ async function getAllPatterns(searchOptions = {}, connOptions) {
228
+ return queryPatterns(searchOptions, 1e3, connOptions);
121
229
  }
122
230
  async function getPatternStats(connOptions) {
123
231
  const db = getDatabase({ ...connOptions, readonly: true });
@@ -172,6 +280,7 @@ function mapRowToPattern(row) {
172
280
  tags: extractTags(row.raw_code),
173
281
  difficulty: inferDifficulty(row.raw_code),
174
282
  engine: row.engine || null,
283
+ translatable: row.translatable !== 0,
175
284
  createdAt: new Date(row.created_at)
176
285
  };
177
286
  }
@@ -196,6 +305,7 @@ function inferDifficulty(code) {
196
305
  }
197
306
 
198
307
  // src/api/translations.ts
308
+ init_cjs_shims();
199
309
  async function getTranslation(patternId, language, options) {
200
310
  const db = getDatabase({ ...options, readonly: true });
201
311
  const row = db.prepare(
@@ -257,32 +367,35 @@ async function verifyTranslation(translation, options) {
257
367
  let parseSuccess = false;
258
368
  let errorMessage = null;
259
369
  let confidence = translation.confidence;
370
+ const db = getDatabase(options);
371
+ const example = db.prepare("SELECT translatable FROM code_examples WHERE id = ?").get(translation.codeExampleId);
260
372
  try {
261
- const { canParse, parse } = await import('@lokascript/semantic');
262
- if (canParse(translation.hyperscript, translation.language)) {
263
- parse(translation.hyperscript, translation.language);
264
- parseSuccess = true;
265
- } else {
266
- errorMessage = "canParse returned false";
267
- }
373
+ const { verifyParses: verifyParses2 } = await Promise.resolve().then(() => (init_verify_parses(), verify_parses_exports));
374
+ parseSuccess = verifyParses2(
375
+ translation.hyperscript,
376
+ translation.language,
377
+ example?.translatable !== 0
378
+ );
379
+ if (!parseSuccess) errorMessage = "the semantic parser rejected it (or it has no hyperscript)";
268
380
  } catch (e) {
269
381
  errorMessage = e.message;
270
382
  }
271
- const db = getDatabase(options);
272
- db.prepare(
383
+ db.transaction(() => {
384
+ db.prepare(
385
+ `
386
+ UPDATE pattern_translations
387
+ SET verified_parses = ?, updated_at = datetime('now')
388
+ WHERE id = ?
273
389
  `
274
- UPDATE pattern_translations
275
- SET verified_parses = ?, updated_at = datetime('now')
276
- WHERE id = ?
277
- `
278
- ).run(parseSuccess ? 1 : 0, translation.id);
279
- db.prepare(
390
+ ).run(parseSuccess ? 1 : 0, translation.id);
391
+ db.prepare(
392
+ `
393
+ INSERT INTO pattern_tests
394
+ (code_example_id, language, test_type, success, error_message, test_date)
395
+ VALUES (?, ?, 'parse', ?, ?, datetime('now'))
280
396
  `
281
- INSERT INTO pattern_tests
282
- (code_example_id, language, test_date, parse_success, error_message)
283
- VALUES (?, ?, datetime('now'), ?, ?)
284
- `
285
- ).run(translation.codeExampleId, translation.language, parseSuccess ? 1 : 0, errorMessage);
397
+ ).run(translation.codeExampleId, translation.language, parseSuccess ? 1 : 0, errorMessage);
398
+ })();
286
399
  return {
287
400
  translation,
288
401
  parseSuccess,
@@ -352,74 +465,70 @@ function getWordOrder(language) {
352
465
  }
353
466
 
354
467
  // src/api/llm.ts
468
+ init_cjs_shims();
469
+ var EXAMPLES_FROM = `
470
+ SELECT le.*, ce.engine AS engine
471
+ FROM llm_examples le
472
+ JOIN code_examples ce ON ce.id = le.code_example_id`;
355
473
  async function getLLMExamples(prompt, language = "en", limit = 5, options) {
356
474
  const db = getDatabase({ ...options, readonly: true });
475
+ const runs = exampleCondition("ce.engine", options?.engine);
357
476
  const keywords = extractKeywords(prompt);
358
477
  if (keywords.length === 0) {
359
478
  const rows2 = db.prepare(
360
- `
361
- SELECT * FROM llm_examples
362
- WHERE language = ?
363
- ORDER BY quality_score DESC, usage_count DESC
479
+ `${EXAMPLES_FROM}
480
+ WHERE le.language = ? AND ${runs.sql}
481
+ ORDER BY le.quality_score DESC, le.usage_count DESC
364
482
  LIMIT ?
365
483
  `
366
- ).all(language, limit);
367
- trackUsage(
368
- db,
369
- rows2.map((r) => r.id)
370
- );
484
+ ).all(language, ...runs.params, limit);
371
485
  return rows2.map(mapRowToLLMExample);
372
486
  }
373
- const likeClauses = keywords.map(() => "(prompt LIKE ? OR completion LIKE ?)").join(" OR ");
487
+ const likeClauses = keywords.map(() => "(le.prompt LIKE ? OR le.completion LIKE ?)").join(" OR ");
374
488
  const params = keywords.flatMap((k) => [`%${k}%`, `%${k}%`]);
375
489
  const rows = db.prepare(
376
- `
377
- SELECT * FROM llm_examples
378
- WHERE language = ? AND (${likeClauses})
379
- ORDER BY quality_score DESC
490
+ `${EXAMPLES_FROM}
491
+ WHERE le.language = ? AND ${runs.sql} AND (${likeClauses})
492
+ ORDER BY le.quality_score DESC
380
493
  LIMIT ?
381
494
  `
382
- ).all(language, ...params, limit);
383
- trackUsage(
384
- db,
385
- rows.map((r) => r.id)
386
- );
495
+ ).all(language, ...runs.params, ...params, limit);
387
496
  return rows.map(mapRowToLLMExample);
388
497
  }
389
498
  async function getExamplesByCommand(command, language = "en", limit = 5, options) {
390
499
  const db = getDatabase({ ...options, readonly: true });
500
+ const runs = exampleCondition("ce.engine", options?.engine);
391
501
  const rows = db.prepare(
392
- `
393
- SELECT * FROM llm_examples
394
- WHERE language = ? AND completion LIKE ?
395
- ORDER BY quality_score DESC
502
+ `${EXAMPLES_FROM}
503
+ WHERE le.language = ? AND ${runs.sql} AND le.completion LIKE ?
504
+ ORDER BY le.quality_score DESC
396
505
  LIMIT ?
397
506
  `
398
- ).all(language, `%${command}%`, limit);
507
+ ).all(language, ...runs.params, `%${command}%`, limit);
399
508
  return rows.map(mapRowToLLMExample);
400
509
  }
401
510
  async function getHighQualityExamples(language = "en", minQuality = 0.8, limit = 10, options) {
402
511
  const db = getDatabase({ ...options, readonly: true });
512
+ const runs = exampleCondition("ce.engine", options?.engine);
403
513
  const rows = db.prepare(
404
- `
405
- SELECT * FROM llm_examples
406
- WHERE language = ? AND quality_score >= ?
407
- ORDER BY quality_score DESC, usage_count DESC
514
+ `${EXAMPLES_FROM}
515
+ WHERE le.language = ? AND ${runs.sql} AND le.quality_score >= ?
516
+ ORDER BY le.quality_score DESC, le.usage_count DESC
408
517
  LIMIT ?
409
518
  `
410
- ).all(language, minQuality, limit);
519
+ ).all(language, ...runs.params, minQuality, limit);
411
520
  return rows.map(mapRowToLLMExample);
412
521
  }
413
522
  async function getMostUsedExamples(language = "en", limit = 10, options) {
414
523
  const db = getDatabase({ ...options, readonly: true });
524
+ const runs = exampleCondition("ce.engine", options?.engine);
415
525
  const rows = db.prepare(
416
- `
417
- SELECT * FROM llm_examples
418
- WHERE language = ?
419
- ORDER BY usage_count DESC, quality_score DESC
526
+ `${EXAMPLES_FROM}
527
+ WHERE le.language = ? AND ${runs.sql}
528
+ ORDER BY le.usage_count DESC, le.quality_score DESC
420
529
  LIMIT ?
421
530
  `
422
- ).all(language, limit);
531
+ ).all(language, ...runs.params, limit);
423
532
  return rows.map(mapRowToLLMExample);
424
533
  }
425
534
  async function buildFewShotContext(prompt, language = "en", numExamples = 3, options) {
@@ -517,18 +626,6 @@ function extractKeywords(prompt) {
517
626
  ]);
518
627
  return prompt.toLowerCase().split(/\W+/).filter((word) => word.length > 2 && !stopWords.has(word));
519
628
  }
520
- function trackUsage(db, ids) {
521
- if (ids.length === 0) return;
522
- try {
523
- const stmt = db.prepare(`
524
- UPDATE llm_examples SET usage_count = usage_count + 1 WHERE id = ?
525
- `);
526
- for (const id of ids) {
527
- stmt.run(id);
528
- }
529
- } catch {
530
- }
531
- }
532
629
  function mapRowToLLMExample(row) {
533
630
  return {
534
631
  id: row.id,
@@ -538,11 +635,13 @@ function mapRowToLLMExample(row) {
538
635
  completion: row.completion,
539
636
  qualityScore: row.quality_score,
540
637
  usageCount: row.usage_count,
541
- createdAt: new Date(row.created_at)
638
+ createdAt: new Date(row.created_at),
639
+ engine: row.engine ?? null
542
640
  };
543
641
  }
544
642
 
545
643
  // src/api/roles.ts
644
+ init_cjs_shims();
546
645
  async function getPatternRoles(patternId, options) {
547
646
  const db = getDatabase({ ...options, readonly: true });
548
647
  const rows = db.prepare(
@@ -559,7 +658,7 @@ async function getPatternsByRole(role, options) {
559
658
  const db = getDatabase({ ...options, readonly: true });
560
659
  const rows = db.prepare(
561
660
  `
562
- SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
661
+ SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
563
662
  FROM code_examples ce
564
663
  INNER JOIN pattern_roles pr ON ce.id = pr.code_example_id
565
664
  WHERE pr.role = ?
@@ -575,7 +674,7 @@ async function getPatternsByRoles(roles, matchMode = "any", options) {
575
674
  const placeholders = roles.map(() => "?").join(", ");
576
675
  const rows = db.prepare(
577
676
  `
578
- SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
677
+ SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
579
678
  FROM code_examples ce
580
679
  INNER JOIN pattern_roles pr ON ce.id = pr.code_example_id
581
680
  WHERE pr.role IN (${placeholders})
@@ -587,7 +686,7 @@ async function getPatternsByRoles(roles, matchMode = "any", options) {
587
686
  const placeholders = roles.map(() => "?").join(", ");
588
687
  const rows = db.prepare(
589
688
  `
590
- SELECT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
689
+ SELECT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
591
690
  FROM code_examples ce
592
691
  WHERE (
593
692
  SELECT COUNT(DISTINCT pr.role)
@@ -604,7 +703,7 @@ async function getPatternsByRoleValue(role, value, options) {
604
703
  const db = getDatabase({ ...options, readonly: true });
605
704
  const rows = db.prepare(
606
705
  `
607
- SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.created_at
706
+ SELECT DISTINCT ce.id, ce.title, ce.raw_code, ce.description, ce.feature, ce.engine, ce.translatable, ce.created_at
608
707
  FROM code_examples ce
609
708
  INNER JOIN pattern_roles pr ON ce.id = pr.code_example_id
610
709
  WHERE pr.role = ? AND pr.role_value LIKE ?
@@ -737,6 +836,7 @@ function mapRowToPattern2(row) {
737
836
  tags: extractTags2(row.raw_code),
738
837
  difficulty: inferDifficulty2(row.raw_code),
739
838
  engine: row.engine || null,
839
+ translatable: row.translatable !== 0,
740
840
  createdAt: new Date(row.created_at)
741
841
  };
742
842
  }