@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.
- package/.dynamo/logs/cicd-pipeline/output.log +1622 -1705
- package/.dynamo/logs/cicd-pipeline/status.json +30 -30
- package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.d.ts.map +1 -1
- package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.js +2 -2
- package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.js.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts +1 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.js.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.d.ts.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.js.map +1 -1
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.d.ts +1 -1
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.d.ts.map +1 -1
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.js +3 -3
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.js.map +1 -1
- package/build/_modules/ai/index.d.ts +2 -0
- package/build/_modules/ai/index.d.ts.map +1 -1
- package/build/_modules/ai/index.js +4 -0
- package/build/_modules/ai/index.js.map +1 -1
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.d.ts +17 -17
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.d.ts.map +1 -1
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.js +21 -21
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.js.map +1 -1
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.d.ts +4 -4
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.js +5 -5
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts +4 -4
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js +4 -4
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts +6 -6
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js +5 -5
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js.map +1 -1
- package/build/_modules/mcp/_models/interfaces/dynts-mcp.interface.d.ts +1 -1
- package/build/_modules/mcp/_models/interfaces/dynts-mcp.interface.js +1 -1
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.d.ts +4 -4
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.d.ts.map +1 -1
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.js +6 -6
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.js.map +1 -1
- package/build/_modules/mcp/_services/dynts-mcp.adapter.d.ts +11 -11
- package/build/_modules/mcp/_services/dynts-mcp.adapter.d.ts.map +1 -1
- package/build/_modules/mcp/_services/dynts-mcp.adapter.js +16 -11
- package/build/_modules/mcp/_services/dynts-mcp.adapter.js.map +1 -1
- package/build/_modules/mcp/index.js +1 -1
- package/build/_modules/mcp/index.js.map +1 -1
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.d.ts +3 -3
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.d.ts.map +1 -1
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.js +4 -4
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.js.map +1 -1
- package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.d.ts.map +1 -1
- package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.js +9 -0
- package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.js.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.d.ts +3 -3
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.d.ts.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.js +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.js.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.d.ts +7 -7
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.d.ts.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.js +2 -2
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.js.map +1 -1
- package/package.json +1 -1
- package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +39 -7
- package/src/_modules/ai/_services/ai-embedding-mock.service.ts +18 -4
- package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +4 -0
- package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +26 -5
- package/src/_modules/ai/index.ts +5 -0
- package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +145 -130
- package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +131 -120
- package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +6 -5
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +35 -35
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +9 -5
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +11 -11
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +19 -17
- package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +1 -1
- package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +123 -114
- package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +44 -39
- package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +114 -103
- package/src/_modules/mcp/index.ts +1 -1
- package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +5 -4
- package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +0 -2
- package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +19 -13
- package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +37 -21
- package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +11 -6
- 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
|
-
|
|
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
|
}
|
package/src/_modules/ai/index.ts
CHANGED
|
@@ -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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
});
|