@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.
@@ -0,0 +1,529 @@
1
+ /**
2
+ * Language Documentation API
3
+ *
4
+ * Provides functions to query hyperscript language documentation:
5
+ * commands, expressions, keywords, features, and special symbols.
6
+ */
7
+
8
+ import { getDatabase } from '../database/connection';
9
+ import type {
10
+ Command,
11
+ Expression,
12
+ ExpressionOperator,
13
+ Keyword,
14
+ Feature,
15
+ SpecialSymbol,
16
+ LanguageElement,
17
+ LanguageElementType,
18
+ LanguageDocsStats,
19
+ ConnectionOptions,
20
+ } from '../types';
21
+
22
+ // =============================================================================
23
+ // Database Row Types
24
+ // =============================================================================
25
+
26
+ interface CommandRow {
27
+ id: string;
28
+ name: string;
29
+ description: string | null;
30
+ syntax: string | null;
31
+ purpose: string | null;
32
+ implicit_target: string | null;
33
+ implicit_result_target: string | null;
34
+ is_blocking: number;
35
+ has_body: number;
36
+ created_at: string;
37
+ updated_at: string;
38
+ }
39
+
40
+ interface ExpressionRow {
41
+ id: string;
42
+ name: string;
43
+ description: string | null;
44
+ category: string;
45
+ evaluates_to_type: string | null;
46
+ precedence: number | null;
47
+ associativity: string | null;
48
+ created_at: string;
49
+ updated_at: string;
50
+ }
51
+
52
+ interface KeywordRow {
53
+ id: string;
54
+ name: string;
55
+ description: string | null;
56
+ context_of_use: string | null;
57
+ is_optional: number;
58
+ created_at: string;
59
+ updated_at: string;
60
+ }
61
+
62
+ interface FeatureRow {
63
+ id: string;
64
+ name: string;
65
+ description: string | null;
66
+ syntax: string | null;
67
+ trigger: string | null;
68
+ structure_description: string | null;
69
+ scope_impact: string | null;
70
+ created_at: string;
71
+ updated_at: string;
72
+ }
73
+
74
+ interface SpecialSymbolRow {
75
+ id: string;
76
+ name: string;
77
+ symbol: string;
78
+ symbol_type: string;
79
+ description: string | null;
80
+ typical_value: string | null;
81
+ scope_implications: string | null;
82
+ created_at: string;
83
+ updated_at: string;
84
+ }
85
+
86
+ // =============================================================================
87
+ // Row Mappers
88
+ // =============================================================================
89
+
90
+ function mapCommand(row: CommandRow): Command {
91
+ return {
92
+ id: row.id,
93
+ name: row.name,
94
+ description: row.description,
95
+ syntax: row.syntax,
96
+ purpose: row.purpose,
97
+ implicitTarget: row.implicit_target,
98
+ implicitResultTarget: row.implicit_result_target,
99
+ isBlocking: row.is_blocking === 1,
100
+ hasBody: row.has_body === 1,
101
+ createdAt: new Date(row.created_at),
102
+ updatedAt: new Date(row.updated_at),
103
+ };
104
+ }
105
+
106
+ function mapExpression(row: ExpressionRow, operators: string[] = []): Expression {
107
+ return {
108
+ id: row.id,
109
+ name: row.name,
110
+ description: row.description,
111
+ category: row.category,
112
+ evaluatesToType: row.evaluates_to_type,
113
+ precedence: row.precedence,
114
+ associativity: row.associativity,
115
+ operators,
116
+ createdAt: new Date(row.created_at),
117
+ updatedAt: new Date(row.updated_at),
118
+ };
119
+ }
120
+
121
+ function mapKeyword(row: KeywordRow): Keyword {
122
+ return {
123
+ id: row.id,
124
+ name: row.name,
125
+ description: row.description,
126
+ contextOfUse: row.context_of_use,
127
+ isOptional: row.is_optional === 1,
128
+ createdAt: new Date(row.created_at),
129
+ updatedAt: new Date(row.updated_at),
130
+ };
131
+ }
132
+
133
+ function mapFeature(row: FeatureRow): Feature {
134
+ return {
135
+ id: row.id,
136
+ name: row.name,
137
+ description: row.description,
138
+ syntax: row.syntax,
139
+ trigger: row.trigger,
140
+ structureDescription: row.structure_description,
141
+ scopeImpact: row.scope_impact,
142
+ createdAt: new Date(row.created_at),
143
+ updatedAt: new Date(row.updated_at),
144
+ };
145
+ }
146
+
147
+ function mapSpecialSymbol(row: SpecialSymbolRow): SpecialSymbol {
148
+ return {
149
+ id: row.id,
150
+ name: row.name,
151
+ symbol: row.symbol,
152
+ symbolType: row.symbol_type,
153
+ description: row.description,
154
+ typicalValue: row.typical_value,
155
+ scopeImplications: row.scope_implications,
156
+ createdAt: new Date(row.created_at),
157
+ updatedAt: new Date(row.updated_at),
158
+ };
159
+ }
160
+
161
+ // =============================================================================
162
+ // Command Functions
163
+ // =============================================================================
164
+
165
+ /**
166
+ * Get a command by name.
167
+ */
168
+ export async function getCommandByName(
169
+ name: string,
170
+ options?: ConnectionOptions
171
+ ): Promise<Command | null> {
172
+ const db = getDatabase({ ...options, readonly: true });
173
+
174
+ const row = db.prepare('SELECT * FROM commands WHERE LOWER(name) = LOWER(?)').get(name) as
175
+ | CommandRow
176
+ | undefined;
177
+
178
+ return row ? mapCommand(row) : null;
179
+ }
180
+
181
+ /**
182
+ * Get all commands.
183
+ */
184
+ export async function getAllCommands(options?: ConnectionOptions): Promise<Command[]> {
185
+ const db = getDatabase({ ...options, readonly: true });
186
+
187
+ const rows = db.prepare('SELECT * FROM commands ORDER BY name').all() as CommandRow[];
188
+
189
+ return rows.map(mapCommand);
190
+ }
191
+
192
+ /**
193
+ * Search commands by name or description.
194
+ */
195
+ export async function searchCommands(
196
+ query: string,
197
+ options?: ConnectionOptions
198
+ ): Promise<Command[]> {
199
+ const db = getDatabase({ ...options, readonly: true });
200
+
201
+ const pattern = `%${query}%`;
202
+ const rows = db
203
+ .prepare(
204
+ `SELECT * FROM commands
205
+ WHERE LOWER(name) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?)
206
+ ORDER BY name`
207
+ )
208
+ .all(pattern, pattern) as CommandRow[];
209
+
210
+ return rows.map(mapCommand);
211
+ }
212
+
213
+ // =============================================================================
214
+ // Expression Functions
215
+ // =============================================================================
216
+
217
+ /**
218
+ * Get an expression by name.
219
+ */
220
+ export async function getExpressionByName(
221
+ name: string,
222
+ options?: ConnectionOptions
223
+ ): Promise<Expression | null> {
224
+ const db = getDatabase({ ...options, readonly: true });
225
+
226
+ const row = db.prepare('SELECT * FROM expressions WHERE LOWER(name) = LOWER(?)').get(name) as
227
+ | ExpressionRow
228
+ | undefined;
229
+
230
+ if (!row) return null;
231
+
232
+ // Get operators for this expression
233
+ const operators = db
234
+ .prepare('SELECT operator FROM expression_operators WHERE expression_id = ?')
235
+ .all(row.id) as { operator: string }[];
236
+
237
+ return mapExpression(
238
+ row,
239
+ operators.map(o => o.operator)
240
+ );
241
+ }
242
+
243
+ /**
244
+ * Get all expressions.
245
+ */
246
+ export async function getAllExpressions(options?: ConnectionOptions): Promise<Expression[]> {
247
+ const db = getDatabase({ ...options, readonly: true });
248
+
249
+ const rows = db.prepare('SELECT * FROM expressions ORDER BY name').all() as ExpressionRow[];
250
+
251
+ // Get all operators in one query
252
+ const allOperators = db
253
+ .prepare('SELECT expression_id, operator FROM expression_operators')
254
+ .all() as { expression_id: string; operator: string }[];
255
+
256
+ // Group by expression_id
257
+ const operatorMap = new Map<string, string[]>();
258
+ for (const op of allOperators) {
259
+ const ops = operatorMap.get(op.expression_id) || [];
260
+ ops.push(op.operator);
261
+ operatorMap.set(op.expression_id, ops);
262
+ }
263
+
264
+ return rows.map(row => mapExpression(row, operatorMap.get(row.id) || []));
265
+ }
266
+
267
+ /**
268
+ * Get expressions by category.
269
+ */
270
+ export async function getExpressionsByCategory(
271
+ category: string,
272
+ options?: ConnectionOptions
273
+ ): Promise<Expression[]> {
274
+ const db = getDatabase({ ...options, readonly: true });
275
+
276
+ const rows = db
277
+ .prepare('SELECT * FROM expressions WHERE LOWER(category) = LOWER(?) ORDER BY name')
278
+ .all(category) as ExpressionRow[];
279
+
280
+ // Get operators
281
+ const ids = rows.map(r => r.id);
282
+ if (ids.length === 0) return [];
283
+
284
+ const placeholders = ids.map(() => '?').join(',');
285
+ const allOperators = db
286
+ .prepare(
287
+ `SELECT expression_id, operator FROM expression_operators WHERE expression_id IN (${placeholders})`
288
+ )
289
+ .all(...ids) as { expression_id: string; operator: string }[];
290
+
291
+ const operatorMap = new Map<string, string[]>();
292
+ for (const op of allOperators) {
293
+ const ops = operatorMap.get(op.expression_id) || [];
294
+ ops.push(op.operator);
295
+ operatorMap.set(op.expression_id, ops);
296
+ }
297
+
298
+ return rows.map(row => mapExpression(row, operatorMap.get(row.id) || []));
299
+ }
300
+
301
+ // =============================================================================
302
+ // Keyword Functions
303
+ // =============================================================================
304
+
305
+ /**
306
+ * Get a keyword by name.
307
+ */
308
+ export async function getKeywordByName(
309
+ name: string,
310
+ options?: ConnectionOptions
311
+ ): Promise<Keyword | null> {
312
+ const db = getDatabase({ ...options, readonly: true });
313
+
314
+ const row = db.prepare('SELECT * FROM keywords WHERE LOWER(name) = LOWER(?)').get(name) as
315
+ | KeywordRow
316
+ | undefined;
317
+
318
+ return row ? mapKeyword(row) : null;
319
+ }
320
+
321
+ /**
322
+ * Get all keywords.
323
+ */
324
+ export async function getAllKeywords(options?: ConnectionOptions): Promise<Keyword[]> {
325
+ const db = getDatabase({ ...options, readonly: true });
326
+
327
+ const rows = db.prepare('SELECT * FROM keywords ORDER BY name').all() as KeywordRow[];
328
+
329
+ return rows.map(mapKeyword);
330
+ }
331
+
332
+ // =============================================================================
333
+ // Feature Functions
334
+ // =============================================================================
335
+
336
+ /**
337
+ * Get a feature by name.
338
+ */
339
+ export async function getFeatureByName(
340
+ name: string,
341
+ options?: ConnectionOptions
342
+ ): Promise<Feature | null> {
343
+ const db = getDatabase({ ...options, readonly: true });
344
+
345
+ const row = db.prepare('SELECT * FROM features WHERE LOWER(name) = LOWER(?)').get(name) as
346
+ | FeatureRow
347
+ | undefined;
348
+
349
+ return row ? mapFeature(row) : null;
350
+ }
351
+
352
+ /**
353
+ * Get all features.
354
+ */
355
+ export async function getAllFeatures(options?: ConnectionOptions): Promise<Feature[]> {
356
+ const db = getDatabase({ ...options, readonly: true });
357
+
358
+ const rows = db.prepare('SELECT * FROM features ORDER BY name').all() as FeatureRow[];
359
+
360
+ return rows.map(mapFeature);
361
+ }
362
+
363
+ // =============================================================================
364
+ // Special Symbol Functions
365
+ // =============================================================================
366
+
367
+ /**
368
+ * Get a special symbol by name.
369
+ */
370
+ export async function getSpecialSymbolByName(
371
+ name: string,
372
+ options?: ConnectionOptions
373
+ ): Promise<SpecialSymbol | null> {
374
+ const db = getDatabase({ ...options, readonly: true });
375
+
376
+ const row = db
377
+ .prepare(
378
+ 'SELECT * FROM special_symbols WHERE LOWER(name) = LOWER(?) OR LOWER(symbol) = LOWER(?)'
379
+ )
380
+ .get(name, name) as SpecialSymbolRow | undefined;
381
+
382
+ return row ? mapSpecialSymbol(row) : null;
383
+ }
384
+
385
+ /**
386
+ * Get all special symbols.
387
+ */
388
+ export async function getAllSpecialSymbols(options?: ConnectionOptions): Promise<SpecialSymbol[]> {
389
+ const db = getDatabase({ ...options, readonly: true });
390
+
391
+ const rows = db
392
+ .prepare('SELECT * FROM special_symbols ORDER BY name')
393
+ .all() as SpecialSymbolRow[];
394
+
395
+ return rows.map(mapSpecialSymbol);
396
+ }
397
+
398
+ // =============================================================================
399
+ // Unified Search
400
+ // =============================================================================
401
+
402
+ /**
403
+ * Search across all language elements.
404
+ */
405
+ export async function searchLanguageElements(
406
+ query: string,
407
+ types?: LanguageElementType[],
408
+ options?: ConnectionOptions
409
+ ): Promise<LanguageElement[]> {
410
+ const results: LanguageElement[] = [];
411
+ const searchTypes = types || ['command', 'expression', 'keyword', 'feature', 'special_symbol'];
412
+
413
+ if (searchTypes.includes('command')) {
414
+ const commands = await searchCommands(query, options);
415
+ results.push(...commands.map(c => ({ type: 'command' as const, element: c })));
416
+ }
417
+
418
+ if (searchTypes.includes('expression')) {
419
+ const db = getDatabase({ ...options, readonly: true });
420
+ const pattern = `%${query}%`;
421
+ const rows = db
422
+ .prepare(
423
+ `SELECT * FROM expressions
424
+ WHERE LOWER(name) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?) OR LOWER(category) LIKE LOWER(?)
425
+ ORDER BY name`
426
+ )
427
+ .all(pattern, pattern, pattern) as ExpressionRow[];
428
+
429
+ // Get operators
430
+ const ids = rows.map(r => r.id);
431
+ const operatorMap = new Map<string, string[]>();
432
+ if (ids.length > 0) {
433
+ const placeholders = ids.map(() => '?').join(',');
434
+ const ops = db
435
+ .prepare(
436
+ `SELECT expression_id, operator FROM expression_operators WHERE expression_id IN (${placeholders})`
437
+ )
438
+ .all(...ids) as { expression_id: string; operator: string }[];
439
+ for (const op of ops) {
440
+ const arr = operatorMap.get(op.expression_id) || [];
441
+ arr.push(op.operator);
442
+ operatorMap.set(op.expression_id, arr);
443
+ }
444
+ }
445
+
446
+ results.push(
447
+ ...rows.map(row => ({
448
+ type: 'expression' as const,
449
+ element: mapExpression(row, operatorMap.get(row.id) || []),
450
+ }))
451
+ );
452
+ }
453
+
454
+ if (searchTypes.includes('keyword')) {
455
+ const db = getDatabase({ ...options, readonly: true });
456
+ const pattern = `%${query}%`;
457
+ const rows = db
458
+ .prepare(
459
+ `SELECT * FROM keywords
460
+ WHERE LOWER(name) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?)
461
+ ORDER BY name`
462
+ )
463
+ .all(pattern, pattern) as KeywordRow[];
464
+ results.push(...rows.map(row => ({ type: 'keyword' as const, element: mapKeyword(row) })));
465
+ }
466
+
467
+ if (searchTypes.includes('feature')) {
468
+ const db = getDatabase({ ...options, readonly: true });
469
+ const pattern = `%${query}%`;
470
+ const rows = db
471
+ .prepare(
472
+ `SELECT * FROM features
473
+ WHERE LOWER(name) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?)
474
+ ORDER BY name`
475
+ )
476
+ .all(pattern, pattern) as FeatureRow[];
477
+ results.push(...rows.map(row => ({ type: 'feature' as const, element: mapFeature(row) })));
478
+ }
479
+
480
+ if (searchTypes.includes('special_symbol')) {
481
+ const db = getDatabase({ ...options, readonly: true });
482
+ const pattern = `%${query}%`;
483
+ const rows = db
484
+ .prepare(
485
+ `SELECT * FROM special_symbols
486
+ WHERE LOWER(name) LIKE LOWER(?) OR LOWER(symbol) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?)
487
+ ORDER BY name`
488
+ )
489
+ .all(pattern, pattern, pattern) as SpecialSymbolRow[];
490
+ results.push(
491
+ ...rows.map(row => ({ type: 'special_symbol' as const, element: mapSpecialSymbol(row) }))
492
+ );
493
+ }
494
+
495
+ return results;
496
+ }
497
+
498
+ // =============================================================================
499
+ // Statistics
500
+ // =============================================================================
501
+
502
+ /**
503
+ * Get language documentation statistics.
504
+ */
505
+ export async function getLanguageDocsStats(
506
+ options?: ConnectionOptions
507
+ ): Promise<LanguageDocsStats> {
508
+ const db = getDatabase({ ...options, readonly: true });
509
+
510
+ const getCount = (table: string): number => {
511
+ try {
512
+ const result = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get() as {
513
+ count: number;
514
+ };
515
+ return result.count;
516
+ } catch {
517
+ return 0;
518
+ }
519
+ };
520
+
521
+ return {
522
+ commands: getCount('commands'),
523
+ expressions: getCount('expressions'),
524
+ keywords: getCount('keywords'),
525
+ features: getCount('features'),
526
+ specialSymbols: getCount('special_symbols'),
527
+ expressionOperators: getCount('expression_operators'),
528
+ };
529
+ }