@futdevpro/fdp-agent-memory 0.1.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 +345 -0
- package/build/package.json +96 -0
- package/build/src/_assets/mcp-client-config/README.md +29 -0
- package/build/src/_assets/mcp-client-config/claude_desktop_config.json +15 -0
- package/build/src/_assets/mcp-client-config/mcp.json +15 -0
- package/build/src/_collections/config-catalog.const.js +180 -0
- package/build/src/_collections/config-error-codes.const.js +30 -0
- package/build/src/_collections/config-presets.const.js +25 -0
- package/build/src/_collections/error-banners.const.js +100 -0
- package/build/src/_collections/error-codes.const.js +150 -0
- package/build/src/_collections/fam-db-models.const.js +37 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +80 -0
- package/build/src/_collections/fam-error-context.util.js +90 -0
- package/build/src/_collections/fam-error-factory.util.js +64 -0
- package/build/src/_enums/fam-config-level.type-enum.js +15 -0
- package/build/src/_enums/fam-table.type-enum.js +20 -0
- package/build/src/_integration-tests/_helpers/fam-integration-test-setup.util.js +105 -0
- package/build/src/_models/data-models/fam-codebase.data-model.js +51 -0
- package/build/src/_models/data-models/fam-coding-patterns.data-model.js +58 -0
- package/build/src/_models/data-models/fam-config.data-model.js +68 -0
- package/build/src/_models/data-models/fam-documents.data-model.js +53 -0
- package/build/src/_models/data-models/fam-entry-base-properties.const.js +43 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +81 -0
- package/build/src/_models/data-models/fam-error.data-model.js +88 -0
- package/build/src/_models/data-models/fam-ingest-run.data-model.js +74 -0
- package/build/src/_models/data-models/fam-knowledge.data-model.js +48 -0
- package/build/src/_models/data-models/fam-memory.data-model.js +55 -0
- package/build/src/_models/data-models/fam-reference.data-model.js +67 -0
- package/build/src/_models/data-models/fam-rules.data-model.js +51 -0
- package/build/src/_models/data-models/fam-scope.data-model.js +52 -0
- package/build/src/_models/interfaces/fam-common.interface.js +23 -0
- package/build/src/_models/interfaces/fam-config.interface.js +2 -0
- package/build/src/_models/interfaces/fam-error.interface.js +2 -0
- package/build/src/_modules/embedding/_collections/fam-embedding-pricing.const.js +22 -0
- package/build/src/_modules/embedding/_collections/fam-store-registry.const.js +63 -0
- package/build/src/_modules/embedding/_models/interfaces/fam-embedding-cost.interface.js +10 -0
- package/build/src/_modules/embedding/_models/interfaces/fam-embedding-provider.interface.js +2 -0
- package/build/src/_modules/embedding/_models/interfaces/fam-resolved-provider.interface.js +2 -0
- package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +52 -0
- package/build/src/_modules/embedding/_services/fam-embedding-cost.control-service.js +175 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +202 -0
- package/build/src/_modules/embedding/_services/fam-embedding-preset.control-service.js +66 -0
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +253 -0
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +64 -0
- package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +112 -0
- package/build/src/_modules/embedding/_services/fam-mock-embedding.provider.js +64 -0
- package/build/src/_modules/embedding/_services/fam-openai-embedding.provider.js +64 -0
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +244 -0
- package/build/src/_modules/embedding/index.js +40 -0
- package/build/src/_modules/ingest/_collections/fam-content-hash.util.js +35 -0
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +95 -0
- package/build/src/_modules/ingest/_collections/fam-glob-match.util.js +84 -0
- package/build/src/_modules/ingest/_collections/fam-md-chunker.util.js +164 -0
- package/build/src/_modules/ingest/_collections/fam-scan-path.util.js +91 -0
- package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +54 -0
- package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +76 -0
- package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +316 -0
- package/build/src/_modules/ingest/_models/interfaces/fam-ingest.interface.js +2 -0
- package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +114 -0
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +74 -0
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +85 -0
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +384 -0
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +211 -0
- package/build/src/_modules/ingest/index.js +46 -0
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +186 -0
- package/build/src/_modules/mcp/_models/interfaces/fam-mcp.interface.js +31 -0
- package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +111 -0
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +1180 -0
- package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +123 -0
- package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +69 -0
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +99 -0
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +460 -0
- package/build/src/_modules/mcp/index.js +35 -0
- package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +166 -0
- package/build/src/_modules/migration/_collections/fam-import-content-hash.util.js +38 -0
- package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +90 -0
- package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +20 -0
- package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +26 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-export-reader.service.js +134 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +533 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-sqlite-reader.service.js +144 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-worker-reader.service.js +115 -0
- package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +102 -0
- package/build/src/_modules/migration/index.js +38 -0
- package/build/src/_modules/retrieval/_models/interfaces/fam-retrieval.interface.js +2 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +67 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-suggestions.util.js +182 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +282 -0
- package/build/src/_modules/retrieval/index.js +22 -0
- package/build/src/_modules/scope-reference/_collections/fam-fuzzy-match.util.js +86 -0
- package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +47 -0
- package/build/src/_modules/scope-reference/_models/interfaces/fam-reference-resolution.interface.js +2 -0
- package/build/src/_modules/scope-reference/_models/interfaces/fam-resolution-trace.interface.js +2 -0
- package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +179 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +473 -0
- package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +215 -0
- package/build/src/_modules/scope-reference/index.js +26 -0
- package/build/src/_routes/server/api/api.controller.js +400 -0
- package/build/src/_routes/server/client-app/client-app.control-service.js +132 -0
- package/build/src/_routes/server/client-app/client-app.controller.js +35 -0
- package/build/src/_routes/server/config/config.control-service.js +476 -0
- package/build/src/_routes/server/config/config.data-service.js +49 -0
- package/build/src/_routes/server/errors/errors.control-service.js +123 -0
- package/build/src/_routes/server/errors/errors.controller.js +65 -0
- package/build/src/_routes/server/errors/errors.data-service.js +80 -0
- package/build/src/_routes/server/server-status/server-status.control-service.js +19 -0
- package/build/src/_routes/server/server-status/server-status.controller.js +39 -0
- package/build/src/app.server.js +122 -0
- package/build/src/environments/environment.js +20 -0
- package/build/src/index.js +18 -0
- package/client-dist/chunk-GHKRM4SM.js +1 -0
- package/client-dist/chunk-LMTL7GA3.js +575 -0
- package/client-dist/index.html +17 -0
- package/client-dist/main-2KWB3QYK.js +2 -0
- package/client-dist/polyfills-HGDOEU5L.js +2 -0
- package/client-dist/styles-3J7JD5YE.css +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_Scope_DataService = void 0;
|
|
4
|
+
const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
|
|
5
|
+
const error_codes_const_1 = require("../../../_collections/error-codes.const");
|
|
6
|
+
const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
|
|
7
|
+
const fam_scope_data_model_1 = require("../../../_models/data-models/fam-scope.data-model");
|
|
8
|
+
const fam_scope_normalize_util_1 = require("../_collections/fam-scope-normalize.util");
|
|
9
|
+
/** A scope-böngészés CCAP-örökség konstansai (dsgn-007 §4 grounding). */
|
|
10
|
+
const DEFAULT_PAGE_SIZE = 50;
|
|
11
|
+
const MAX_PAGE_SIZE = 500;
|
|
12
|
+
/** A parent-lánc ciklus-/mélység-védelem felső korlátja (SP-3.1 guard). */
|
|
13
|
+
const MAX_SCOPE_DEPTH = 64;
|
|
14
|
+
/**
|
|
15
|
+
* `FAM_Scope_DataService` (SP-3.1, dsgn-002 §2/§6) — a `fam_scopes` (MAIN) collection CRUD + a scope-fa
|
|
16
|
+
* traverzál-API. `extends DyNTS_DataService` (soft-delete = move-to-archive a `getArchiveDataService`-en
|
|
17
|
+
* át): a törölt scope NEM jelenik meg a traverzálban (a `findDataList` az aktívakat adja).
|
|
18
|
+
*
|
|
19
|
+
* **Determinizmus (dsgn-002 §7.6):** ugyanaz a canonical entitás (normalizált név + layer +
|
|
20
|
+
* parentScopeId) ugyanahhoz a `scopeId`-hoz vezet — a `findOrCreateScope` find-by-key fast-path
|
|
21
|
+
* MIELŐTT létrehozna (nincs duplikált fa-csúcs).
|
|
22
|
+
*
|
|
23
|
+
* **FIGYELEM (memory: dynts_dataservice_eager_resolve):** a base-ctor EAGER `getDBService`-t hív,
|
|
24
|
+
* ezért a control-service-ek NEM tartanak élő példányt mezőként — minden művelet előtt lazy `new`.
|
|
25
|
+
*/
|
|
26
|
+
class FAM_Scope_DataService extends nts_dynamo_1.DyNTS_DataService {
|
|
27
|
+
constructor(set) {
|
|
28
|
+
super(set.data instanceof fam_scope_data_model_1.FAM_Scope_DataModel ? set.data : new fam_scope_data_model_1.FAM_Scope_DataModel(set.data), fam_scope_data_model_1.famScope_dataParams, set.issuer);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A move-to-archive soft-delete (dsgn-001 §8) archív-collection service-e: a base `deleteData`
|
|
32
|
+
* (addArchive) ezt hívja, hogy a törölt scope-ot a `fam_scopes_archived` tárba mozgassa. A
|
|
33
|
+
* `DyNTS_DataService` base default-ja DOB (nincs ökoszisztéma-implementáció) — ezért az explicit
|
|
34
|
+
* override (a `DyNTS_ArchiveDataService` a `dataParams.addArchive`-ból oldja az archív-nevet).
|
|
35
|
+
*/
|
|
36
|
+
getArchiveDataService() {
|
|
37
|
+
return new nts_dynamo_1.DyNTS_ArchiveDataService(new fam_scope_data_model_1.FAM_Scope_DataModel(), this.dataParams, this.issuer);
|
|
38
|
+
}
|
|
39
|
+
// =========================================================================
|
|
40
|
+
// CRUD + alap-lekérdezés (dsgn-002 §6)
|
|
41
|
+
// =========================================================================
|
|
42
|
+
/** Egy aktív scope `_id` alapján (vagy üres-objektum, ha nincs / törölt). */
|
|
43
|
+
async findScopeById(scopeId) {
|
|
44
|
+
return this.findData({ _id: scopeId }, true);
|
|
45
|
+
}
|
|
46
|
+
/** Egy layer összes aktív scope-ja (a böngészéshez / determinizmus-feloldáshoz). */
|
|
47
|
+
async findByLayer(layer) {
|
|
48
|
+
return this.findDataList({ layer: layer }, true);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `findByCanonical(layer, name)` (SP-3.1) — egy layer azon aktív scope-jai, amelyek
|
|
52
|
+
* canonical-neve NORMALIZÁLT-egyezik a megadott névvel. A Mongo-szűrés a layerre megy
|
|
53
|
+
* (nincs normalizált-mező a sémában), a normalizált-egyezést memóriában szűrjük (a scope-halmaz
|
|
54
|
+
* kicsi). A `parentScopeId` opcionális szűkítés (azonos név más ágon külön entitás lehet).
|
|
55
|
+
*/
|
|
56
|
+
async findByCanonical(layer, name, parentScopeId) {
|
|
57
|
+
const layerScopes = await this.findByLayer(layer);
|
|
58
|
+
const targetKey = fam_scope_normalize_util_1.FAM_ScopeNormalize_Util.normalize(name);
|
|
59
|
+
return layerScopes.filter((scope) => {
|
|
60
|
+
const nameMatch = fam_scope_normalize_util_1.FAM_ScopeNormalize_Util.normalize(scope.canonicalName) === targetKey;
|
|
61
|
+
const parentMatch = parentScopeId === undefined || scope.parentScopeId === parentScopeId;
|
|
62
|
+
return nameMatch && parentMatch;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Egy scope közvetlen (aktív) gyerekei (a subtree-traverzál egy lépése). */
|
|
66
|
+
async findChildren(parentScopeId) {
|
|
67
|
+
return this.findDataList({ parentScopeId: parentScopeId }, true);
|
|
68
|
+
}
|
|
69
|
+
/** Minden aktív scope (a scope-fa in-memory bejárásához; a halmaz kicsi). */
|
|
70
|
+
async findAllActive() {
|
|
71
|
+
return this.findDataList({}, true);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* `listScopes` (SP-3.1) — lapozott scope-lista az UI/CLI böngészéshez. A `pageSize` a
|
|
75
|
+
* `MAX_PAGE_SIZE`-ra kapozott; a lapozás memóriában (a scope-halmaz kicsi, a determinizmus a
|
|
76
|
+
* fontos, nem a DB-paging). `layer`/`parentScopeId` opcionális szűrés.
|
|
77
|
+
*/
|
|
78
|
+
async listScopes(input) {
|
|
79
|
+
const filter = {};
|
|
80
|
+
if (input.layer) {
|
|
81
|
+
filter.layer = input.layer;
|
|
82
|
+
}
|
|
83
|
+
if (input.parentScopeId) {
|
|
84
|
+
filter.parentScopeId = input.parentScopeId;
|
|
85
|
+
}
|
|
86
|
+
const all = await this.findDataList(filter, true);
|
|
87
|
+
const pageSize = Math.min(input.pageSize ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
|
|
88
|
+
const page = input.page && input.page > 0 ? input.page : 1;
|
|
89
|
+
const start = (page - 1) * pageSize;
|
|
90
|
+
return all.slice(start, start + pageSize);
|
|
91
|
+
}
|
|
92
|
+
// =========================================================================
|
|
93
|
+
// SCOPE-FA TRAVERZÁL (dsgn-002 §2/§6, SP-3.1)
|
|
94
|
+
// =========================================================================
|
|
95
|
+
/**
|
|
96
|
+
* `buildScopePath(scopeId)` (SP-3.1) — parent-lánc traverzál: a `parentScopeId`-n FELFELÉ a
|
|
97
|
+
* gyökérig, majd root→leaf rendezve `FAM_ScopeRef[]`-re map-elve. Ciklus-/mélység-védelem:
|
|
98
|
+
* `MAX_SCOPE_DEPTH` lépés VAGY már-látott scopeId → `FAM-REF-INDEX-001` (NEM végtelen ciklus).
|
|
99
|
+
* Hiányzó/törölt scope a láncban → a lánc ott megszakad (a meglévő prefix visszaadva).
|
|
100
|
+
*/
|
|
101
|
+
async buildScopePath(scopeId) {
|
|
102
|
+
const chain = [];
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
let currentId = scopeId;
|
|
105
|
+
let depth = 0;
|
|
106
|
+
while (currentId) {
|
|
107
|
+
if (seen.has(currentId)) {
|
|
108
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
109
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.refIndexRebuild,
|
|
110
|
+
message: `Ciklikus parent-referencia a scope-fában (scopeId '${currentId}' már szerepelt a láncban). `
|
|
111
|
+
+ 'A scope-fa hibás — a parent-lánc nem zárható le.',
|
|
112
|
+
issuer: this.issuer,
|
|
113
|
+
context: { operation: 'build-scope-path' },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (depth >= MAX_SCOPE_DEPTH) {
|
|
117
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
118
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.refIndexRebuild,
|
|
119
|
+
message: `A scope parent-lánc meghaladta a maximális mélységet (${MAX_SCOPE_DEPTH}) — `
|
|
120
|
+
+ 'valószínűleg hibás parent-referencia. A lánc nem zárható le.',
|
|
121
|
+
issuer: this.issuer,
|
|
122
|
+
context: { operation: 'build-scope-path' },
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
seen.add(currentId);
|
|
126
|
+
depth++;
|
|
127
|
+
const scope = await this.findScopeById(currentId);
|
|
128
|
+
if (!scope || !scope._id) {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
// A láncot leaf→root építjük, a végén megfordítjuk root→leaf-re.
|
|
132
|
+
chain.push({
|
|
133
|
+
scopeId: scope._id,
|
|
134
|
+
layer: scope.layer ?? '',
|
|
135
|
+
canonicalName: scope.canonicalName ?? '',
|
|
136
|
+
});
|
|
137
|
+
currentId = scope.parentScopeId;
|
|
138
|
+
}
|
|
139
|
+
return chain.reverse();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* `getSubtreeScopeIds(scopeId)` (SP-3.1) — a megadott scope + ÖSSZES (aktív) leszármazott
|
|
143
|
+
* `scopeId`-ja (subtree-match, dsgn-002 §3.3). Egyetlen aktív-snapshot olvasás + in-memory
|
|
144
|
+
* parent→children BFS (nincs N+1 query; a scope-halmaz kicsi). A testvér-ágakat NEM tartalmazza.
|
|
145
|
+
* Ciklus-védelem: már-látott scopeId-t nem jár be újra.
|
|
146
|
+
*/
|
|
147
|
+
async getSubtreeScopeIds(scopeId) {
|
|
148
|
+
const all = await this.findAllActive();
|
|
149
|
+
// parent → gyerek-ID-k index (egyetlen passz; in-memory fa).
|
|
150
|
+
const childrenByParent = new Map();
|
|
151
|
+
for (const scope of all) {
|
|
152
|
+
if (!scope._id) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const parent = scope.parentScopeId ?? '';
|
|
156
|
+
const bucket = childrenByParent.get(parent) ?? [];
|
|
157
|
+
bucket.push(scope._id);
|
|
158
|
+
childrenByParent.set(parent, bucket);
|
|
159
|
+
}
|
|
160
|
+
const result = [];
|
|
161
|
+
const seen = new Set();
|
|
162
|
+
const queue = [scopeId];
|
|
163
|
+
while (queue.length) {
|
|
164
|
+
const currentId = queue.shift();
|
|
165
|
+
if (seen.has(currentId)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
seen.add(currentId);
|
|
169
|
+
result.push(currentId);
|
|
170
|
+
const children = childrenByParent.get(currentId) ?? [];
|
|
171
|
+
for (const childId of children) {
|
|
172
|
+
if (!seen.has(childId)) {
|
|
173
|
+
queue.push(childId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
// =========================================================================
|
|
180
|
+
// DETERMINISZTIKUS DE-DUP (dsgn-002 §7.6, SP-3.1)
|
|
181
|
+
// =========================================================================
|
|
182
|
+
/**
|
|
183
|
+
* `findOrCreateScope` (SP-3.1) — determinisztikus scope-de-dup. Ha a (normalizált
|
|
184
|
+
* `canonicalName` + `layer` + `parentScopeId`) hármas már létezik AKTÍVAN → a meglévő `scopeId`;
|
|
185
|
+
* KÜLÖNBEN új entitás (`createdBy` a bemenetből). Ugyanazzal a hármassal kétszer hívva UGYANAZT
|
|
186
|
+
* a `scopeId`-t adja (nincs duplikált fa-csúcs — dsgn-002 §7.6). Az `_id`-t a Dynamo `saveData`
|
|
187
|
+
* generálja (a `find` MIELŐTT — ezért determinisztikus a feloldás, nem az `_id` maga).
|
|
188
|
+
*/
|
|
189
|
+
async findOrCreateScope(input) {
|
|
190
|
+
const existing = await this.findByCanonical(input.layer, input.canonicalName, input.parentScopeId);
|
|
191
|
+
if (existing.length && existing[0]._id) {
|
|
192
|
+
return { scope: existing[0], created: false };
|
|
193
|
+
}
|
|
194
|
+
const record = new fam_scope_data_model_1.FAM_Scope_DataModel({
|
|
195
|
+
layer: input.layer,
|
|
196
|
+
canonicalName: input.canonicalName,
|
|
197
|
+
parentScopeId: input.parentScopeId,
|
|
198
|
+
description: input.description,
|
|
199
|
+
createdBy: input.createdBy,
|
|
200
|
+
aliases: [],
|
|
201
|
+
});
|
|
202
|
+
const writeService = new FAM_Scope_DataService({ data: record, issuer: this.issuer });
|
|
203
|
+
const saved = await writeService.saveData(record);
|
|
204
|
+
return { scope: saved, created: true };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Egy scope `aliases[]` denormalizált gyors-cache frissítése (`$set`, atomikus — a Mixed-szerű
|
|
208
|
+
* `string[]` silent-drop ellen; memory: reference_mongoose_mixed_atomic_write). A forrás a
|
|
209
|
+
* `fam_reference` (SP-3.2/SP-3.3 töltheti). NEM a teljes instance-en `saveData`-val.
|
|
210
|
+
*/
|
|
211
|
+
async setAliases(scopeId, aliases) {
|
|
212
|
+
await this.updateData({ filterBy: { _id: scopeId }, update: { $set: { aliases: aliases } } });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
exports.FAM_Scope_DataService = FAM_Scope_DataService;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `scope-reference` modul barrel (MP-3, dsgn-002) — a scope/reference feloldó-engine publikus
|
|
4
|
+
* felülete. A fogyasztó-MP-k innen importálnak: a WRITE-path (MP-6) a `resolveForWrite`-ot, a
|
|
5
|
+
* READ prefilter (MP-5) az `expandForRead`-et, a `rebuild_reference_index`/`trace_*` capability
|
|
6
|
+
* (MP-6) a `rebuildReferenceIndex`/`traceResolution`-t, az MP-7 config-set a `scopeExists`-et hívja.
|
|
7
|
+
*
|
|
8
|
+
* Boundary (dsgn-002 §6): MP-3 = a feloldó-ENGINE; a write/read wiring + capability-regisztráció
|
|
9
|
+
* a fogyasztó-MP-ké. A reference/scope collection-ök MP-1-ben már a `dbModels`-ben vannak.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.FAM_FuzzyMatch_Util = exports.FAM_ScopeNormalize_Util = exports.FAM_ScopeResolver_ControlService = exports.FAM_Reference_DataService = exports.FAM_Scope_DataService = void 0;
|
|
13
|
+
// SP-3.1 — Scope CRUD + scope-fa traverzál
|
|
14
|
+
var fam_scope_data_service_1 = require("./_services/fam-scope.data-service");
|
|
15
|
+
Object.defineProperty(exports, "FAM_Scope_DataService", { enumerable: true, get: function () { return fam_scope_data_service_1.FAM_Scope_DataService; } });
|
|
16
|
+
// SP-3.2 — Reference CRUD + exact/string-fuzzy/vektor-fuzzy
|
|
17
|
+
var fam_reference_data_service_1 = require("./_services/fam-reference.data-service");
|
|
18
|
+
Object.defineProperty(exports, "FAM_Reference_DataService", { enumerable: true, get: function () { return fam_reference_data_service_1.FAM_Reference_DataService; } });
|
|
19
|
+
// SP-3.3/SP-3.4 — a feloldó control-service (write-resolve / read-expand / cache / scopeExists / uncertainty)
|
|
20
|
+
var fam_scope_resolver_control_service_1 = require("./_services/fam-scope-resolver.control-service");
|
|
21
|
+
Object.defineProperty(exports, "FAM_ScopeResolver_ControlService", { enumerable: true, get: function () { return fam_scope_resolver_control_service_1.FAM_ScopeResolver_ControlService; } });
|
|
22
|
+
// utils (SP-3.1/SP-3.2)
|
|
23
|
+
var fam_scope_normalize_util_1 = require("./_collections/fam-scope-normalize.util");
|
|
24
|
+
Object.defineProperty(exports, "FAM_ScopeNormalize_Util", { enumerable: true, get: function () { return fam_scope_normalize_util_1.FAM_ScopeNormalize_Util; } });
|
|
25
|
+
var fam_fuzzy_match_util_1 = require("./_collections/fam-fuzzy-match.util");
|
|
26
|
+
Object.defineProperty(exports, "FAM_FuzzyMatch_Util", { enumerable: true, get: function () { return fam_fuzzy_match_util_1.FAM_FuzzyMatch_Util; } });
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Api_Controller = void 0;
|
|
4
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
5
|
+
const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
|
|
6
|
+
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
7
|
+
const fam_config_level_type_enum_1 = require("../../../_enums/fam-config-level.type-enum");
|
|
8
|
+
const config_control_service_1 = require("../config/config.control-service");
|
|
9
|
+
const embedding_1 = require("../../../_modules/embedding");
|
|
10
|
+
const ingest_1 = require("../../../_modules/ingest");
|
|
11
|
+
const mcp_1 = require("../../../_modules/mcp");
|
|
12
|
+
/** A UI/CLI lapozás CCAP-örökség konstansai (dsgn-010/dsgn-011 §4.1). */
|
|
13
|
+
const DEFAULT_PAGE_SIZE = 50;
|
|
14
|
+
const MAX_PAGE_SIZE = 500;
|
|
15
|
+
/** A 6 fő RAG-tár (a `/stats` + `/items` REST-hez). */
|
|
16
|
+
const MAIN_TABLES = [
|
|
17
|
+
fam_table_type_enum_1.FAM_Table.rules, fam_table_type_enum_1.FAM_Table.documents, fam_table_type_enum_1.FAM_Table.codebase,
|
|
18
|
+
fam_table_type_enum_1.FAM_Table.knowledge, fam_table_type_enum_1.FAM_Table.codingPatterns, fam_table_type_enum_1.FAM_Table.memory,
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* `Api_Controller` (SP-6.5, dsgn-010 §9 / dsgn-011 §2) — a CLI (MP-9) + UI (MP-10) közös szerver-
|
|
22
|
+
* belépési pontja: a 3 core-tool **REST-tükre** + a UI-specifikus listázó/stat/ingest endpoint-ok.
|
|
23
|
+
* A REST-handler UGYANAZT a transport-agnosztikus core-service-t hívja, mint az MCP-réteg
|
|
24
|
+
* (`FAM_ReadTool_Service`/`FAM_WriteTool_Service`/`FAM_CapabilitiesTool_Service`) — paritás-garancia,
|
|
25
|
+
* no-duplication (dsgn-003 §6.5). No-auth, lokál, port 39185 (ADR-004).
|
|
26
|
+
*
|
|
27
|
+
* **Endpoint-felület (a kód-szintű kontraktus — a formális `__documentations/server-api.md` follow-up;
|
|
28
|
+
* dsgn-013 user-jóváhagyásra vár):** lásd a modul `README.md`-jét.
|
|
29
|
+
* POST /read · POST /write · POST /capabilities — a dsgn-003 §2/§3/§4 mirror
|
|
30
|
+
* GET /health — CLI remote reachability-check (dsgn-010 §9)
|
|
31
|
+
* GET /items/:table — lapozható/szűrhető elem-lista (dsgn-011 §4.1)
|
|
32
|
+
* GET /stats · GET /stats/:table — high-level stats aggregátum (dsgn-011 §3/§4.4)
|
|
33
|
+
* GET /ingest-runs — ingest-run idővonal (dsgn-011 §7)
|
|
34
|
+
*
|
|
35
|
+
* **Hiba-fordítás (dsgn-008, néma hiba tilos):** minden handler-hiba strukturált REST-hibatestté
|
|
36
|
+
* (`{ ok:false, error:{ errorCode, message } }`) fordul, megfelelő HTTP-státusszal — a UI banner /
|
|
37
|
+
* CLI exit-kód köthető. Singleton: `getInstance()` → `getSingletonInstance()`.
|
|
38
|
+
*/
|
|
39
|
+
class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
|
|
40
|
+
static getInstance() {
|
|
41
|
+
return Api_Controller.getSingletonInstance();
|
|
42
|
+
}
|
|
43
|
+
/** Default issuer (a REST-réteg hibáihoz; dsgn-008). */
|
|
44
|
+
issuer = 'Api_Controller';
|
|
45
|
+
setupEndpoints() {
|
|
46
|
+
this.endpoints = [
|
|
47
|
+
this.rootRedirectEndpoint(),
|
|
48
|
+
this.readEndpoint(),
|
|
49
|
+
this.writeEndpoint(),
|
|
50
|
+
this.capabilitiesEndpoint(),
|
|
51
|
+
this.healthEndpoint(),
|
|
52
|
+
this.itemsEndpoint(),
|
|
53
|
+
this.statsEndpoint(),
|
|
54
|
+
this.statsTableEndpoint(),
|
|
55
|
+
this.ingestRunsEndpoint(),
|
|
56
|
+
this.configGetEndpoint(),
|
|
57
|
+
this.configSetEndpoint(),
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
// =========================================================================
|
|
61
|
+
// / (→ /api/) — az API-gyökér átirányít a beépített admin-UI-ra
|
|
62
|
+
// =========================================================================
|
|
63
|
+
/** `GET /api/` — a felhasználót a beépített Angular admin-UI-ra (`/api/app/`) irányítja (UX). */
|
|
64
|
+
rootRedirectEndpoint() {
|
|
65
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
66
|
+
name: 'rootRedirect',
|
|
67
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
68
|
+
endpoint: '/',
|
|
69
|
+
tasks: [
|
|
70
|
+
async (_req, res) => {
|
|
71
|
+
res.redirect(302, '/api/app/');
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// =========================================================================
|
|
77
|
+
// core-mirror: /read · /write · /capabilities (dsgn-003 §2/§3/§4)
|
|
78
|
+
// =========================================================================
|
|
79
|
+
/** `POST /read` — a dsgn-003 §2 input → §2.2 output (a MCP `read` tool REST-tükre). */
|
|
80
|
+
readEndpoint() {
|
|
81
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
82
|
+
name: 'read',
|
|
83
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.post,
|
|
84
|
+
endpoint: '/read',
|
|
85
|
+
tasks: [
|
|
86
|
+
async (req, res) => {
|
|
87
|
+
await this.run(res, async () => {
|
|
88
|
+
const input = (req.body ?? {});
|
|
89
|
+
return mcp_1.FAM_ReadTool_Service.getInstance().handle(input);
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** `POST /write` — a dsgn-003 §3 input → §3.2 output (a MCP `write` tool REST-tükre). */
|
|
96
|
+
writeEndpoint() {
|
|
97
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
98
|
+
name: 'write',
|
|
99
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.post,
|
|
100
|
+
endpoint: '/write',
|
|
101
|
+
tasks: [
|
|
102
|
+
async (req, res) => {
|
|
103
|
+
await this.run(res, async () => {
|
|
104
|
+
const input = (req.body ?? {});
|
|
105
|
+
return mcp_1.FAM_WriteTool_Service.getInstance().handle(input);
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** `POST /capabilities` — a dsgn-003 §4 action input → output (list/describe/invoke REST-tükre). */
|
|
112
|
+
capabilitiesEndpoint() {
|
|
113
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
114
|
+
name: 'capabilities',
|
|
115
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.post,
|
|
116
|
+
endpoint: '/capabilities',
|
|
117
|
+
tasks: [
|
|
118
|
+
async (req, res) => {
|
|
119
|
+
await this.run(res, async () => {
|
|
120
|
+
const input = (req.body ?? {});
|
|
121
|
+
return mcp_1.FAM_CapabilitiesTool_Service.getInstance().handle(input);
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// =========================================================================
|
|
128
|
+
// /health — CLI remote reachability-check (dsgn-010 §9)
|
|
129
|
+
// =========================================================================
|
|
130
|
+
/** `GET /health` — a CLI `--server-url` elérhetőség-próbája (dsgn-010 §9). */
|
|
131
|
+
healthEndpoint() {
|
|
132
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
133
|
+
name: 'health',
|
|
134
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
135
|
+
endpoint: '/health',
|
|
136
|
+
tasks: [
|
|
137
|
+
async (req, res) => {
|
|
138
|
+
res.send({ ok: true, service: 'fdp-agent-memory', uptime: process.uptime() });
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// =========================================================================
|
|
144
|
+
// /items/:table — lapozható/szűrhető elem-lista (dsgn-011 §4.1)
|
|
145
|
+
// =========================================================================
|
|
146
|
+
/**
|
|
147
|
+
* `GET /items/:table` — egy tár lapozható/szűrhető elem-listája (a UI table-detail; dsgn-011 §4.1).
|
|
148
|
+
* Query: `page`/`pageSize` (cap 500), `tag`/`kind`/`ingestRunId` szűrő. A scope-szűrés a UI-n a
|
|
149
|
+
* `read`/capability-n megy (itt sima substring/mező-szűrő, NEM vektor).
|
|
150
|
+
*/
|
|
151
|
+
itemsEndpoint() {
|
|
152
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
153
|
+
name: 'items',
|
|
154
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
155
|
+
endpoint: '/items/:table',
|
|
156
|
+
tasks: [
|
|
157
|
+
async (req, res) => {
|
|
158
|
+
await this.run(res, async () => this.listItems(req));
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** A `/items/:table` adat-oldali lekérdezése (lapozott + szűrt). */
|
|
164
|
+
async listItems(req) {
|
|
165
|
+
const table = this.parseTable(req.params.table);
|
|
166
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(table);
|
|
167
|
+
if (!registryEntry) {
|
|
168
|
+
throw new fsm_dynamo_1.DyFM_Error({
|
|
169
|
+
errorCode: 'FAM-VAL-READ-001',
|
|
170
|
+
message: `A(z) '${req.params.table}' nem fő RAG-tár (a 6 fő tár egyike szükséges).`,
|
|
171
|
+
issuer: this.issuer,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const filter = {};
|
|
175
|
+
if (req.query.kind) {
|
|
176
|
+
filter.kind = String(req.query.kind);
|
|
177
|
+
}
|
|
178
|
+
if (req.query.tag) {
|
|
179
|
+
filter.tags = String(req.query.tag);
|
|
180
|
+
}
|
|
181
|
+
if (req.query.ingestRunId) {
|
|
182
|
+
filter.ingestRunId = String(req.query.ingestRunId);
|
|
183
|
+
}
|
|
184
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
185
|
+
const all = await dataService.findHydratableList(filter);
|
|
186
|
+
const pageSize = Math.min(this.parseNumber(req.query.pageSize, DEFAULT_PAGE_SIZE), MAX_PAGE_SIZE);
|
|
187
|
+
const page = Math.max(this.parseNumber(req.query.page, 1), 1);
|
|
188
|
+
const start = (page - 1) * pageSize;
|
|
189
|
+
const items = all.slice(start, start + pageSize);
|
|
190
|
+
return { table: table, total: all.length, page: page, pageSize: pageSize, items: items };
|
|
191
|
+
}
|
|
192
|
+
// =========================================================================
|
|
193
|
+
// /stats · /stats/:table — high-level stats aggregátum (dsgn-011 §3/§4.4)
|
|
194
|
+
// =========================================================================
|
|
195
|
+
/** `GET /stats` — minden fő tár stats-aggregátuma (a UI landing; dsgn-011 §3). */
|
|
196
|
+
statsEndpoint() {
|
|
197
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
198
|
+
name: 'stats',
|
|
199
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
200
|
+
endpoint: '/stats',
|
|
201
|
+
tasks: [
|
|
202
|
+
async (req, res) => {
|
|
203
|
+
await this.run(res, async () => ({ tables: await this.collectTableStats(MAIN_TABLES) }));
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** `GET /stats/:table` — egy tár részletes stats-a (a UI table-detail; dsgn-011 §4.4). */
|
|
209
|
+
statsTableEndpoint() {
|
|
210
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
211
|
+
name: 'statsTable',
|
|
212
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
213
|
+
endpoint: '/stats/:table',
|
|
214
|
+
tasks: [
|
|
215
|
+
async (req, res) => {
|
|
216
|
+
await this.run(res, async () => {
|
|
217
|
+
const table = this.parseTable(req.params.table);
|
|
218
|
+
const stats = await this.collectTableStats([table]);
|
|
219
|
+
return stats[0] ?? { table: table, count: 0 };
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/** A tár-stats aggregátum (aktív elem-szám + embedding-státusz + utolsó ingest) — dsgn-011 §3/§4.4. */
|
|
226
|
+
async collectTableStats(tables) {
|
|
227
|
+
const result = [];
|
|
228
|
+
for (const table of tables) {
|
|
229
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(table);
|
|
230
|
+
if (!registryEntry) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
234
|
+
const entries = await dataService.findHydratableList({});
|
|
235
|
+
const byStatus = { pending: 0, completed: 0, error: 0 };
|
|
236
|
+
let lastModifiedMs = 0;
|
|
237
|
+
for (const entry of entries) {
|
|
238
|
+
const status = entry.embeddingStatus ?? 'pending';
|
|
239
|
+
byStatus[status] = (byStatus[status] ?? 0) + 1;
|
|
240
|
+
const modified = entry.__lastModified ? new Date(entry.__lastModified).getTime() : 0;
|
|
241
|
+
if (modified > lastModifiedMs) {
|
|
242
|
+
lastModifiedMs = modified;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
result.push({
|
|
246
|
+
table: table,
|
|
247
|
+
count: entries.length,
|
|
248
|
+
embeddingStatus: byStatus,
|
|
249
|
+
lastModified: lastModifiedMs ? new Date(lastModifiedMs).toISOString() : null,
|
|
250
|
+
status: entries.length ? 'ok' : 'empty',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
// =========================================================================
|
|
256
|
+
// /ingest-runs — ingest-run idővonal (dsgn-011 §7)
|
|
257
|
+
// =========================================================================
|
|
258
|
+
/** `GET /ingest-runs` — az ingest-run-ok listája (a UI idővonal; `?table=` szűrővel). */
|
|
259
|
+
ingestRunsEndpoint() {
|
|
260
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
261
|
+
name: 'ingestRuns',
|
|
262
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
263
|
+
endpoint: '/ingest-runs',
|
|
264
|
+
tasks: [
|
|
265
|
+
async (req, res) => {
|
|
266
|
+
await this.run(res, async () => {
|
|
267
|
+
const runDataService = new ingest_1.FAM_IngestRun_DataService({ issuer: this.issuer });
|
|
268
|
+
const runs = await runDataService.listRuns();
|
|
269
|
+
const tableFilter = req.query.table ? String(req.query.table) : undefined;
|
|
270
|
+
const filtered = tableFilter
|
|
271
|
+
? runs.filter((run) => this.runHasTable(run.table, tableFilter))
|
|
272
|
+
: runs;
|
|
273
|
+
return { runs: filtered };
|
|
274
|
+
});
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/** Egy run érinti-e az adott tárat (a `table` lehet egyetlen érték vagy tömb). */
|
|
280
|
+
runHasTable(runTable, table) {
|
|
281
|
+
if (!runTable) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
return Array.isArray(runTable) ? runTable.some((entry) => String(entry) === table) : String(runTable) === table;
|
|
285
|
+
}
|
|
286
|
+
// =========================================================================
|
|
287
|
+
// /config — scoped config olvasás/írás (dsgn-007; a UI settings + CLI config)
|
|
288
|
+
// =========================================================================
|
|
289
|
+
/**
|
|
290
|
+
* `GET /config` — a teljes effektív (merge-elt) config az adott kontextusra (`?table=`),
|
|
291
|
+
* a precedencia-forrással (global<table<scope; dsgn-007 §3). A `list_*`/config-capability REST-tükre.
|
|
292
|
+
*/
|
|
293
|
+
configGetEndpoint() {
|
|
294
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
295
|
+
name: 'configGet',
|
|
296
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.get,
|
|
297
|
+
endpoint: '/config',
|
|
298
|
+
tasks: [
|
|
299
|
+
async (req, res) => {
|
|
300
|
+
await this.run(res, async () => {
|
|
301
|
+
const table = req.query.table
|
|
302
|
+
? this.parseTable(String(req.query.table))
|
|
303
|
+
: undefined;
|
|
304
|
+
const effective = await config_control_service_1.FAM_Config_ControlService.getInstance().resolveAll({ table: table });
|
|
305
|
+
return { table: table, effective: effective };
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* `PUT /config` — egy config-érték beállítása egy szinten (dsgn-007 §7). Body: `{ key, value,
|
|
313
|
+
* level?, table?, scopeId? }`. A `set` validál + soft-delete-eli a régit + invalidál (a UI settings
|
|
314
|
+
* + CLI `config set` REST-tükre). A validációs hiba strukturált REST-hibatest (dsgn-008).
|
|
315
|
+
*/
|
|
316
|
+
configSetEndpoint() {
|
|
317
|
+
return new nts_dynamo_1.DyNTS_Endpoint_Params({
|
|
318
|
+
name: 'configSet',
|
|
319
|
+
type: fsm_dynamo_1.DyFM_HttpCallType.put,
|
|
320
|
+
endpoint: '/config',
|
|
321
|
+
tasks: [
|
|
322
|
+
async (req, res) => {
|
|
323
|
+
await this.run(res, async () => this.setConfig(req));
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
/** A `PUT /config` body-feldolgozása + `set` (dsgn-007 §7). */
|
|
329
|
+
async setConfig(req) {
|
|
330
|
+
const body = (req.body ?? {});
|
|
331
|
+
if (!body.key) {
|
|
332
|
+
throw new fsm_dynamo_1.DyFM_Error({
|
|
333
|
+
errorCode: 'FAM-VAL-WRITE-001',
|
|
334
|
+
message: 'A config-set `key`-t igényel (a CONFIG_CATALOG dotted kulcsa).',
|
|
335
|
+
issuer: this.issuer,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const level = body.scopeId
|
|
339
|
+
? fam_config_level_type_enum_1.FAM_ConfigLevel.scope
|
|
340
|
+
: body.table
|
|
341
|
+
? fam_config_level_type_enum_1.FAM_ConfigLevel.table
|
|
342
|
+
: fam_config_level_type_enum_1.FAM_ConfigLevel.global;
|
|
343
|
+
const options = {
|
|
344
|
+
level: level,
|
|
345
|
+
tableScope: body.table ? this.parseTable(body.table) : undefined,
|
|
346
|
+
scopeId: body.scopeId,
|
|
347
|
+
setBy: 'ui',
|
|
348
|
+
};
|
|
349
|
+
const saved = await config_control_service_1.FAM_Config_ControlService.getInstance()
|
|
350
|
+
.set(body.key, body.value, options);
|
|
351
|
+
return { ok: true, key: body.key, level: level, saved: saved };
|
|
352
|
+
}
|
|
353
|
+
// =========================================================================
|
|
354
|
+
// helpers — egységes hiba-fordítás (dsgn-008, néma hiba tilos)
|
|
355
|
+
// =========================================================================
|
|
356
|
+
/**
|
|
357
|
+
* Egy handler futtatása + egységes REST-hiba-fordítás (dsgn-008). A `DyFM_Error` → strukturált
|
|
358
|
+
* hibatest (`{ ok:false, error:{ errorCode, message } }`) HTTP 400-zal (user-level) / 500-zal;
|
|
359
|
+
* a sikeres eredmény nyers JSON-ként. A `globalErrorHandler` (App) a hibát persistálja is.
|
|
360
|
+
*/
|
|
361
|
+
async run(res, handler) {
|
|
362
|
+
try {
|
|
363
|
+
const data = await handler();
|
|
364
|
+
res.send(data);
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
const errorCode = error instanceof fsm_dynamo_1.DyFM_Error
|
|
368
|
+
? (fsm_dynamo_1.DyFM_Error.getErrorCode(error) ?? 'FAM-MCP-DISPATCH-001')
|
|
369
|
+
: 'FAM-MCP-DISPATCH-001';
|
|
370
|
+
const message = error instanceof fsm_dynamo_1.DyFM_Error
|
|
371
|
+
? (fsm_dynamo_1.DyFM_Error.getErrorMessage(error) || 'Ismeretlen FAM hiba.')
|
|
372
|
+
: (error instanceof Error ? error.message : 'Ismeretlen hiba.');
|
|
373
|
+
const status = this.httpStatusForCode(errorCode);
|
|
374
|
+
res.status(status).send({ ok: false, error: { errorCode: errorCode, message: message } });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/** A FAM hibakód → HTTP-státusz (a user/validation-hibák 400, a többi 500; dsgn-008). */
|
|
378
|
+
httpStatusForCode(errorCode) {
|
|
379
|
+
const userPrefixes = ['FAM-VAL-', 'FAM-REF-RESOLVE-', 'FAM-SCOPE-WRITE-', 'FAM-MCP-DISPATCH-'];
|
|
380
|
+
return userPrefixes.some((prefix) => errorCode.startsWith(prefix)) ? 400 : 500;
|
|
381
|
+
}
|
|
382
|
+
/** A `:table` path-param → `FAM_Table` (ismeretlen → hiba). */
|
|
383
|
+
parseTable(raw) {
|
|
384
|
+
const match = Object.values(fam_table_type_enum_1.FAM_Table).find((table) => table === raw);
|
|
385
|
+
if (!match) {
|
|
386
|
+
throw new fsm_dynamo_1.DyFM_Error({
|
|
387
|
+
errorCode: 'FAM-VAL-READ-001',
|
|
388
|
+
message: `Ismeretlen tár: '${raw}'. Megengedett: ${Object.values(fam_table_type_enum_1.FAM_Table).join(', ')}.`,
|
|
389
|
+
issuer: this.issuer,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return match;
|
|
393
|
+
}
|
|
394
|
+
/** Egy query-paraméter szám-ként (vagy a fallback). */
|
|
395
|
+
parseNumber(value, fallback) {
|
|
396
|
+
const parsed = Number(value);
|
|
397
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
exports.Api_Controller = Api_Controller;
|