@futdevpro/fdp-agent-memory 0.1.0 → 1.1.14
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/LICENSE +21 -0
- package/README.md +7 -7
- package/build/package.json +6 -5
- package/build/src/_cli/_collections/fam-arg.util.js +48 -0
- package/build/src/_cli/_collections/fam-cli.const.js +40 -0
- package/build/src/_cli/_collections/fam-output.util.js +86 -0
- package/build/src/_cli/_collections/fam-project-discovery.util.js +98 -0
- package/build/src/_cli/_commands/capture.command.js +73 -0
- package/build/src/_cli/_commands/config.command.js +93 -0
- package/build/src/_cli/_commands/doctor.command.js +124 -0
- package/build/src/_cli/_commands/errors.command.js +66 -0
- package/build/src/_cli/_commands/export.command.js +65 -0
- package/build/src/_cli/_commands/find-duplicates.command.js +97 -0
- package/build/src/_cli/_commands/import.command.js +136 -0
- package/build/src/_cli/_commands/init.command.js +147 -0
- package/build/src/_cli/_commands/read.command.js +109 -0
- package/build/src/_cli/_commands/scan-projects.command.js +138 -0
- package/build/src/_cli/_commands/scan.command.js +98 -0
- package/build/src/_cli/_commands/seed.command.js +40 -0
- package/build/src/_cli/_commands/serve.command.js +373 -0
- package/build/src/_cli/_commands/start.command.js +134 -0
- package/build/src/_cli/_commands/stats.command.js +54 -0
- package/build/src/_cli/_commands/write.command.js +103 -0
- package/build/src/_cli/_models/interfaces/fam-cli-global-options.interface.js +2 -0
- package/build/src/_cli/_models/interfaces/fam-cli-output.interface.js +9 -0
- package/build/src/_cli/_models/interfaces/fam-client-result.interface.js +2 -0
- package/build/src/_cli/_services/fam-client.service.js +140 -0
- package/build/src/_cli/register-commands.js +86 -0
- package/build/src/_collections/config-catalog.const.js +67 -1
- package/build/src/_collections/fam-console.util.js +367 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +158 -4
- package/build/src/_collections/fam-error-factory.util.js +0 -9
- package/build/src/_collections/fam-mcp-bridge.util.js +49 -0
- package/build/src/_collections/fam-reference-code.util.js +105 -0
- package/build/src/_collections/fam-version.const.js +10 -0
- package/build/src/_models/data-models/fam-entry-base-properties.const.js +1 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
- package/build/src/_models/data-models/fam-ingest-run.data-model.js +3 -1
- package/build/src/_models/data-models/fam-reference.data-model.js +7 -0
- package/build/src/_modules/capture/_collections/fam-capture.const.js +11 -0
- package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +87 -0
- package/build/src/_modules/capture/index.js +8 -0
- package/build/src/_modules/embedding/_collections/fam-embedding-prefix.util.js +77 -0
- package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +202 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +33 -9
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +21 -2
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +135 -0
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +42 -32
- package/build/src/_modules/embedding/index.js +4 -1
- package/build/src/_modules/export/_collections/fam-export.const.js +22 -0
- package/build/src/_modules/export/_services/fam-export.control-service.js +64 -0
- package/build/src/_modules/export/index.js +8 -0
- package/build/src/_modules/ingest/_collections/fam-famignore.util.js +83 -0
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +59 -48
- package/build/src/_modules/ingest/_collections/fam-git-repo.util.js +193 -0
- package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +134 -0
- package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +57 -0
- package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +60 -0
- package/build/src/_modules/ingest/_collections/fam-scan-weight.util.js +53 -0
- package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +37 -14
- package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +34 -0
- package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +200 -14
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +4 -1
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +7 -4
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +349 -17
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +25 -2
- package/build/src/_modules/ingest/index.js +3 -1
- package/build/src/_modules/mcp/_collections/fam-active-rules.util.js +56 -0
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +47 -6
- package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +224 -18
- package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +53 -1
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +104 -8
- package/build/src/_modules/mcp/index.js +4 -4
- package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +66 -3
- package/build/src/_modules/migration/_collections/fam-prompt-aggregate.util.js +143 -0
- package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +19 -0
- package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +6 -0
- package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +5 -0
- package/build/src/_modules/migration/_services/fam-agent-memory-reader.service.js +125 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +101 -18
- package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +53 -0
- package/build/src/_modules/migration/index.js +3 -1
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +78 -4
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +293 -50
- package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +6 -3
- package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +18 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +79 -20
- package/build/src/_routes/server/api/api.controller.js +34 -2
- package/build/src/_routes/server/client-app/client-app.control-service.js +1 -1
- package/build/src/_routes/server/server-status/server-status.controller.js +2 -1
- package/build/src/app.server.js +13 -1
- package/build/src/environments/environment.js +1 -1
- package/build/src/index.js +1 -1
- package/client-dist/{chunk-GHKRM4SM.js → chunk-I77GXVAQ.js} +1 -1
- package/client-dist/{chunk-LMTL7GA3.js → chunk-YXHWCJ5O.js} +1 -1
- package/client-dist/index.html +1 -1
- package/client-dist/{main-2KWB3QYK.js → main-PJPEDVJT.js} +1 -1
- package/package.json +6 -5
|
@@ -5,6 +5,7 @@ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
|
5
5
|
const error_codes_const_1 = require("../../../_collections/error-codes.const");
|
|
6
6
|
const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
|
|
7
7
|
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
8
|
+
const fam_embedding_prefix_util_1 = require("../_collections/fam-embedding-prefix.util");
|
|
8
9
|
const fam_embedding_cost_control_service_1 = require("./fam-embedding-cost.control-service");
|
|
9
10
|
const fam_lmstudio_embedding_provider_1 = require("./fam-lmstudio-embedding.provider");
|
|
10
11
|
const fam_mock_embedding_provider_1 = require("./fam-mock-embedding.provider");
|
|
@@ -92,10 +93,19 @@ class FAM_Embedding_ControlService {
|
|
|
92
93
|
fsm_dynamo_1.DyFM_Log.warn(`[FAM embedding cost-warn] ${decision.reason}`);
|
|
93
94
|
}
|
|
94
95
|
const provider = this.buildProvider(resolved, issuer);
|
|
96
|
+
// FEAT-004: task-prefix (query/document aszimmetria) — a provider-dispatch ELŐTT, a feloldott
|
|
97
|
+
// modell + callType alapján. A `mock` provider KIVÉTEL (`off`): a determinisztikus hash-en az
|
|
98
|
+
// aszimmetrikus prefix elrontaná a query↔doc egyezést (a teszt-double match-ét).
|
|
99
|
+
const prefixMode = resolved.provider === 'mock'
|
|
100
|
+
? 'off'
|
|
101
|
+
: await this.resolveTaskPrefixMode({ table: set.table, scopePath: set.scopePath });
|
|
102
|
+
const texts = fam_embedding_prefix_util_1.FAM_EmbeddingPrefix_Util.apply({
|
|
103
|
+
texts: set.texts, modelId: resolved.modelId, callType: callType, mode: prefixMode,
|
|
104
|
+
});
|
|
95
105
|
// Batch-elt dispatch (token-/rate-limit védelem; dsgn-007 embedding.batchSize).
|
|
96
106
|
const allVectors = [];
|
|
97
|
-
for (let i = 0; i <
|
|
98
|
-
const batch =
|
|
107
|
+
for (let i = 0; i < texts.length; i += resolved.batchSize) {
|
|
108
|
+
const batch = texts.slice(i, i + resolved.batchSize);
|
|
99
109
|
try {
|
|
100
110
|
const vectors = await provider.embedTexts({ texts: batch, model: resolved.modelId, issuer: issuer });
|
|
101
111
|
this.assertDimensions(vectors, resolved, issuer);
|
|
@@ -232,6 +242,15 @@ class FAM_Embedding_ControlService {
|
|
|
232
242
|
}
|
|
233
243
|
return process.env.OPENAI_EMBEDDING_MODEL || 'text-embedding-3-large';
|
|
234
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* A task-prefix mód feloldása a config-precedencián (`embedding.taskPrefixMode`, FEAT-004). Ismeretlen /
|
|
247
|
+
* nem-enum érték → `auto` (a katalógus-default). A `mock`-provider-skip-et a hívó (`embedTexts`) kezeli.
|
|
248
|
+
*/
|
|
249
|
+
async resolveTaskPrefixMode(context) {
|
|
250
|
+
const value = (await config_control_service_1.FAM_Config_ControlService.getInstance()
|
|
251
|
+
.resolve('embedding.taskPrefixMode', { table: context.table, scopePath: context.scopePath })).value;
|
|
252
|
+
return value === 'off' || value === 'on' || value === 'auto' ? value : 'auto';
|
|
253
|
+
}
|
|
235
254
|
/** Az LM Studio base URL az ENV-ből (titok-szerű, nem DB; dsgn-006 §8). */
|
|
236
255
|
lmStudioBaseUrl() {
|
|
237
256
|
return process.env.LMSTUDIO_BASE_URL;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FAM_Entry_DataService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
4
5
|
const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
|
|
6
|
+
const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
|
|
5
7
|
const fam_entry_data_model_1 = require("../../../_models/data-models/fam-entry.data-model");
|
|
6
8
|
/**
|
|
7
9
|
* `FAM_Entry_DataService` (SP-2.2) — egy GENERIKUS, tár-paraméteres entry-CRUD a vektor-rétegnek.
|
|
@@ -39,6 +41,68 @@ class FAM_Entry_DataService extends nts_dynamo_1.DyNTS_DataService {
|
|
|
39
41
|
async findHydratableList(filter) {
|
|
40
42
|
return this.findDataList(filter);
|
|
41
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* PERF (FAM-REV / BFR-AM-013): a boot-hidratálás **lean + `{_id, contentVector}`-projekciós** variánsa.
|
|
46
|
+
* A pool-feltöltés (`hydrateTable`) CSAK az `_id`-t és a `contentVector`-t olvassa — a `content` szöveg,
|
|
47
|
+
* a metadata, a `scopePath`/`tags`/`kind` stb. a hidratáláshoz SOHA nem kell. Ezért: (1) `{_id:1,
|
|
48
|
+
* contentVector:1}`-projekció → a `content` (~1 KB/entry) NEM utazik; (2) `.lean()` → plain object,
|
|
49
|
+
* NINCS Mongoose-doc-hidratálás (ez a boot-OOM-kockázat fő forrása 35k doc-nál — FAM-REV-062). A
|
|
50
|
+
* `_deleted:null` az aktív-szűrő (a `findDataList` DB-szintű soft-delete-kizárását replikálja: a
|
|
51
|
+
* soft-delete `_deleted`-et `Date`-re állítja, az aktívon hiányzik/`null`). Model-hiány (boot-bootstrap
|
|
52
|
+
* előtt) → `findDataList`-fallback (korrekt, csak teljes-doc). Ugyanaz a minta, mint a candidate-prefilter
|
|
53
|
+
* (`FAM_RetrievalCandidate_DataService.findActiveCandidates`).
|
|
54
|
+
*/
|
|
55
|
+
async findHydratableVectorsLean(filter) {
|
|
56
|
+
const dataName = this.dataParams.dataName;
|
|
57
|
+
const model = mongoose_1.default.models[dataName];
|
|
58
|
+
if (!model) {
|
|
59
|
+
return this.findDataList(filter);
|
|
60
|
+
}
|
|
61
|
+
const leanFilter = { ...filter, _deleted: null };
|
|
62
|
+
const docs = await model.find(leanFilter, { _id: 1, contentVector: 1 }).lean().exec();
|
|
63
|
+
// KRITIKUS (_id-konzisztencia): a `.lean()` NYERS ObjectId `_id`-t ad, de a pool-kulcsoknak a
|
|
64
|
+
// `findDataList`-tel AZONOS string `_id`-nek kell lenniük (a candidate-prefilter candidateIds-ai
|
|
65
|
+
// ehhez matchelnek; ObjectId-kulcs → 0 hit). Stringify, hogy a hidratált pool-kulcsok stringek legyenek.
|
|
66
|
+
for (const doc of docs) {
|
|
67
|
+
doc._id = String(doc._id);
|
|
68
|
+
}
|
|
69
|
+
return docs;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* PERF (boot-csúcs): a `findHydratableVectorsLean` STREAMING (Mongoose-cursor) variánsa — a teljes
|
|
73
|
+
* (~23k-doc, ~486 MB `contentVector`-os) listát SOHA nem tartja egyben a memóriában, hanem doc-onként
|
|
74
|
+
* yield-eli az `onVector` callback-nek (a pool-építés közben a doc GC-zhető). Így a boot tranziens-csúcsa
|
|
75
|
+
* a teljes-lista-méretről (~486 MB) ~egy-doc-méretre csökken → kisebb boot-OOM-kockázat + a steady-state
|
|
76
|
+
* RSS alacsonyabb high-water-markra áll be (a `normalizedOnly` megtakarítás láthatóvá válik). Az `_id`
|
|
77
|
+
* stringify itt is kötelező (pool-kulcs-konzisztencia). Visszaadja a yield-elt (érvényes) vektorok számát.
|
|
78
|
+
* Model-hiány (boot-bootstrap előtt) → `findHydratableVectorsLean`-fallback (teljes-lista, korrekt).
|
|
79
|
+
*/
|
|
80
|
+
async streamHydratableVectors(filter, onVector) {
|
|
81
|
+
const dataName = this.dataParams.dataName;
|
|
82
|
+
const model = mongoose_1.default.models[dataName];
|
|
83
|
+
if (!model) {
|
|
84
|
+
const docs = await this.findHydratableVectorsLean(filter);
|
|
85
|
+
let fallbackCount = 0;
|
|
86
|
+
for (const doc of docs) {
|
|
87
|
+
if (doc._id && doc.contentVector?.length) {
|
|
88
|
+
onVector(String(doc._id), doc.contentVector);
|
|
89
|
+
fallbackCount++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return fallbackCount;
|
|
93
|
+
}
|
|
94
|
+
const leanFilter = { ...filter, _deleted: null };
|
|
95
|
+
const cursor = model.find(leanFilter, { _id: 1, contentVector: 1 }).lean().cursor();
|
|
96
|
+
let count = 0;
|
|
97
|
+
for await (const raw of cursor) {
|
|
98
|
+
const doc = raw;
|
|
99
|
+
if (doc._id && doc.contentVector?.length) {
|
|
100
|
+
onVector(String(doc._id), doc.contentVector);
|
|
101
|
+
count++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return count;
|
|
105
|
+
}
|
|
42
106
|
/**
|
|
43
107
|
* Egy entry vektor-mezőinek ATOMIKUS frissítése (`$set`) — a Mixed silent-drop ellen. A
|
|
44
108
|
* `contentVector` + `embeddingModel` + `embeddingStatus` + opc. `contentHash` egy lépésben.
|
|
@@ -60,5 +124,76 @@ class FAM_Entry_DataService extends nts_dynamo_1.DyNTS_DataService {
|
|
|
60
124
|
const update = { $set: { embeddingStatus: status } };
|
|
61
125
|
await this.updateData({ filterBy: { _id: id }, update: update });
|
|
62
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* A felismert azonosító-kódok (`referenceCodes`) atomikus frissítése (`$set`) — FEAT-003 inverted-index.
|
|
129
|
+
* Az embed-sikertől FÜGGETLEN write (a write-path az embed ELŐTT hívja), így a kód-citáció a még nem
|
|
130
|
+
* embeddelt / embed-bukott elemen is megjelenik a determinisztikus read-expanzióban.
|
|
131
|
+
*/
|
|
132
|
+
async setReferenceCodes(id, referenceCodes) {
|
|
133
|
+
const update = { $set: { referenceCodes: referenceCodes } };
|
|
134
|
+
await this.updateData({ filterBy: { _id: id }, update: update });
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* A scan-provenance `source.root` backfill-je (FAM-REV-048 kiegészítés) — a re-scan `equal`
|
|
138
|
+
* (változatlan-content) ágán hívva, hogy a metaadat-frissítés a content-skip ELLENÉRE megtörténjen.
|
|
139
|
+
* **FELTÉTELES** `$set`: csak akkor ír, ha a `source.root` hiányzik VAGY eltér (a Mongo-feltétel no-op,
|
|
140
|
+
* ha már stimmel), így a teljes korpusz root-tudatossá válik (törölt-fájl reconciliation-lefedettség)
|
|
141
|
+
* re-embed nélkül. Dot-path kulcs `$set`/filter — a Dynamo-típus loose-object útján (mint `setVector`).
|
|
142
|
+
*/
|
|
143
|
+
async backfillSourceRoot(id, root) {
|
|
144
|
+
const setBlock = { ['source.root']: root };
|
|
145
|
+
const update = { $set: setBlock };
|
|
146
|
+
const filterBy = { _id: id };
|
|
147
|
+
filterBy['source.root'] = { $ne: root };
|
|
148
|
+
await this.updateData({ filterBy: filterBy, update: update });
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A `sourceFilePath` (display-relatív út) FELTÉTELES backfill-je (FAM-REV-050) — a re-scan `equal` ágán
|
|
152
|
+
* hívva, hogy a default (basePath-nélküli) relatív út KONZISZTENS legyen, miután a fájl-identitás az
|
|
153
|
+
* `absolutePath`-ra állt át (egy korábban más scan-gyökérről — pl. `scan-file` basename — ingestelt chunk
|
|
154
|
+
* relatív útja a friss scan-gyökérhez igazodjon). Csak akkor ír, ha eltér (`$ne` — no-op, ha már stimmel).
|
|
155
|
+
*/
|
|
156
|
+
async backfillSourceFilePath(id, relativePath) {
|
|
157
|
+
const update = { $set: { sourceFilePath: relativePath } };
|
|
158
|
+
const filterBy = { _id: id };
|
|
159
|
+
filterBy.sourceFilePath = { $ne: relativePath };
|
|
160
|
+
await this.updateData({ filterBy: filterBy, update: update });
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* A `referenceCodes` FELTÉTELES backfill-je (FEAT-003 + 2026-06-20 audit) — a re-scan `equal` ágán
|
|
164
|
+
* hívva, hogy a kód-index a content-skip ELLENÉRE frissüljön (pl. a Tailwind-denylist-bővítés a régi
|
|
165
|
+
* `equal`-chunkokra is érvényesüljön). Csak akkor ír, ha a tárolt tömb ELTÉR az újonnan kinyertől
|
|
166
|
+
* (`referenceCodes: {$ne}` — a Mongo no-op, ha egyezik); a `extract` determinisztikus sorrendje miatt
|
|
167
|
+
* a változatlan content ugyanazt a tömböt adja → nincs fölös write.
|
|
168
|
+
*/
|
|
169
|
+
async backfillReferenceCodes(id, codes) {
|
|
170
|
+
const update = { $set: { referenceCodes: codes } };
|
|
171
|
+
const filterBy = { _id: id };
|
|
172
|
+
filterBy['referenceCodes'] = { $ne: codes };
|
|
173
|
+
await this.updateData({ filterBy: filterBy, update: update });
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* A `position` (chunk char/sor-offset) FELTÉTELES backfill-je (FAM-REV-061) — a re-scan `equal` ágán hívva,
|
|
177
|
+
* hogy a chunker pozíció-fix (a sliding-ablakok per-ablak abszolút charStart-ja) a régi `equal`-chunkokra is
|
|
178
|
+
* érvényesüljön, RE-EMBED NÉLKÜL (a pozíció a vektort nem érinti). Csak akkor ír, ha a `position.charStart`
|
|
179
|
+
* ELTÉR (a bug szimptómája: a nagy-blokk sub-chunkjai mind a blokk-kezdetre ütköztek) — a Mongo no-op, ha
|
|
180
|
+
* már stimmel. A friss `position` mind a négy mezőt felülírja (char/sor egyszerre számolt → konzisztens).
|
|
181
|
+
*/
|
|
182
|
+
async backfillPosition(id, position) {
|
|
183
|
+
const setBlock = {
|
|
184
|
+
['position.charStart']: position.charStart,
|
|
185
|
+
['position.charEnd']: position.charEnd,
|
|
186
|
+
};
|
|
187
|
+
if (position.lineStart !== undefined) {
|
|
188
|
+
setBlock['position.lineStart'] = position.lineStart;
|
|
189
|
+
}
|
|
190
|
+
if (position.lineEnd !== undefined) {
|
|
191
|
+
setBlock['position.lineEnd'] = position.lineEnd;
|
|
192
|
+
}
|
|
193
|
+
const update = { $set: setBlock };
|
|
194
|
+
const filterBy = { _id: id };
|
|
195
|
+
filterBy['position.charStart'] = { $ne: position.charStart };
|
|
196
|
+
await this.updateData({ filterBy: filterBy, update: update });
|
|
197
|
+
}
|
|
63
198
|
}
|
|
64
199
|
exports.FAM_Entry_DataService = FAM_Entry_DataService;
|
|
@@ -74,9 +74,17 @@ class FAM_VectorSearch_ControlService {
|
|
|
74
74
|
this.poolDims.set(table, null);
|
|
75
75
|
const dataService = new fam_entry_data_service_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
76
76
|
const filter = { embeddingStatus: 'completed' };
|
|
77
|
-
let
|
|
77
|
+
let loaded = 0;
|
|
78
78
|
try {
|
|
79
|
-
|
|
79
|
+
// PERF: lean + {_id, contentVector}-projekció + STREAMING (cursor) — a hidratálás CSAK ezt a két
|
|
80
|
+
// mezőt olvassa (nincs content-szöveg, nincs Mongoose-doc-hidratálás), ÉS doc-onként tölti a poolt
|
|
81
|
+
// (a teljes ~486 MB-os lista SOHA nincs egyben memóriában) → kisebb boot-csúcs + boot-OOM-kockázat.
|
|
82
|
+
await dataService.streamHydratableVectors(filter, (id, contentVector) => {
|
|
83
|
+
if (this.acceptVectorForPool(table, contentVector)) {
|
|
84
|
+
pool.addVector(id, contentVector);
|
|
85
|
+
loaded++;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
80
88
|
}
|
|
81
89
|
catch (error) {
|
|
82
90
|
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
@@ -87,16 +95,6 @@ class FAM_VectorSearch_ControlService {
|
|
|
87
95
|
context: { operation: 'hydrate', table: table },
|
|
88
96
|
});
|
|
89
97
|
}
|
|
90
|
-
let loaded = 0;
|
|
91
|
-
for (const entry of entries) {
|
|
92
|
-
if (!entry._id || !entry.contentVector?.length) {
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (this.acceptVectorForPool(table, entry.contentVector)) {
|
|
96
|
-
pool.addVector(entry._id, entry.contentVector);
|
|
97
|
-
loaded++;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
98
|
this.hydrated.set(table, true);
|
|
101
99
|
fsm_dynamo_1.DyFM_Log.log(`[FAM hydrate] '${table}' tár: ${loaded} vektor betöltve az in-memory pool-ba.`);
|
|
102
100
|
}
|
|
@@ -143,7 +141,7 @@ class FAM_VectorSearch_ControlService {
|
|
|
143
141
|
});
|
|
144
142
|
}
|
|
145
143
|
const pool = this.getOrCreatePool(input.table);
|
|
146
|
-
const poolSize = pool.
|
|
144
|
+
const poolSize = pool.size();
|
|
147
145
|
if (!poolSize) {
|
|
148
146
|
// Bootstrap-skip jelzés (dsgn-006 §4.1): üres pool (embedding nincs konfigurálva / nincs
|
|
149
147
|
// completed entry). NEM néma — a capability + UI piros banner ezt jelzi (dsgn-008/dsgn-011).
|
|
@@ -166,7 +164,15 @@ class FAM_VectorSearch_ControlService {
|
|
|
166
164
|
}
|
|
167
165
|
/** Egy tár pool-jának mérete (a `test_vector_search` / `get_storage_usage` capability — dsgn-003). */
|
|
168
166
|
getPoolSize(table) {
|
|
169
|
-
return this.getOrCreatePool(table).
|
|
167
|
+
return this.getOrCreatePool(table).size();
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Egy tár pool-jának tényleges (megfigyelt) embedding-dimenziója (FAM-REV-044) — az első elfogadott
|
|
171
|
+
* vektornál rögzül (`acceptVectorForPool`). `null`, ha a pool még üres / nincs dimenzió. A
|
|
172
|
+
* `get_embedding_config` ezzel jelenti a runtime-valóságot a (gyakran null) deklarált dim mellett.
|
|
173
|
+
*/
|
|
174
|
+
getPoolDim(table) {
|
|
175
|
+
return this.poolDims.get(table) ?? null;
|
|
170
176
|
}
|
|
171
177
|
/** Hidratált-e már egy tár (a bootstrap-skip diagnosztikához). */
|
|
172
178
|
isHydrated(table) {
|
|
@@ -181,20 +187,13 @@ class FAM_VectorSearch_ControlService {
|
|
|
181
187
|
* szűkített halmazra korlátozódik (a jelöltszám bizonyíthatóan kisebb a teljes tárnál — acceptance #4).
|
|
182
188
|
*/
|
|
183
189
|
searchInCandidates(pool, queryVector, candidateIds, topK) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
scopedCount++;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (!scopedCount) {
|
|
195
|
-
return [];
|
|
196
|
-
}
|
|
197
|
-
const results = scoped.search(queryVector, topK, local_vector_search_1.LVS_Search_Mode.cosineSimilarity);
|
|
190
|
+
// PERF (nts-dynamo ^1.15.71): a pool NATÍVAN szűr a candidate-ID halmazra — a MÁR előre-normalizált
|
|
191
|
+
// pool-on iterál, a nem-jelölteket `Set.has` O(1)-gyel kihagyja. Így NINCS per-query szűkített-pool-
|
|
192
|
+
// építés + a vektorok ÚJRA-normalizálása (a régi út 24k-jelöltnél ~24k l2Normalize-alloc + GC-spike-ot
|
|
193
|
+
// okozott; bench: unscoped 985ms → 90ms = 11×, scoped 37ms → 5ms = 7×, top-egyezés identikus). A
|
|
194
|
+
// pool ∩ candidateIds halmaz pontosan ugyanaz, mint a régi (a pool csak a hidratált entry-ket tartja).
|
|
195
|
+
const candidateSet = new Set(candidateIds);
|
|
196
|
+
const results = pool.search(queryVector, topK, local_vector_search_1.LVS_Search_Mode.cosineSimilarity, candidateSet);
|
|
198
197
|
return results.map((hit) => ({ id: hit.id, score: hit.score }));
|
|
199
198
|
}
|
|
200
199
|
/** A query-vektor feloldása: kész `queryVector`, vagy a `queryText` embeddelése (provider-agnosztikus). */
|
|
@@ -216,18 +215,29 @@ class FAM_VectorSearch_ControlService {
|
|
|
216
215
|
getOrCreatePool(table) {
|
|
217
216
|
let pool = this.pools.get(table);
|
|
218
217
|
if (!pool) {
|
|
219
|
-
|
|
218
|
+
// PERF (nts-dynamo ^1.15.72): a FAM KIZÁRÓLAG cosine-keresést használ + SOSEM olvas nyers vektor-
|
|
219
|
+
// értéket (csak `size()` + `search(cosine)`), ezért a nyers `vectorPool` holt súly. `normalizedOnly`
|
|
220
|
+
// → a pool csak a normalizált másolatot tartja → ~50% pool-memória (35k vektor @2560-dim: ~700MB).
|
|
221
|
+
pool = new local_vector_search_1.LVS_VectorPool_ControlService({ normalizedOnly: true });
|
|
220
222
|
this.pools.set(table, pool);
|
|
221
223
|
this.poolDims.set(table, null);
|
|
222
224
|
}
|
|
223
225
|
return pool;
|
|
224
226
|
}
|
|
225
227
|
/**
|
|
226
|
-
*
|
|
227
|
-
* dimenziót; az ettől eltérő dimenziójú vektort NEM
|
|
228
|
-
*
|
|
228
|
+
* Pool-felvételi kapu: (1) finiteség (NaN/±Inf-szűrés) + (2) dimenzió-homogenitás (dsgn-006 §6.2 /
|
|
229
|
+
* acceptance #6). A pool első vektora rögzíti a dimenziót; az ettől eltérő dimenziójú vektort NEM
|
|
230
|
+
* vesszük fel (a dimenzió-váltás explicit re-embedet igényel). Mindkét elutasítás warn (nem néma).
|
|
229
231
|
*/
|
|
230
232
|
acceptVectorForPool(table, vector) {
|
|
233
|
+
// Finiteség-kapu (consumer-szintű BFR-AM-013 mitigáció): egy nem-véges (NaN/±Inf) elemű vektor
|
|
234
|
+
// némán megmérgezné a koszinusz-rangsort (a NaN-összehasonlítások kiejtik a valódi találatokat),
|
|
235
|
+
// ezért a pool-ba NEM kerül be. Az engine-szintű dot-product-guard továbbra is bedrock-igény.
|
|
236
|
+
if (!vector.every((value) => Number.isFinite(value))) {
|
|
237
|
+
fsm_dynamo_1.DyFM_Log.warn(`[FAM vector-pool] '${table}' tár nem-véges (NaN/±Inf) vektor-elem → kihagyva `
|
|
238
|
+
+ `(hibás embedding / csonka write; BFR-AM-013).`);
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
231
241
|
const current = this.poolDims.get(table);
|
|
232
242
|
if (current === null || current === undefined) {
|
|
233
243
|
this.poolDims.set(table, vector.length);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* (BFR-AM-002/008); a bedrock-csere (MP-15) a `FAM_EmbeddingProvider` interfész mögött non-breaking.
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.FAM_EmbeddingBootstrap_ControlService = exports.FAM_EmbeddingCost_ControlService = exports.FAM_EmbeddingPipeline_ControlService = exports.FAM_EmbeddingPreset_ControlService = exports.FAM_StoreRegistry_Util = exports.FAM_STORE_REGISTRY = exports.FAM_Entry_DataService = exports.FAM_VectorSearch_ControlService = exports.FAM_MOCK_EMBEDDING_DIMS = exports.FAM_Mock_EmbeddingProvider = exports.FAM_LMStudio_EmbeddingProvider = exports.FAM_OpenAI_EmbeddingProvider = exports.FAM_Embedding_ControlService = void 0;
|
|
11
|
+
exports.FAM_EmbeddingBootstrap_ControlService = exports.FAM_DuplicateScan_ControlService = exports.FAM_EmbeddingCost_ControlService = exports.FAM_EmbeddingPipeline_ControlService = exports.FAM_EmbeddingPreset_ControlService = exports.FAM_StoreRegistry_Util = exports.FAM_STORE_REGISTRY = exports.FAM_Entry_DataService = exports.FAM_VectorSearch_ControlService = exports.FAM_MOCK_EMBEDDING_DIMS = exports.FAM_Mock_EmbeddingProvider = exports.FAM_LMStudio_EmbeddingProvider = exports.FAM_OpenAI_EmbeddingProvider = exports.FAM_Embedding_ControlService = void 0;
|
|
12
12
|
// SP-2.1 — provider-dispatch kapu + providerek
|
|
13
13
|
var fam_embedding_control_service_1 = require("./_services/fam-embedding.control-service");
|
|
14
14
|
Object.defineProperty(exports, "FAM_Embedding_ControlService", { enumerable: true, get: function () { return fam_embedding_control_service_1.FAM_Embedding_ControlService; } });
|
|
@@ -35,6 +35,9 @@ var fam_embedding_pipeline_control_service_1 = require("./_services/fam-embeddin
|
|
|
35
35
|
Object.defineProperty(exports, "FAM_EmbeddingPipeline_ControlService", { enumerable: true, get: function () { return fam_embedding_pipeline_control_service_1.FAM_EmbeddingPipeline_ControlService; } });
|
|
36
36
|
var fam_embedding_cost_control_service_1 = require("./_services/fam-embedding-cost.control-service");
|
|
37
37
|
Object.defineProperty(exports, "FAM_EmbeddingCost_ControlService", { enumerable: true, get: function () { return fam_embedding_cost_control_service_1.FAM_EmbeddingCost_ControlService; } });
|
|
38
|
+
// near-duplikátum FELDERÍTŐ (read-only; user-direktíva 2026-06-21)
|
|
39
|
+
var fam_duplicate_scan_control_service_1 = require("./_services/fam-duplicate-scan.control-service");
|
|
40
|
+
Object.defineProperty(exports, "FAM_DuplicateScan_ControlService", { enumerable: true, get: function () { return fam_duplicate_scan_control_service_1.FAM_DuplicateScan_ControlService; } });
|
|
38
41
|
// boot-hook (App.postProcess)
|
|
39
42
|
var fam_embedding_bootstrap_control_service_1 = require("./_services/fam-embedding-bootstrap.control-service");
|
|
40
43
|
Object.defineProperty(exports, "FAM_EmbeddingBootstrap_ControlService", { enumerable: true, get: function () { return fam_embedding_bootstrap_control_service_1.FAM_EmbeddingBootstrap_ControlService; } });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_AUTHORED_SOURCE_TYPES = void 0;
|
|
4
|
+
exports.isAuthoredSource = isAuthoredSource;
|
|
5
|
+
/**
|
|
6
|
+
* FEAT-007 — a **non-file-derived** (kézzel / agent-írt) bejegyzés-források halmaza. Ezek a `source.type`-ok
|
|
7
|
+
* NEM regenerálhatók re-scan-nel: a `manual` (felhasználó kézzel írta) és az `agent` (agent-write) tartalom a
|
|
8
|
+
* **pótolhatatlan, kurált érték** — szemben a `scan` / `scan-summary` (fájlból generált, re-scan visszahozza)
|
|
9
|
+
* és az `import` (külső forrásból re-importálható) bejegyzésekkel. A backup/export ezekre fókuszál.
|
|
10
|
+
*/
|
|
11
|
+
exports.FAM_AUTHORED_SOURCE_TYPES = ['manual', 'agent'];
|
|
12
|
+
/**
|
|
13
|
+
* Egy bejegyzés **authored** (non-file-derived, pótolhatatlan)-e a `source.type` alapján. A hiányzó/ismeretlen
|
|
14
|
+
* `source` defenzíven NEM authored (a fájl-derived túlsúly miatt biztonságos default — a backup nem hígul fel
|
|
15
|
+
* scan-elt elemekkel; a tényleges authored elemeknek mindig van `manual`/`agent` source-uk a write-úton).
|
|
16
|
+
*/
|
|
17
|
+
function isAuthoredSource(source) {
|
|
18
|
+
if (!source) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return exports.FAM_AUTHORED_SOURCE_TYPES.includes(source.type);
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_Export_ControlService = void 0;
|
|
4
|
+
const embedding_1 = require("../../embedding");
|
|
5
|
+
const fam_export_const_1 = require("../_collections/fam-export.const");
|
|
6
|
+
/**
|
|
7
|
+
* `FAM_Export_ControlService` (FEAT-007) — a **non-file-derived, pótolhatatlan** (kézzel/agent-írt) bejegyzések
|
|
8
|
+
* külön exportja/biztonsági mentése. A `scan`/`scan-summary` (fájlból re-scannelhető) és `import` (re-importálható)
|
|
9
|
+
* elemekkel szemben a `manual`/`agent` source-ú `memory`/`knowledge`/… bejegyzés re-scan-nel NEM regenerálható —
|
|
10
|
+
* ezt a kurált értéket menti ki a hívó (CLI `fam export` / `export_authored` capability) egy JSON-fájlba.
|
|
11
|
+
*
|
|
12
|
+
* **Mongo-szintű szűrés:** per-tár `findDataList({ 'source.type': $in [manual, agent] })` — az authored-halmaz
|
|
13
|
+
* kicsi (tipikusan tucatnyi), így a teljes-doc olvasás nem perf-gond; a `contentVector` az export-mappolásnál
|
|
14
|
+
* KIMARAD (a kurált CONTENT a pótolhatatlan, a vektor re-embeddel regenerálható). A `isAuthoredSource` helper a
|
|
15
|
+
* provenance-SSOT (a Mongo-filter mellett defenzív in-memory ellenőrzés).
|
|
16
|
+
*
|
|
17
|
+
* **FIGYELEM (memory: dynts_dataservice_eager_resolve):** per-tár lazy `new FAM_Entry_DataService` (nincs élő mező).
|
|
18
|
+
*/
|
|
19
|
+
class FAM_Export_ControlService {
|
|
20
|
+
issuer = 'FAM_Export_ControlService';
|
|
21
|
+
/**
|
|
22
|
+
* Az ÖSSZES tár authored (non-file-derived) bejegyzésének exportja. Tárakon iterál (`FAM_STORE_REGISTRY`),
|
|
23
|
+
* per-tár `source.type ∈ {manual, agent}`-re szűr, és a kurált mezőket adja vissza (a `contentVector` nélkül).
|
|
24
|
+
*/
|
|
25
|
+
async exportAuthored() {
|
|
26
|
+
const byTable = {};
|
|
27
|
+
const entries = [];
|
|
28
|
+
for (const registryEntry of embedding_1.FAM_STORE_REGISTRY) {
|
|
29
|
+
const storeEntries = await this.loadAuthored(registryEntry);
|
|
30
|
+
byTable[registryEntry.table] = storeEntries.length;
|
|
31
|
+
for (const entry of storeEntries) {
|
|
32
|
+
entries.push(this.toExportEntry(entry, registryEntry.table));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { formatVersion: 1, total: entries.length, byTable: byTable, entries: entries };
|
|
36
|
+
}
|
|
37
|
+
/** Egy tár authored bejegyzései (Mongo-filter `source.type $in [manual, agent]` + defenzív helper-szűrés). */
|
|
38
|
+
async loadAuthored(registryEntry) {
|
|
39
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
40
|
+
const filter = {};
|
|
41
|
+
filter['source.type'] = { $in: fam_export_const_1.FAM_AUTHORED_SOURCE_TYPES };
|
|
42
|
+
const storeEntries = await dataService.findDataList(filter);
|
|
43
|
+
// A helper a provenance-SSOT: a hiányzó/ismeretlen source defenzíven kizárandó (a Mongo-filter már szűrt).
|
|
44
|
+
return storeEntries.filter((entry) => (0, fam_export_const_1.isAuthoredSource)(entry.source));
|
|
45
|
+
}
|
|
46
|
+
/** Egy `FAM_Entry` → exportált authored-bejegyzés (a `contentVector` KIHAGYVA — re-embeddel regenerálható). */
|
|
47
|
+
toExportEntry(entry, table) {
|
|
48
|
+
return {
|
|
49
|
+
id: String(entry._id),
|
|
50
|
+
table: table,
|
|
51
|
+
content: entry.content ?? '',
|
|
52
|
+
source: entry.source,
|
|
53
|
+
kind: entry.kind,
|
|
54
|
+
tags: entry.tags,
|
|
55
|
+
weight: entry.weight,
|
|
56
|
+
scopePath: entry.scopePath,
|
|
57
|
+
referenceCodes: entry.referenceCodes,
|
|
58
|
+
headingPath: entry.headingPath,
|
|
59
|
+
createdMs: entry.__created ? new Date(entry.__created).getTime() : undefined,
|
|
60
|
+
lastModifiedMs: entry.__lastModified ? new Date(entry.__lastModified).getTime() : undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.FAM_Export_ControlService = FAM_Export_ControlService;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAuthoredSource = exports.FAM_AUTHORED_SOURCE_TYPES = exports.FAM_Export_ControlService = void 0;
|
|
4
|
+
var fam_export_control_service_1 = require("./_services/fam-export.control-service");
|
|
5
|
+
Object.defineProperty(exports, "FAM_Export_ControlService", { enumerable: true, get: function () { return fam_export_control_service_1.FAM_Export_ControlService; } });
|
|
6
|
+
var fam_export_const_1 = require("./_collections/fam-export.const");
|
|
7
|
+
Object.defineProperty(exports, "FAM_AUTHORED_SOURCE_TYPES", { enumerable: true, get: function () { return fam_export_const_1.FAM_AUTHORED_SOURCE_TYPES; } });
|
|
8
|
+
Object.defineProperty(exports, "isAuthoredSource", { enumerable: true, get: function () { return fam_export_const_1.isAuthoredSource; } });
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_FamIgnore_Util = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
/**
|
|
8
|
+
* `FAM_FamIgnore_Util` (descriptive user feedback / nagy-scan ergonómia) — a **`.fdpfamignore`** in-repo,
|
|
9
|
+
* gitignore-stílusú ignore-fájl támogatása. A projekt (vagy a workspace-gyökér) MAGÁBAN hordozza, mit hagyjon
|
|
10
|
+
* ki a FAM-scan — a config `scan.ignorePatterns` MELLETT (HOZZÁADÓDIK, nem felülír). A scan a `resolvedRoot`-ban
|
|
11
|
+
* lévő `.fdpfamignore`-t olvassa (scan-folder/scan-project; a scan-file egyetlen explicit fájlt céloz).
|
|
12
|
+
*
|
|
13
|
+
* **Szintaxis (gitignore-szerű, a `FAM_GlobMatch_Util`-ra fordítva):** soronként egy minta; `#` komment + üres
|
|
14
|
+
* sor kihagyva. `node_modules` (csupasz név) → bárhol illeszkedik; `/build` (vezető `/`) → a gyökérhez horgonyzott;
|
|
15
|
+
* `dist/` (záró `/`) → könyvtár; `src/gen` (beágyazott `/`) → gyökér-relatív; `*.tgz` → bárhol. **A `!` negáció
|
|
16
|
+
* v1-ben NEM támogatott** (a flat „matchesAny → skip" modellben — BACKLOG), az ilyen sorok kihagyva.
|
|
17
|
+
*
|
|
18
|
+
* **v1 hatókör:** a scan-GYÖKÉR `.fdpfamignore`-ja (a teljes scanre). A NESTED (per-alkönyvtár) `.fdpfamignore`
|
|
19
|
+
* a két-fázisú felderítés (walk → classify) miatt külön kör — BACKLOG (a `baseDir` paraméter már készen áll rá).
|
|
20
|
+
*/
|
|
21
|
+
class FAM_FamIgnore_Util {
|
|
22
|
+
/** Az ignore-fájl kanonikus neve. */
|
|
23
|
+
static FILENAME = '.fdpfamignore';
|
|
24
|
+
/**
|
|
25
|
+
* Egy `.fdpfamignore` tartalom → nyers minta-sorok (trim; `#`-komment + üres + `!`-negáció kihagyva).
|
|
26
|
+
* Tisztán pure — a fordítást (`toGlobPatterns`) külön végzi, hogy a parse önállóan tesztelhető legyen.
|
|
27
|
+
*/
|
|
28
|
+
static parse(content) {
|
|
29
|
+
const result = [];
|
|
30
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
31
|
+
const line = rawLine.trim();
|
|
32
|
+
if (!line || line.startsWith('#') || line.startsWith('!')) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
result.push(line);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Nyers gitignore-szerű minták → a `FAM_GlobMatch_Util` mintái (az ENTRY + a TARTALMA: `<base>` + `<base>/**`),
|
|
41
|
+
* a `baseDir` (root-relatív könyvtár, ahol az ignore-fájl van) elé fűzve. Horgonyzás: vezető `/` VAGY beágyazott
|
|
42
|
+
* `/` → a `baseDir`-hez horgonyzott; csupasz név → globális (bárhol-illeszkedő) prefix. A záró `/` (könyvtár-jel)
|
|
43
|
+
* levágva (a FAM a fájlt és a könyvtárat egyformán skip-eli).
|
|
44
|
+
*/
|
|
45
|
+
static toGlobPatterns(lines, baseDir = '') {
|
|
46
|
+
const prefix = baseDir ? `${baseDir.replace(/\\/g, '/').replace(/\/+$/, '')}/` : '';
|
|
47
|
+
const patterns = [];
|
|
48
|
+
for (const rawLine of lines) {
|
|
49
|
+
let line = rawLine.trim();
|
|
50
|
+
const anchored = line.startsWith('/');
|
|
51
|
+
if (anchored) {
|
|
52
|
+
line = line.slice(1);
|
|
53
|
+
}
|
|
54
|
+
if (line.endsWith('/')) {
|
|
55
|
+
line = line.slice(0, -1);
|
|
56
|
+
}
|
|
57
|
+
if (!line) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const base = (anchored || line.includes('/')) ? `${prefix}${line}` : `${prefix}**/${line}`;
|
|
61
|
+
patterns.push(base, `${base}/**`);
|
|
62
|
+
}
|
|
63
|
+
return patterns;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A `.fdpfamignore` beolvasása + fordítása egy könyvtárból (a `dirAbsolutePath`-ban). Hiányzó / olvashatatlan
|
|
67
|
+
* fájl → `[]` (opt-in: csak ha létezik). A `baseDir` a root-relatív horgony (a gyökér-szintű hívásnál `''`).
|
|
68
|
+
*/
|
|
69
|
+
static loadFromDir(dirAbsolutePath, baseDir = '') {
|
|
70
|
+
try {
|
|
71
|
+
const filePath = path.join(dirAbsolutePath, FAM_FamIgnore_Util.FILENAME);
|
|
72
|
+
if (!fs.existsSync(filePath)) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
return FAM_FamIgnore_Util.toGlobPatterns(FAM_FamIgnore_Util.parse(fs.readFileSync(filePath, 'utf-8')), baseDir);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Az ignore-fájl olvasás-hibája SOHA nem buktatja a scant (opt-in, best-effort).
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.FAM_FamIgnore_Util = FAM_FamIgnore_Util;
|