@futdevpro/nts-dynamo 1.15.58 → 1.15.60

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 (85) hide show
  1. package/.dynamo/logs/cicd-pipeline/output.log +1622 -1705
  2. package/.dynamo/logs/cicd-pipeline/status.json +30 -30
  3. package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.d.ts.map +1 -1
  4. package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.js +2 -2
  5. package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.js.map +1 -1
  6. package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts +1 -1
  7. package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts.map +1 -1
  8. package/build/_modules/ai/_services/ai-embedding-mock.service.js.map +1 -1
  9. package/build/_modules/ai/_services/ai-embedding-provider.registry.d.ts.map +1 -1
  10. package/build/_modules/ai/_services/ai-embedding-provider.registry.js.map +1 -1
  11. package/build/_modules/ai/_services/lmstudio-embedding.control-service.d.ts +1 -1
  12. package/build/_modules/ai/_services/lmstudio-embedding.control-service.d.ts.map +1 -1
  13. package/build/_modules/ai/_services/lmstudio-embedding.control-service.js +3 -3
  14. package/build/_modules/ai/_services/lmstudio-embedding.control-service.js.map +1 -1
  15. package/build/_modules/ai/index.d.ts +2 -0
  16. package/build/_modules/ai/index.d.ts.map +1 -1
  17. package/build/_modules/ai/index.js +4 -0
  18. package/build/_modules/ai/index.js.map +1 -1
  19. package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.d.ts +17 -17
  20. package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.d.ts.map +1 -1
  21. package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.js +21 -21
  22. package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.js.map +1 -1
  23. package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.d.ts +4 -4
  24. package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.d.ts.map +1 -1
  25. package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.js +5 -5
  26. package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.js.map +1 -1
  27. package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts +4 -4
  28. package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts.map +1 -1
  29. package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js +4 -4
  30. package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js.map +1 -1
  31. package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts +6 -6
  32. package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts.map +1 -1
  33. package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js +5 -5
  34. package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js.map +1 -1
  35. package/build/_modules/mcp/_models/interfaces/dynts-mcp.interface.d.ts +1 -1
  36. package/build/_modules/mcp/_models/interfaces/dynts-mcp.interface.js +1 -1
  37. package/build/_modules/mcp/_services/dynts-mcp-server.service-base.d.ts +4 -4
  38. package/build/_modules/mcp/_services/dynts-mcp-server.service-base.d.ts.map +1 -1
  39. package/build/_modules/mcp/_services/dynts-mcp-server.service-base.js +6 -6
  40. package/build/_modules/mcp/_services/dynts-mcp-server.service-base.js.map +1 -1
  41. package/build/_modules/mcp/_services/dynts-mcp.adapter.d.ts +11 -11
  42. package/build/_modules/mcp/_services/dynts-mcp.adapter.d.ts.map +1 -1
  43. package/build/_modules/mcp/_services/dynts-mcp.adapter.js +16 -11
  44. package/build/_modules/mcp/_services/dynts-mcp.adapter.js.map +1 -1
  45. package/build/_modules/mcp/index.js +1 -1
  46. package/build/_modules/mcp/index.js.map +1 -1
  47. package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.d.ts +3 -3
  48. package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.d.ts.map +1 -1
  49. package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.js +4 -4
  50. package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.js.map +1 -1
  51. package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.d.ts.map +1 -1
  52. package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.js +9 -0
  53. package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.js.map +1 -1
  54. package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.d.ts +3 -3
  55. package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.d.ts.map +1 -1
  56. package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.js +1 -1
  57. package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.js.map +1 -1
  58. package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.d.ts +7 -7
  59. package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.d.ts.map +1 -1
  60. package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.js +2 -2
  61. package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +39 -7
  64. package/src/_modules/ai/_services/ai-embedding-mock.service.ts +18 -4
  65. package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +4 -0
  66. package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +26 -5
  67. package/src/_modules/ai/index.ts +5 -0
  68. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +145 -130
  69. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +131 -120
  70. package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +6 -5
  71. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +35 -35
  72. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +9 -5
  73. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +11 -11
  74. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +19 -17
  75. package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +1 -1
  76. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +123 -114
  77. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +44 -39
  78. package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +114 -103
  79. package/src/_modules/mcp/index.ts +1 -1
  80. package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +5 -4
  81. package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +0 -2
  82. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +19 -13
  83. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +37 -21
  84. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +11 -6
  85. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.ts +17 -14
@@ -1,11 +1,12 @@
1
1
  import { CreateEmbeddingResponse } from 'openai/resources';
2
+
3
+ import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
2
4
  import { DyFM_AI_Provider, DyFM_AI_ProviderCapabilities, DyFM_AI_Config } from '@futdevpro/fsm-dynamo/ai';
3
5
  import { DyFM_DAI_EmbeddingInfo } from '@futdevpro/fsm-dynamo/ai/document-ai';
4
- import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
5
6
 
6
- import { DyNTS_AI_Embedding_ServiceBase } from './ai-embedding.service-base';
7
7
  import { DyNTS_AI_CostEventCallback } from '../_models/interfaces/dynts-ai-cost-event-callback.interface';
8
8
  import { DyNTS_global_settings } from '../../../_collections/global-settings.const';
9
+ import { DyNTS_AI_Embedding_ServiceBase } from './ai-embedding.service-base';
9
10
 
10
11
  /**
11
12
  * Az LM Studio embedding control-service config-set-je. A `baseUrl` az OpenAI-kompatibilis
@@ -68,7 +69,8 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
68
69
  */
69
70
  constructor(set: DyNTS_LMStudio_Embedding_Settings) {
70
71
  super();
71
- if (!set?.baseUrl || !set.baseUrl.trim().length) {
72
+
73
+ if (!set?.baseUrl?.trim().length) {
72
74
  throw new DyFM_Error({
73
75
  ...this.getDefaultErrorSettings(
74
76
  'constructor',
@@ -80,6 +82,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
80
82
  }
81
83
  this.baseUrl = this.trimTrailingSlashes(set.baseUrl.trim());
82
84
  this.apiKey = set.apiKey;
85
+
83
86
  if (set.onCostEvent) {
84
87
  this.onCostEvent = set.onCostEvent;
85
88
  }
@@ -93,6 +96,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
93
96
  if (config?.baseURL) {
94
97
  this.baseUrl = this.trimTrailingSlashes(config.baseURL);
95
98
  }
99
+
96
100
  if (config?.apiKey) {
97
101
  this.apiKey = config.apiKey;
98
102
  }
@@ -122,16 +126,18 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
122
126
  const response: CreateEmbeddingResponse = await this.callEmbeddingsEndpoint(set.model, set.text, set.issuer);
123
127
  const durationMs: number = Date.now() - start;
124
128
 
125
- this.emitLmStudioCostEvent('embedding-single', set.model, [set.text], response, durationMs, set.issuer);
129
+ this.emitLmStudioCostEvent('embedding-single', set.model, [ set.text ], response, durationMs, set.issuer);
126
130
 
127
131
  if (set.fullResponse) {
128
132
  return response;
129
133
  }
134
+
130
135
  return response.data[0].embedding;
131
136
  } catch (error) {
132
137
  if (error instanceof DyFM_Error) {
133
138
  throw error;
134
139
  }
140
+
135
141
  throw new DyFM_Error({
136
142
  ...this.getDefaultErrorSettings('createEmbedding', error, set.issuer),
137
143
  errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-LMS-ECS-CE0`,
@@ -168,11 +174,13 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
168
174
  if (set.fullResponse) {
169
175
  return response;
170
176
  }
177
+
171
178
  return response.data.map((item) => item.embedding);
172
179
  } catch (error) {
173
180
  if (error instanceof DyFM_Error) {
174
181
  throw error;
175
182
  }
183
+
176
184
  throw new DyFM_Error({
177
185
  ...this.getDefaultErrorSettings('createEmbeddings', error, set.issuer),
178
186
  errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-LMS-ECS-CES0`,
@@ -198,17 +206,19 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
198
206
  async testConnection(issuer: string): Promise<boolean> {
199
207
  try {
200
208
  const vectors: number[][] | CreateEmbeddingResponse = await this.createEmbeddings({
201
- texts: ['ping'],
209
+ texts: [ 'ping' ],
202
210
  model: this.defaultProbeModel(),
203
211
  fullResponse: false,
204
212
  issuer: issuer,
205
213
  });
214
+
206
215
  return Array.isArray(vectors) && vectors.length === 1 && vectors[0].length > 0;
207
216
  } catch (error) {
208
217
  DyFM_Log.error('DyNTS_LMStudio_Embedding_ControlService', 'testConnection', 'Connection test failed', {
209
218
  error: error,
210
219
  issuer: issuer,
211
220
  });
221
+
212
222
  return false;
213
223
  }
214
224
  }
@@ -229,6 +239,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
229
239
  ): Promise<CreateEmbeddingResponse> {
230
240
  const url: string = `${this.baseUrl}/embeddings`;
231
241
  const headers: { [key: string]: string } = { 'Content-Type': 'application/json' };
242
+
232
243
  if (this.apiKey && this.apiKey.trim().length) {
233
244
  headers.Authorization = `Bearer ${this.apiKey.trim()}`;
234
245
  }
@@ -248,6 +259,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
248
259
  }
249
260
 
250
261
  let parsed: unknown;
262
+
251
263
  try {
252
264
  parsed = JSON.parse(rawText);
253
265
  } catch {
@@ -257,6 +269,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
257
269
  }
258
270
 
259
271
  const expectedCount: number = Array.isArray(input) ? input.length : 1;
272
+
260
273
  return this.normalizeResponse(parsed, expectedCount, url, model);
261
274
  }
262
275
 
@@ -270,21 +283,25 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
270
283
  throw new Error(`LM Studio embeddings: invalid response object | url=${url} | model=${modelId}`);
271
284
  }
272
285
  const dataRaw: unknown = Reflect.get(json, 'data');
286
+
273
287
  if (!Array.isArray(dataRaw)) {
274
288
  throw new Error(`LM Studio embeddings: missing data array | url=${url} | model=${modelId}`);
275
289
  }
276
290
 
277
291
  const data: CreateEmbeddingResponse['data'] = [];
278
292
  let index: number = 0;
293
+
279
294
  for (const item of dataRaw) {
280
295
  if (item === null || typeof item !== 'object' || Array.isArray(item)) {
281
296
  continue;
282
297
  }
283
298
  const embRaw: unknown = Reflect.get(item, 'embedding');
299
+
284
300
  if (!Array.isArray(embRaw)) {
285
301
  continue;
286
302
  }
287
303
  const vec: number[] = embRaw.filter((x: unknown): x is number => typeof x === 'number' && Number.isFinite(x));
304
+
288
305
  if (vec.length) {
289
306
  data.push({ object: 'embedding', embedding: vec, index: index });
290
307
  index++;
@@ -345,9 +362,11 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
345
362
  /** Becsült token-szám lokális endpoint-hoz (ha nincs `usage`): 4 char ≈ 1 token. */
346
363
  protected estimateTokens(texts: string[]): number {
347
364
  let chars: number = 0;
365
+
348
366
  for (const text of texts) {
349
367
  chars += (text ?? '').length;
350
368
  }
369
+
351
370
  return Math.max(1, Math.ceil(chars / 4));
352
371
  }
353
372
 
@@ -364,6 +383,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
364
383
  /** Rövid, biztonságos válasz-snapshot a hiba-üzenethez (max 300 char). */
365
384
  protected snapshot(value: string): string {
366
385
  const trimmed: string = (value ?? '').slice(0, 300);
386
+
367
387
  return trimmed.length === 300 ? `${trimmed}…` : trimmed;
368
388
  }
369
389
 
@@ -373,6 +393,7 @@ export class DyNTS_LMStudio_Embedding_ControlService extends DyNTS_AI_Embedding_
373
393
  return 0;
374
394
  }
375
395
  const value: unknown = Reflect.get(obj, key);
396
+
376
397
  return typeof value === 'number' && Number.isFinite(value) ? value : 0;
377
398
  }
378
399
  }
@@ -5,6 +5,11 @@ export * from '@futdevpro/fsm-dynamo/ai';
5
5
  export * from './_models/ai-input-interfaces';
6
6
  export * from './_models/ai-test-generation-result.interface';
7
7
 
8
+ // Cost-event interfaces (BFR-AM-007) — a consumer (CCAP / FAM) ezekkel típusozza az `onCostEvent`
9
+ // callback-jét; ezért barrel-export kell (M3, FAM-REV bedrock-fix).
10
+ export * from './_models/interfaces/dynts-ai-cost-event.interface';
11
+ export * from './_models/interfaces/dynts-ai-cost-event-callback.interface';
12
+
8
13
  // Abstract Services
9
14
  export * from './_services/ai-provider.service-base';
10
15
  export * from './_services/ai-llm.service-base';
@@ -9,6 +9,11 @@ import { DyFM_Error } from '@futdevpro/fsm-dynamo';
9
9
  import { DyNTS_Sqlite_Reader_Util } from './dynts-sqlite-reader.util';
10
10
  import { DyNTS_SqliteColumnInfo } from '../_models/interfaces/dynts-sqlite-reader.interface';
11
11
 
12
+ interface NameInterface {
13
+ name: string;
14
+ }
15
+
16
+
12
17
  /**
13
18
  * `DyNTS_Sqlite_Reader_Util` spec (BFR-AM-009). Egy temp `.db` fájlt hozunk létre (a reader path-szal
14
19
  * + `fileMustExist:true` nyit, ezért valódi fájl kell — a `:memory:` path-ön nem ad fájlt), feltöltjük,
@@ -20,142 +25,152 @@ import { DyNTS_SqliteColumnInfo } from '../_models/interfaces/dynts-sqlite-reade
20
25
  * - `query` NEM-SELECT (INSERT/UPDATE/DELETE/DROP/multi-statement) → READONLY-hiba (elutasít),
21
26
  * - hiányzó path / nem-létező fájl → strukturált hiba.
22
27
  */
23
- describe('DyNTS_Sqlite_Reader_Util (read-only SQLite reader, BFR-AM-009)', () => {
28
+ describe('| DyNTS_Sqlite_Reader_Util (read-only SQLite reader, BFR-AM-009)', () => {
29
+
30
+ /** A temp DB-fájl útvonala (minden teszt-futáshoz egyedi). */
31
+ let dbPath: string;
24
32
 
25
- /** A temp DB-fájl útvonala (minden teszt-futáshoz egyedi). */
26
- let dbPath: string;
33
+ beforeAll(() => {
34
+ dbPath = path.join(os.tmpdir(), `dynts-sqlite-spec-${process.pid}-${Date.now()}.db`);
35
+ const db: Database.Database = new Database(dbPath);
27
36
 
28
- beforeAll(() => {
29
- dbPath = path.join(os.tmpdir(), `dynts-sqlite-spec-${process.pid}-${Date.now()}.db`);
30
- const db: Database.Database = new Database(dbPath);
31
- db.exec(`
37
+ db.exec(`
32
38
  CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER);
33
39
  CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT);
34
40
  `);
35
- db.prepare('INSERT INTO users (id, name, age) VALUES (?, ?, ?)').run(1, 'Ada', 36);
36
- db.prepare('INSERT INTO users (id, name, age) VALUES (?, ?, ?)').run(2, 'Grace', 40);
37
- db.prepare('INSERT INTO posts (id, title) VALUES (?, ?)').run(1, 'Hello');
38
- db.close();
39
- });
40
-
41
- afterAll(() => {
42
- if (dbPath && fs.existsSync(dbPath)) {
43
- fs.unlinkSync(dbPath);
44
- }
45
- });
46
-
47
- /** Egy hívás, ami egy adott errorCode-dal kell hogy dobjon (strukturált, NEM néma). */
48
- const expectErrorCode = (fn: () => unknown, expectedCode: string): void => {
49
- let thrown: unknown;
50
- try {
51
- fn();
52
- } catch (error) {
53
- thrown = error;
54
- }
55
- expect(thrown).withContext(`dobnia kellett (${expectedCode})`).toBeDefined();
56
- const code: string | undefined =
41
+ db.prepare('INSERT INTO users (id, name, age) VALUES (?, ?, ?)').run(1, 'Ada', 36);
42
+ db.prepare('INSERT INTO users (id, name, age) VALUES (?, ?, ?)').run(2, 'Grace', 40);
43
+ db.prepare('INSERT INTO posts (id, title) VALUES (?, ?)').run(1, 'Hello');
44
+ db.close();
45
+ });
46
+
47
+ afterAll(() => {
48
+ if (dbPath && fs.existsSync(dbPath)) {
49
+ fs.unlinkSync(dbPath);
50
+ }
51
+ });
52
+
53
+ /** Egy hívás, ami egy adott errorCode-dal kell hogy dobjon (strukturált, NEM néma). */
54
+ const expectErrorCode = (fn: () => unknown, expectedCode: string): void => {
55
+ let thrown: unknown;
56
+
57
+ try {
58
+ fn();
59
+ } catch (error) {
60
+ thrown = error;
61
+ }
62
+ expect(thrown).withContext(`dobnia kellett (${expectedCode})`).toBeDefined();
63
+ const code: string | undefined =
57
64
  thrown instanceof DyFM_Error ? DyFM_Error.getErrorCode(thrown) : undefined;
58
- expect(code).withContext('a hibakód').toBe(expectedCode);
59
- };
60
-
61
- describe('listTables', () => {
62
- it('a user-táblák ABC-sorrendben (a belső sqlite_% táblák kiszűrve)', () => {
63
- expect(DyNTS_Sqlite_Reader_Util.listTables(dbPath)).toEqual(['posts', 'users']);
64
- });
65
- });
66
-
67
- describe('getSchema', () => {
68
- it('a users tábla oszlop-info-ja (name/type/notNull/primaryKey)', () => {
69
- const schema: DyNTS_SqliteColumnInfo[] = DyNTS_Sqlite_Reader_Util.getSchema(dbPath, 'users');
70
- expect(schema.map((column) => column.name)).toEqual(['id', 'name', 'age']);
71
- const idColumn: DyNTS_SqliteColumnInfo = schema.find((column) => column.name === 'id')!;
72
- expect(idColumn.primaryKey).toBe(true);
73
- const nameColumn: DyNTS_SqliteColumnInfo = schema.find((column) => column.name === 'name')!;
74
- expect(nameColumn.notNull).toBe(true);
75
- expect(nameColumn.type).toBe('TEXT');
76
- });
77
-
78
- it('nem-létező tábla → DyNTS-SQLITE-READ-002 (NEM néma)', () => {
79
- expectErrorCode(() => DyNTS_Sqlite_Reader_Util.getSchema(dbPath, 'no_such_table'), 'DyNTS-SQLITE-READ-002');
80
- });
81
- });
82
-
83
- describe('readTable', () => {
84
- it('a users tábla minden sora', () => {
85
- const rows: { id: number; name: string }[] =
65
+
66
+ expect(code).withContext('a hibakód').toBe(expectedCode);
67
+ };
68
+
69
+ describe('| listTables', () => {
70
+ it('| a user-táblák ABC-sorrendben (a belső sqlite_% táblák kiszűrve)', () => {
71
+ expect(DyNTS_Sqlite_Reader_Util.listTables(dbPath)).toEqual([ 'posts', 'users' ]);
72
+ });
73
+ });
74
+
75
+ describe('| getSchema', () => {
76
+ it('| a users tábla oszlop-info-ja (name/type/notNull/primaryKey)', () => {
77
+ const schema: DyNTS_SqliteColumnInfo[] = DyNTS_Sqlite_Reader_Util.getSchema(dbPath, 'users');
78
+
79
+ expect(schema.map((column) => column.name)).toEqual([ 'id', 'name', 'age' ]);
80
+ const idColumn: DyNTS_SqliteColumnInfo = schema.find((column) => column.name === 'id');
81
+
82
+ expect(idColumn.primaryKey).toBe(true);
83
+ const nameColumn: DyNTS_SqliteColumnInfo = schema.find((column) => column.name === 'name');
84
+
85
+ expect(nameColumn.notNull).toBe(true);
86
+ expect(nameColumn.type).toBe('TEXT');
87
+ });
88
+
89
+ it('| nem-létező tábla → DyNTS-SQLITE-READ-002 (NEM néma)', () => {
90
+ expectErrorCode(() => DyNTS_Sqlite_Reader_Util.getSchema(dbPath, 'no_such_table'), 'DyNTS-SQLITE-READ-002');
91
+ });
92
+ });
93
+
94
+ describe('| readTable', () => {
95
+ it('| a users tábla minden sora', () => {
96
+ const rows: { id: number; name: string }[] =
86
97
  DyNTS_Sqlite_Reader_Util.readTable(dbPath, 'users') as { id: number; name: string }[];
87
- expect(rows.length).toBe(2);
88
- expect(rows.map((row) => row.name).sort()).toEqual(['Ada', 'Grace']);
89
- });
90
-
91
- it('nem-létező tábla → DyNTS-SQLITE-READ-002 (NEM néma-[])', () => {
92
- expectErrorCode(() => DyNTS_Sqlite_Reader_Util.readTable(dbPath, 'ghost'), 'DyNTS-SQLITE-READ-002');
93
- });
94
- });
95
-
96
- describe('query (SELECT-only)', () => {
97
- it('egy SELECT lekérdezés sorai', () => {
98
- const rows: { name: string }[] =
99
- DyNTS_Sqlite_Reader_Util.query(dbPath, 'SELECT name FROM users ORDER BY age DESC') as { name: string }[];
100
- expect(rows.map((row) => row.name)).toEqual(['Grace', 'Ada']);
101
- });
102
-
103
- it('paraméteres SELECT (prepared-statement param)', () => {
104
- const rows: { name: string }[] =
105
- DyNTS_Sqlite_Reader_Util.query(dbPath, 'SELECT * FROM users WHERE age > ?', [37]) as { name: string }[];
106
- expect(rows.map((row) => row.name)).toEqual(['Grace']);
107
- });
108
-
109
- it('WITH (CTE) is engedett (read-only)', () => {
110
- const rows: { n: number }[] = DyNTS_Sqlite_Reader_Util.query(dbPath,
111
- 'WITH adults AS (SELECT * FROM users WHERE age >= 36) SELECT COUNT(*) AS n FROM adults') as { n: number }[];
112
- expect(rows[0].n).toBe(2);
113
- });
114
-
115
- it('INSERT → DyNTS-SQLITE-READONLY-001 (elutasít a forrás-rétegben)', () => {
116
- expectErrorCode(
117
- () => DyNTS_Sqlite_Reader_Util.query(dbPath, "INSERT INTO users (id, name) VALUES (9, 'x')"),
118
- 'DyNTS-SQLITE-READONLY-001');
119
- });
120
-
121
- it('UPDATE DyNTS-SQLITE-READONLY-001', () => {
122
- expectErrorCode(
123
- () => DyNTS_Sqlite_Reader_Util.query(dbPath, "UPDATE users SET name='x' WHERE id=1"),
124
- 'DyNTS-SQLITE-READONLY-001');
125
- });
126
-
127
- it('DELETE DyNTS-SQLITE-READONLY-001', () => {
128
- expectErrorCode(
129
- () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'DELETE FROM users'),
130
- 'DyNTS-SQLITE-READONLY-001');
131
- });
132
-
133
- it('DROP DyNTS-SQLITE-READONLY-001', () => {
134
- expectErrorCode(
135
- () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'DROP TABLE users'),
136
- 'DyNTS-SQLITE-READONLY-001');
137
- });
138
-
139
- it('multi-statement (SELECT; DELETE) DyNTS-SQLITE-READONLY-001 (nem rejthető írás SELECT mögé)', () => {
140
- expectErrorCode(
141
- () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'SELECT 1; DELETE FROM users'),
142
- 'DyNTS-SQLITE-READONLY-001');
143
- });
144
-
145
- it('üres SQL DyNTS-SQLITE-READONLY-001', () => {
146
- expectErrorCode(() => DyNTS_Sqlite_Reader_Util.query(dbPath, ' '), 'DyNTS-SQLITE-READONLY-001');
147
- });
148
- });
149
-
150
- describe('path-validáció (fail-soft, NEM crash)', () => {
151
- it('üres path → DyNTS-SQLITE-READ-001', () => {
152
- expectErrorCode(() => DyNTS_Sqlite_Reader_Util.listTables(''), 'DyNTS-SQLITE-READ-001');
153
- });
154
-
155
- it('nem-létező fájl → DyNTS-SQLITE-READ-001', () => {
156
- expectErrorCode(
157
- () => DyNTS_Sqlite_Reader_Util.listTables(path.join(os.tmpdir(), 'no-such-db-file-xyz.db')),
158
- 'DyNTS-SQLITE-READ-001');
159
- });
98
+
99
+ expect(rows.length).toBe(2);
100
+ expect(rows.map((row) => row.name).sort()).toEqual([ 'Ada', 'Grace' ]);
101
+ });
102
+
103
+ it('| nem-létező tábla DyNTS-SQLITE-READ-002 (NEM néma-[])', () => {
104
+ expectErrorCode(() => DyNTS_Sqlite_Reader_Util.readTable(dbPath, 'ghost'), 'DyNTS-SQLITE-READ-002');
105
+ });
106
+ });
107
+
108
+ describe('| query (SELECT-only)', () => {
109
+ it('| egy SELECT lekérdezés sorai', () => {
110
+ const rows: NameInterface[] =
111
+ DyNTS_Sqlite_Reader_Util.query(dbPath, 'SELECT name FROM users ORDER BY age DESC') as NameInterface[];
112
+
113
+ expect(rows.map((row) => row.name)).toEqual([ 'Grace', 'Ada' ]);
114
+ });
115
+
116
+ it('| paraméteres SELECT (prepared-statement param)', () => {
117
+ const rows: NameInterface[] =
118
+ DyNTS_Sqlite_Reader_Util.query(dbPath, 'SELECT * FROM users WHERE age > ?', [ 37 ]) as NameInterface[];
119
+
120
+ expect(rows.map((row) => row.name)).toEqual([ 'Grace' ]);
121
+ });
122
+
123
+ it('| WITH (CTE) is engedett (read-only)', () => {
124
+ const rows: { n: number }[] = DyNTS_Sqlite_Reader_Util.query(dbPath,
125
+ 'WITH adults AS (SELECT * FROM users WHERE age >= 36) SELECT COUNT(*) AS n FROM adults') as { n: number }[];
126
+
127
+ expect(rows[0].n).toBe(2);
128
+ });
129
+
130
+ it('| INSERT → DyNTS-SQLITE-READONLY-001 (elutasít a forrás-rétegben)', () => {
131
+ expectErrorCode(
132
+ () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'INSERT INTO users (id, name) VALUES (9, \'x\')'),
133
+ 'DyNTS-SQLITE-READONLY-001');
134
+ });
135
+
136
+ it('| UPDATE → DyNTS-SQLITE-READONLY-001', () => {
137
+ expectErrorCode(
138
+ () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'UPDATE users SET name=\'x\' WHERE id=1'),
139
+ 'DyNTS-SQLITE-READONLY-001');
140
+ });
141
+
142
+ it('| DELETE → DyNTS-SQLITE-READONLY-001', () => {
143
+ expectErrorCode(
144
+ () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'DELETE FROM users'),
145
+ 'DyNTS-SQLITE-READONLY-001');
146
+ });
147
+
148
+ it('| DROP → DyNTS-SQLITE-READONLY-001', () => {
149
+ expectErrorCode(
150
+ () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'DROP TABLE users'),
151
+ 'DyNTS-SQLITE-READONLY-001');
152
+ });
153
+
154
+ it('| multi-statement (SELECT; DELETE) → DyNTS-SQLITE-READONLY-001 (nem rejthető írás SELECT mögé)', () => {
155
+ expectErrorCode(
156
+ () => DyNTS_Sqlite_Reader_Util.query(dbPath, 'SELECT 1; DELETE FROM users'),
157
+ 'DyNTS-SQLITE-READONLY-001');
158
+ });
159
+
160
+ it('| üres SQL → DyNTS-SQLITE-READONLY-001', () => {
161
+ expectErrorCode(() => DyNTS_Sqlite_Reader_Util.query(dbPath, ' '), 'DyNTS-SQLITE-READONLY-001');
162
+ });
163
+ });
164
+
165
+ describe('| path-validáció (fail-soft, NEM crash)', () => {
166
+ it('| üres path → DyNTS-SQLITE-READ-001', () => {
167
+ expectErrorCode(() => DyNTS_Sqlite_Reader_Util.listTables(''), 'DyNTS-SQLITE-READ-001');
168
+ });
169
+
170
+ it('| nem-létező fájl → DyNTS-SQLITE-READ-001', () => {
171
+ expectErrorCode(
172
+ () => DyNTS_Sqlite_Reader_Util.listTables(path.join(os.tmpdir(), 'no-such-db-file-xyz.db')),
173
+ 'DyNTS-SQLITE-READ-001');
160
174
  });
175
+ });
161
176
  });