@hyperfixi/patterns-reference 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +311 -0
- package/data/hyperfixi-extensions.json +106 -0
- package/dist/api/index.d.mts +59 -0
- package/dist/api/index.d.ts +59 -0
- package/dist/api/index.js +769 -0
- package/dist/api/index.mjs +731 -0
- package/dist/index-CBoczxz7.d.mts +296 -0
- package/dist/index-CBoczxz7.d.ts +296 -0
- package/dist/index.d.mts +403 -0
- package/dist/index.d.ts +403 -0
- package/dist/index.js +1312 -0
- package/dist/index.mjs +1240 -0
- package/dist/llm-DMR6tmkM.d.ts +123 -0
- package/dist/llm-UOOBXwj3.d.mts +123 -0
- package/dist/sync/index.d.mts +42 -0
- package/dist/sync/index.d.ts +42 -0
- package/dist/sync/index.js +28 -0
- package/dist/sync/index.mjs +23 -0
- package/package.json +92 -0
- package/src/adapters/index.ts +7 -0
- package/src/adapters/llm-adapter.ts +394 -0
- package/src/api/index.ts +52 -0
- package/src/api/language-docs.ts +529 -0
- package/src/api/llm.ts +357 -0
- package/src/api/patterns.ts +276 -0
- package/src/api/roles.ts +401 -0
- package/src/api/translations.ts +283 -0
- package/src/database/connection.ts +85 -0
- package/src/index.ts +266 -0
- package/src/registry/index.ts +7 -0
- package/src/registry/patterns-provider.ts +250 -0
- package/src/semantic-bridge.ts +204 -0
- package/src/sync/index.ts +70 -0
- package/src/types/better-sqlite3.d.ts +44 -0
- package/src/types/index.ts +434 -0
package/src/api/llm.ts
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Examples API
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to query and manage LLM examples for few-shot learning.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getDatabase } from '../database/connection';
|
|
8
|
+
import type { LLMExample, ConnectionOptions } from '../types';
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Database Row Types
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
interface LLMExampleRow {
|
|
15
|
+
id: number;
|
|
16
|
+
code_example_id: string;
|
|
17
|
+
language: string;
|
|
18
|
+
prompt: string;
|
|
19
|
+
completion: string;
|
|
20
|
+
quality_score: number;
|
|
21
|
+
usage_count: number;
|
|
22
|
+
created_at: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Query Functions
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get LLM examples relevant to a prompt.
|
|
31
|
+
* Uses text similarity to find relevant patterns.
|
|
32
|
+
*/
|
|
33
|
+
export async function getLLMExamples(
|
|
34
|
+
prompt: string,
|
|
35
|
+
language: string = 'en',
|
|
36
|
+
limit: number = 5,
|
|
37
|
+
options?: ConnectionOptions
|
|
38
|
+
): Promise<LLMExample[]> {
|
|
39
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
40
|
+
|
|
41
|
+
// Extract keywords from prompt
|
|
42
|
+
const keywords = extractKeywords(prompt);
|
|
43
|
+
|
|
44
|
+
if (keywords.length === 0) {
|
|
45
|
+
// Return top-quality examples as fallback
|
|
46
|
+
const rows = db
|
|
47
|
+
.prepare(
|
|
48
|
+
`
|
|
49
|
+
SELECT * FROM llm_examples
|
|
50
|
+
WHERE language = ?
|
|
51
|
+
ORDER BY quality_score DESC, usage_count DESC
|
|
52
|
+
LIMIT ?
|
|
53
|
+
`
|
|
54
|
+
)
|
|
55
|
+
.all(language, limit) as LLMExampleRow[];
|
|
56
|
+
|
|
57
|
+
// Track usage
|
|
58
|
+
trackUsage(
|
|
59
|
+
db,
|
|
60
|
+
rows.map(r => r.id)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return rows.map(mapRowToLLMExample);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Build LIKE clauses for keyword matching
|
|
67
|
+
const likeClauses = keywords.map(() => '(prompt LIKE ? OR completion LIKE ?)').join(' OR ');
|
|
68
|
+
const params = keywords.flatMap(k => [`%${k}%`, `%${k}%`]);
|
|
69
|
+
|
|
70
|
+
const rows = db
|
|
71
|
+
.prepare(
|
|
72
|
+
`
|
|
73
|
+
SELECT * FROM llm_examples
|
|
74
|
+
WHERE language = ? AND (${likeClauses})
|
|
75
|
+
ORDER BY quality_score DESC
|
|
76
|
+
LIMIT ?
|
|
77
|
+
`
|
|
78
|
+
)
|
|
79
|
+
.all(language, ...params, limit) as LLMExampleRow[];
|
|
80
|
+
|
|
81
|
+
// Track usage
|
|
82
|
+
trackUsage(
|
|
83
|
+
db,
|
|
84
|
+
rows.map(r => r.id)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return rows.map(mapRowToLLMExample);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get examples by command type.
|
|
92
|
+
*/
|
|
93
|
+
export async function getExamplesByCommand(
|
|
94
|
+
command: string,
|
|
95
|
+
language: string = 'en',
|
|
96
|
+
limit: number = 5,
|
|
97
|
+
options?: ConnectionOptions
|
|
98
|
+
): Promise<LLMExample[]> {
|
|
99
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
100
|
+
|
|
101
|
+
const rows = db
|
|
102
|
+
.prepare(
|
|
103
|
+
`
|
|
104
|
+
SELECT * FROM llm_examples
|
|
105
|
+
WHERE language = ? AND completion LIKE ?
|
|
106
|
+
ORDER BY quality_score DESC
|
|
107
|
+
LIMIT ?
|
|
108
|
+
`
|
|
109
|
+
)
|
|
110
|
+
.all(language, `%${command}%`, limit) as LLMExampleRow[];
|
|
111
|
+
|
|
112
|
+
return rows.map(mapRowToLLMExample);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get high-quality examples (for few-shot prompts).
|
|
117
|
+
*/
|
|
118
|
+
export async function getHighQualityExamples(
|
|
119
|
+
language: string = 'en',
|
|
120
|
+
minQuality: number = 0.8,
|
|
121
|
+
limit: number = 10,
|
|
122
|
+
options?: ConnectionOptions
|
|
123
|
+
): Promise<LLMExample[]> {
|
|
124
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
125
|
+
|
|
126
|
+
const rows = db
|
|
127
|
+
.prepare(
|
|
128
|
+
`
|
|
129
|
+
SELECT * FROM llm_examples
|
|
130
|
+
WHERE language = ? AND quality_score >= ?
|
|
131
|
+
ORDER BY quality_score DESC, usage_count DESC
|
|
132
|
+
LIMIT ?
|
|
133
|
+
`
|
|
134
|
+
)
|
|
135
|
+
.all(language, minQuality, limit) as LLMExampleRow[];
|
|
136
|
+
|
|
137
|
+
return rows.map(mapRowToLLMExample);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get most used examples (popular).
|
|
142
|
+
*/
|
|
143
|
+
export async function getMostUsedExamples(
|
|
144
|
+
language: string = 'en',
|
|
145
|
+
limit: number = 10,
|
|
146
|
+
options?: ConnectionOptions
|
|
147
|
+
): Promise<LLMExample[]> {
|
|
148
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
149
|
+
|
|
150
|
+
const rows = db
|
|
151
|
+
.prepare(
|
|
152
|
+
`
|
|
153
|
+
SELECT * FROM llm_examples
|
|
154
|
+
WHERE language = ?
|
|
155
|
+
ORDER BY usage_count DESC, quality_score DESC
|
|
156
|
+
LIMIT ?
|
|
157
|
+
`
|
|
158
|
+
)
|
|
159
|
+
.all(language, limit) as LLMExampleRow[];
|
|
160
|
+
|
|
161
|
+
return rows.map(mapRowToLLMExample);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Build few-shot context for LLM prompting.
|
|
166
|
+
*/
|
|
167
|
+
export async function buildFewShotContext(
|
|
168
|
+
prompt: string,
|
|
169
|
+
language: string = 'en',
|
|
170
|
+
numExamples: number = 3,
|
|
171
|
+
options?: ConnectionOptions
|
|
172
|
+
): Promise<string> {
|
|
173
|
+
const examples = await getLLMExamples(prompt, language, numExamples, options);
|
|
174
|
+
|
|
175
|
+
if (examples.length === 0) {
|
|
176
|
+
return '';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let context = 'Here are some example hyperscript patterns:\n\n';
|
|
180
|
+
|
|
181
|
+
for (const ex of examples) {
|
|
182
|
+
context += `Task: ${ex.prompt}\n`;
|
|
183
|
+
context += `Code: ${ex.completion}\n\n`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
context += `Now generate hyperscript for: ${prompt}\n`;
|
|
187
|
+
|
|
188
|
+
return context;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Add a new LLM example.
|
|
193
|
+
*/
|
|
194
|
+
export async function addLLMExample(
|
|
195
|
+
example: Omit<LLMExample, 'id' | 'usageCount' | 'createdAt'>,
|
|
196
|
+
options?: ConnectionOptions
|
|
197
|
+
): Promise<number> {
|
|
198
|
+
const db = getDatabase(options);
|
|
199
|
+
|
|
200
|
+
const result = db
|
|
201
|
+
.prepare(
|
|
202
|
+
`
|
|
203
|
+
INSERT INTO llm_examples
|
|
204
|
+
(code_example_id, language, prompt, completion, quality_score, usage_count, created_at)
|
|
205
|
+
VALUES (?, ?, ?, ?, ?, 0, datetime('now'))
|
|
206
|
+
`
|
|
207
|
+
)
|
|
208
|
+
.run(
|
|
209
|
+
example.patternId,
|
|
210
|
+
example.language,
|
|
211
|
+
example.prompt,
|
|
212
|
+
example.completion,
|
|
213
|
+
example.qualityScore
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
return result.lastInsertRowid as number;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Update example quality score.
|
|
221
|
+
*/
|
|
222
|
+
export async function updateQualityScore(
|
|
223
|
+
id: number,
|
|
224
|
+
qualityScore: number,
|
|
225
|
+
options?: ConnectionOptions
|
|
226
|
+
): Promise<void> {
|
|
227
|
+
const db = getDatabase(options);
|
|
228
|
+
|
|
229
|
+
db.prepare(
|
|
230
|
+
`
|
|
231
|
+
UPDATE llm_examples SET quality_score = ? WHERE id = ?
|
|
232
|
+
`
|
|
233
|
+
).run(qualityScore, id);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get LLM example statistics.
|
|
238
|
+
*/
|
|
239
|
+
export async function getLLMStats(options?: ConnectionOptions): Promise<{
|
|
240
|
+
total: number;
|
|
241
|
+
byLanguage: Record<string, number>;
|
|
242
|
+
avgQuality: number;
|
|
243
|
+
totalUsage: number;
|
|
244
|
+
}> {
|
|
245
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
246
|
+
|
|
247
|
+
const totalResult = db.prepare('SELECT COUNT(*) as count FROM llm_examples').get() as {
|
|
248
|
+
count: number;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const byLangResult = db
|
|
252
|
+
.prepare(
|
|
253
|
+
`
|
|
254
|
+
SELECT language, COUNT(*) as count
|
|
255
|
+
FROM llm_examples
|
|
256
|
+
GROUP BY language
|
|
257
|
+
`
|
|
258
|
+
)
|
|
259
|
+
.all() as { language: string; count: number }[];
|
|
260
|
+
|
|
261
|
+
const avgResult = db.prepare('SELECT AVG(quality_score) as avg FROM llm_examples').get() as {
|
|
262
|
+
avg: number;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const usageResult = db.prepare('SELECT SUM(usage_count) as total FROM llm_examples').get() as {
|
|
266
|
+
total: number;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const byLanguage: Record<string, number> = {};
|
|
270
|
+
for (const { language, count } of byLangResult) {
|
|
271
|
+
byLanguage[language] = count;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
total: totalResult.count,
|
|
276
|
+
byLanguage,
|
|
277
|
+
avgQuality: avgResult.avg || 0,
|
|
278
|
+
totalUsage: usageResult.total || 0,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// =============================================================================
|
|
283
|
+
// Helper Functions
|
|
284
|
+
// =============================================================================
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Extract keywords from a prompt for matching.
|
|
288
|
+
*/
|
|
289
|
+
function extractKeywords(prompt: string): string[] {
|
|
290
|
+
const stopWords = new Set([
|
|
291
|
+
'a',
|
|
292
|
+
'an',
|
|
293
|
+
'the',
|
|
294
|
+
'to',
|
|
295
|
+
'on',
|
|
296
|
+
'in',
|
|
297
|
+
'for',
|
|
298
|
+
'is',
|
|
299
|
+
'it',
|
|
300
|
+
'when',
|
|
301
|
+
'i',
|
|
302
|
+
'want',
|
|
303
|
+
'need',
|
|
304
|
+
'create',
|
|
305
|
+
'make',
|
|
306
|
+
'please',
|
|
307
|
+
'can',
|
|
308
|
+
'you',
|
|
309
|
+
'would',
|
|
310
|
+
'should',
|
|
311
|
+
'like',
|
|
312
|
+
'that',
|
|
313
|
+
'this',
|
|
314
|
+
'with',
|
|
315
|
+
'from',
|
|
316
|
+
]);
|
|
317
|
+
|
|
318
|
+
return prompt
|
|
319
|
+
.toLowerCase()
|
|
320
|
+
.split(/\W+/)
|
|
321
|
+
.filter(word => word.length > 2 && !stopWords.has(word));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Track usage of examples.
|
|
326
|
+
*/
|
|
327
|
+
function trackUsage(db: any, ids: number[]): void {
|
|
328
|
+
if (ids.length === 0) return;
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
const stmt = db.prepare(`
|
|
332
|
+
UPDATE llm_examples SET usage_count = usage_count + 1 WHERE id = ?
|
|
333
|
+
`);
|
|
334
|
+
|
|
335
|
+
for (const id of ids) {
|
|
336
|
+
stmt.run(id);
|
|
337
|
+
}
|
|
338
|
+
} catch {
|
|
339
|
+
// Silently ignore tracking errors
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Map database row to LLMExample type.
|
|
345
|
+
*/
|
|
346
|
+
function mapRowToLLMExample(row: LLMExampleRow): LLMExample {
|
|
347
|
+
return {
|
|
348
|
+
id: row.id,
|
|
349
|
+
patternId: row.code_example_id,
|
|
350
|
+
language: row.language,
|
|
351
|
+
prompt: row.prompt,
|
|
352
|
+
completion: row.completion,
|
|
353
|
+
qualityScore: row.quality_score,
|
|
354
|
+
usageCount: row.usage_count,
|
|
355
|
+
createdAt: new Date(row.created_at),
|
|
356
|
+
};
|
|
357
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Queries API
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to query patterns from the database.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getDatabase } from '../database/connection';
|
|
8
|
+
import type {
|
|
9
|
+
Pattern,
|
|
10
|
+
SearchOptions,
|
|
11
|
+
PatternStats,
|
|
12
|
+
ConnectionOptions,
|
|
13
|
+
EngineCompat,
|
|
14
|
+
} from '../types';
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Database Row Types
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
interface CodeExampleRow {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
raw_code: string;
|
|
24
|
+
description: string | null;
|
|
25
|
+
feature: string | null;
|
|
26
|
+
engine: string | null;
|
|
27
|
+
source_url: string | null;
|
|
28
|
+
created_at: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Query Functions
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get a pattern by ID.
|
|
37
|
+
*/
|
|
38
|
+
export async function getPatternById(
|
|
39
|
+
id: string,
|
|
40
|
+
options?: ConnectionOptions
|
|
41
|
+
): Promise<Pattern | null> {
|
|
42
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
43
|
+
const row = db
|
|
44
|
+
.prepare(
|
|
45
|
+
`
|
|
46
|
+
SELECT id, title, raw_code, description, feature, engine, created_at
|
|
47
|
+
FROM code_examples
|
|
48
|
+
WHERE id = ?
|
|
49
|
+
`
|
|
50
|
+
)
|
|
51
|
+
.get(id) as CodeExampleRow | undefined;
|
|
52
|
+
|
|
53
|
+
return row ? mapRowToPattern(row) : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get patterns by category (feature in hyperscript-lsp schema).
|
|
58
|
+
*/
|
|
59
|
+
export async function getPatternsByCategory(
|
|
60
|
+
category: string,
|
|
61
|
+
options?: ConnectionOptions
|
|
62
|
+
): Promise<Pattern[]> {
|
|
63
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
64
|
+
const rows = db
|
|
65
|
+
.prepare(
|
|
66
|
+
`
|
|
67
|
+
SELECT id, title, raw_code, description, feature, engine, created_at
|
|
68
|
+
FROM code_examples
|
|
69
|
+
WHERE feature = ?
|
|
70
|
+
ORDER BY title
|
|
71
|
+
`
|
|
72
|
+
)
|
|
73
|
+
.all(category) as CodeExampleRow[];
|
|
74
|
+
|
|
75
|
+
return rows.map(mapRowToPattern);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get patterns by command (extracted from raw_code).
|
|
80
|
+
*/
|
|
81
|
+
export async function getPatternsByCommand(
|
|
82
|
+
command: string,
|
|
83
|
+
options?: ConnectionOptions
|
|
84
|
+
): Promise<Pattern[]> {
|
|
85
|
+
const db = getDatabase({ ...options, readonly: true });
|
|
86
|
+
// Use word boundary regex in SQLite
|
|
87
|
+
const rows = db
|
|
88
|
+
.prepare(
|
|
89
|
+
`
|
|
90
|
+
SELECT id, title, raw_code, description, feature, engine, created_at
|
|
91
|
+
FROM code_examples
|
|
92
|
+
WHERE raw_code LIKE ?
|
|
93
|
+
ORDER BY title
|
|
94
|
+
`
|
|
95
|
+
)
|
|
96
|
+
.all(`%${command}%`) as CodeExampleRow[];
|
|
97
|
+
|
|
98
|
+
// Filter more precisely in JS (SQLite LIKE is case-insensitive and broad)
|
|
99
|
+
return rows
|
|
100
|
+
.filter(row => new RegExp(`\\b${command}\\b`, 'i').test(row.raw_code))
|
|
101
|
+
.map(mapRowToPattern);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Search patterns by text query.
|
|
106
|
+
*/
|
|
107
|
+
export async function searchPatterns(
|
|
108
|
+
query: string,
|
|
109
|
+
searchOptions: SearchOptions = {},
|
|
110
|
+
connOptions?: ConnectionOptions
|
|
111
|
+
): Promise<Pattern[]> {
|
|
112
|
+
const db = getDatabase({ ...connOptions, readonly: true });
|
|
113
|
+
const { limit = 50, offset = 0 } = searchOptions;
|
|
114
|
+
|
|
115
|
+
const rows = db
|
|
116
|
+
.prepare(
|
|
117
|
+
`
|
|
118
|
+
SELECT id, title, raw_code, description, feature, engine, created_at
|
|
119
|
+
FROM code_examples
|
|
120
|
+
WHERE title LIKE ? OR raw_code LIKE ? OR description LIKE ?
|
|
121
|
+
ORDER BY title
|
|
122
|
+
LIMIT ? OFFSET ?
|
|
123
|
+
`
|
|
124
|
+
)
|
|
125
|
+
.all(`%${query}%`, `%${query}%`, `%${query}%`, limit, offset) as CodeExampleRow[];
|
|
126
|
+
|
|
127
|
+
return rows.map(mapRowToPattern);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get all patterns.
|
|
132
|
+
*/
|
|
133
|
+
export async function getAllPatterns(
|
|
134
|
+
searchOptions: SearchOptions = {},
|
|
135
|
+
connOptions?: ConnectionOptions
|
|
136
|
+
): Promise<Pattern[]> {
|
|
137
|
+
const db = getDatabase({ ...connOptions, readonly: true });
|
|
138
|
+
const { limit = 1000, offset = 0 } = searchOptions;
|
|
139
|
+
|
|
140
|
+
const rows = db
|
|
141
|
+
.prepare(
|
|
142
|
+
`
|
|
143
|
+
SELECT id, title, raw_code, description, feature, engine, created_at
|
|
144
|
+
FROM code_examples
|
|
145
|
+
ORDER BY title
|
|
146
|
+
LIMIT ? OFFSET ?
|
|
147
|
+
`
|
|
148
|
+
)
|
|
149
|
+
.all(limit, offset) as CodeExampleRow[];
|
|
150
|
+
|
|
151
|
+
return rows.map(mapRowToPattern);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get pattern statistics.
|
|
156
|
+
*/
|
|
157
|
+
export async function getPatternStats(connOptions?: ConnectionOptions): Promise<PatternStats> {
|
|
158
|
+
const db = getDatabase({ ...connOptions, readonly: true });
|
|
159
|
+
|
|
160
|
+
// Total patterns
|
|
161
|
+
const patternCount = db.prepare('SELECT COUNT(*) as count FROM code_examples').get() as {
|
|
162
|
+
count: number;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Total translations
|
|
166
|
+
const translationCount = db
|
|
167
|
+
.prepare('SELECT COUNT(*) as count FROM pattern_translations')
|
|
168
|
+
.get() as {
|
|
169
|
+
count: number;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// By language
|
|
173
|
+
const byLangRows = db
|
|
174
|
+
.prepare(
|
|
175
|
+
`
|
|
176
|
+
SELECT
|
|
177
|
+
language,
|
|
178
|
+
COUNT(*) as count,
|
|
179
|
+
SUM(verified_parses) as verified_count
|
|
180
|
+
FROM pattern_translations
|
|
181
|
+
GROUP BY language
|
|
182
|
+
`
|
|
183
|
+
)
|
|
184
|
+
.all() as { language: string; count: number; verified_count: number }[];
|
|
185
|
+
|
|
186
|
+
const byLanguage: Record<string, { count: number; verifiedCount: number }> = {};
|
|
187
|
+
for (const row of byLangRows) {
|
|
188
|
+
byLanguage[row.language] = {
|
|
189
|
+
count: row.count,
|
|
190
|
+
verifiedCount: row.verified_count,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// By category
|
|
195
|
+
const byCatRows = db
|
|
196
|
+
.prepare(
|
|
197
|
+
`
|
|
198
|
+
SELECT feature, COUNT(*) as count
|
|
199
|
+
FROM code_examples
|
|
200
|
+
WHERE feature IS NOT NULL
|
|
201
|
+
GROUP BY feature
|
|
202
|
+
`
|
|
203
|
+
)
|
|
204
|
+
.all() as { feature: string; count: number }[];
|
|
205
|
+
|
|
206
|
+
const byCategory: Record<string, number> = {};
|
|
207
|
+
for (const row of byCatRows) {
|
|
208
|
+
byCategory[row.feature] = row.count;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Average confidence
|
|
212
|
+
const avgConfResult = db
|
|
213
|
+
.prepare('SELECT AVG(confidence) as avg FROM pattern_translations WHERE confidence > 0')
|
|
214
|
+
.get() as { avg: number };
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
totalPatterns: patternCount.count,
|
|
218
|
+
totalTranslations: translationCount.count,
|
|
219
|
+
byLanguage,
|
|
220
|
+
byCategory,
|
|
221
|
+
avgConfidence: avgConfResult.avg || 0,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// =============================================================================
|
|
226
|
+
// Helper Functions
|
|
227
|
+
// =============================================================================
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Map database row to Pattern type.
|
|
231
|
+
*/
|
|
232
|
+
function mapRowToPattern(row: CodeExampleRow): Pattern {
|
|
233
|
+
return {
|
|
234
|
+
id: row.id,
|
|
235
|
+
title: row.title,
|
|
236
|
+
description: row.description,
|
|
237
|
+
rawCode: row.raw_code,
|
|
238
|
+
category: row.feature,
|
|
239
|
+
primaryCommand: extractPrimaryCommand(row.raw_code),
|
|
240
|
+
tags: extractTags(row.raw_code),
|
|
241
|
+
difficulty: inferDifficulty(row.raw_code),
|
|
242
|
+
engine: (row.engine as EngineCompat) || null,
|
|
243
|
+
createdAt: new Date(row.created_at),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Extract primary command from hyperscript code.
|
|
249
|
+
*/
|
|
250
|
+
function extractPrimaryCommand(code: string): string | null {
|
|
251
|
+
const match = code.match(/^(on|toggle|put|set|add|remove|show|hide|wait|log|send|fetch|call)\b/i);
|
|
252
|
+
return match ? match[1].toLowerCase() : null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Extract tags from code.
|
|
257
|
+
*/
|
|
258
|
+
function extractTags(code: string): string[] {
|
|
259
|
+
const tags: string[] = [];
|
|
260
|
+
if (code.includes('.')) tags.push('class');
|
|
261
|
+
if (code.includes('#')) tags.push('id');
|
|
262
|
+
if (code.includes('on ')) tags.push('event');
|
|
263
|
+
if (code.includes('fetch')) tags.push('async');
|
|
264
|
+
if (code.includes('wait')) tags.push('timing');
|
|
265
|
+
return tags;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Infer difficulty from code complexity.
|
|
270
|
+
*/
|
|
271
|
+
function inferDifficulty(code: string): 'beginner' | 'intermediate' | 'advanced' {
|
|
272
|
+
const lines = code.split('\n').filter(l => l.trim()).length;
|
|
273
|
+
if (lines === 1 && !code.includes('then')) return 'beginner';
|
|
274
|
+
if (lines <= 3) return 'intermediate';
|
|
275
|
+
return 'advanced';
|
|
276
|
+
}
|