@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,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ScanPath_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
|
+
const error_codes_const_1 = require("../../../_collections/error-codes.const");
|
|
8
|
+
const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
|
|
9
|
+
/**
|
|
10
|
+
* `FAM_ScanPath_Util` (SP-4.1, dsgn-004 §6.1) — a scan-réteg path-traversal védelme. A scan
|
|
11
|
+
* LOKÁLIS fájlrendszert olvas, ezért minden bejárt fájl feloldott **valódi** abszolút útvonalának
|
|
12
|
+
* (realpath) a scan-gyökér alatt KELL maradnia — `..`-szegmens / symlink-escape → `FAM-SCAN-*`
|
|
13
|
+
* hiba (dsgn-008 §6.1). A `sourceFilePath` a gyökértől relatívan számolódik (a chunk-mezőre).
|
|
14
|
+
*
|
|
15
|
+
* **Symlink-escape (a `MP-4-detailed` kockázat):** NEM string-prefix összevetés (azt a junction/
|
|
16
|
+
* symlink megkerüli), hanem a `fs.realpathSync`-cal feloldott VALÓDI útvonalat veti össze a
|
|
17
|
+
* (szintén realpath-olt) gyökérrel, `path.relative` + szegmens-vizsgálattal. Statikus util — nincs
|
|
18
|
+
* DB-/service-függés (a `FAM_Error_Util.create` csak megépíti a hibát, a hívó dobja/emittálja).
|
|
19
|
+
*/
|
|
20
|
+
class FAM_ScanPath_Util {
|
|
21
|
+
/**
|
|
22
|
+
* Egy scan-gyökér feloldott VALÓDI abszolút útvonala (`realpath`). A gyökérnek léteznie kell;
|
|
23
|
+
* nem-létező / olvashatatlan gyökér → `FAM-SCAN-READ-001` (a run `failed`). A többi útvonal
|
|
24
|
+
* ehhez a kanonikus gyökérhez méri magát.
|
|
25
|
+
*/
|
|
26
|
+
static resolveRoot(rootPath, issuer) {
|
|
27
|
+
try {
|
|
28
|
+
return fs.realpathSync(path.resolve(rootPath));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
32
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.scanRead,
|
|
33
|
+
message: `A scan-gyökér nem oldható fel / nem olvasható: '${rootPath}'. `
|
|
34
|
+
+ 'Ellenőrizd, hogy az útvonal létezik és olvasható.',
|
|
35
|
+
issuer: issuer,
|
|
36
|
+
context: { operation: 'resolve-scan-root', sourceFilePath: rootPath },
|
|
37
|
+
cause: error,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Biztosítja, hogy a `candidatePath` valódi feloldott útvonala a `resolvedRoot` ALATT (vagy
|
|
43
|
+
* azonos vele) maradjon (dsgn-004 §6.1). Escape (`..`/symlink a gyökéren kívülre) →
|
|
44
|
+
* `FAM-SCAN-READ-001` hiba. Visszaadja a feloldott valódi abszolút útvonalat (a hívó ezt
|
|
45
|
+
* használja olvasásra + a relatív-útvonal számításhoz).
|
|
46
|
+
*/
|
|
47
|
+
static assertInsideRoot(resolvedRoot, candidatePath, issuer) {
|
|
48
|
+
let realCandidate;
|
|
49
|
+
try {
|
|
50
|
+
realCandidate = fs.realpathSync(path.resolve(candidatePath));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
54
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.scanRead,
|
|
55
|
+
message: `A scan-útvonal nem oldható fel / nem olvasható: '${candidatePath}'.`,
|
|
56
|
+
issuer: issuer,
|
|
57
|
+
context: { operation: 'assert-inside-root', sourceFilePath: candidatePath },
|
|
58
|
+
cause: error,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (!FAM_ScanPath_Util.isInside(resolvedRoot, realCandidate)) {
|
|
62
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
63
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.scanRead,
|
|
64
|
+
message: `Path-traversal védelem: a feloldott útvonal ('${realCandidate}') a scan-gyökéren `
|
|
65
|
+
+ `('${resolvedRoot}') KÍVÜL esik ('..'-szegmens vagy symlink-escape). A scan elutasítva.`,
|
|
66
|
+
issuer: issuer,
|
|
67
|
+
context: { operation: 'path-traversal-guard', sourceFilePath: candidatePath },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return realCandidate;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A `child` valódi-útvonal a `root` ALATT van-e (vagy azonos). A `path.relative` eredménye
|
|
74
|
+
* NEM kezdődhet `..`-tal és nem lehet abszolút (más meghajtó) — ez a szegmens-szintű, NEM
|
|
75
|
+
* string-prefix összevetés (a string-prefix `/a/bc`-t `/a/b` alá sorolná tévesen).
|
|
76
|
+
*/
|
|
77
|
+
static isInside(root, child) {
|
|
78
|
+
const relative = path.relative(root, child);
|
|
79
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A `resolvedRoot`-tól relatív, `/`-normalizált projekt-relatív útvonal (a `sourceFilePath`
|
|
83
|
+
* chunk-mezőé, dsgn-004 §3). Windows-on a backslash-eket előre-perjelre normalizálja (stabil,
|
|
84
|
+
* platform-független `sourceFilePath` a delta-kulcshoz).
|
|
85
|
+
*/
|
|
86
|
+
static toRelative(resolvedRoot, absolutePath) {
|
|
87
|
+
const relative = path.relative(resolvedRoot, absolutePath);
|
|
88
|
+
return relative.replace(/\\/g, '/');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.FAM_ScanPath_Util = FAM_ScanPath_Util;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_SecretExclude_Util = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_SecretExclude_Util` (SP-4.1, dsgn-004 §6.3) — a scan-réteg **fájlnév-/útvonal-alapú**
|
|
6
|
+
* secret-kizárása. A felsorolt minták (`.env*`, `*.key`, `*.pem`, `*.p12`, `id_rsa*`, `*secret*`,
|
|
7
|
+
* `*credential*`, `*.crt`, `secrets/` mappa) **SOHA** nem ingestálódnak; a kizárás a file-típus-
|
|
8
|
+
* routing **ELŐTT** fut, és a kizárt fájlok a `filesSkipped`-be számolódnak (audit, NEM néma).
|
|
9
|
+
*
|
|
10
|
+
* **HATÁR (dsgn-004 §6 megjegyzés + `MP-4-detailed` kockázat) — KIFEJEZETTEN BACKLOG:** ez CSAK
|
|
11
|
+
* fájlnév-/útvonal-alapú kizárás. A **tartalmi** PII/secret-detektálás (a fájlon BELÜLI titkok
|
|
12
|
+
* kiszűrése, pl. egy `.ts`-be ágyazott API-key) NINCS implementálva MVP1-ben — false sense of
|
|
13
|
+
* safety elkerülése végett ezt itt + a run-riportban egyértelműen jelezzük. A tartalmi szkennelés
|
|
14
|
+
* későbbi fázis (BACKLOG / bedrock-igény).
|
|
15
|
+
*/
|
|
16
|
+
class FAM_SecretExclude_Util {
|
|
17
|
+
/**
|
|
18
|
+
* A beépített secret-fájlnév-minták (dsgn-004 §6.3). Ezek a `scan.ignorePatterns` config
|
|
19
|
+
* biztonsági default-jával átfedők (dsgn-007 §4.5 `SCAN_IGNORE_PATTERNS_DEFAULT`), de a scan
|
|
20
|
+
* ettől FÜGGETLENÜL is alkalmazza őket (a config-override NEM kapcsolhatja ki a secret-védelmet).
|
|
21
|
+
* A vizsgálat a fájlnévre (regex) + az útvonal-szegmensekre (`secrets/`) megy.
|
|
22
|
+
*/
|
|
23
|
+
static SECRET_FILENAME_PATTERNS = [
|
|
24
|
+
/^\.env(\..*)?$/i, // .env, .env.local, .env.production, ...
|
|
25
|
+
/\.key$/i, // *.key
|
|
26
|
+
/\.pem$/i, // *.pem
|
|
27
|
+
/\.p12$/i, // *.p12
|
|
28
|
+
/\.crt$/i, // *.crt (cert)
|
|
29
|
+
/\.pfx$/i, // *.pfx (cert)
|
|
30
|
+
/^id_rsa.*$/i, // id_rsa, id_rsa.pub, ...
|
|
31
|
+
/^id_ed25519.*$/i, // id_ed25519, id_ed25519.pub
|
|
32
|
+
/secret/i, // *secret* (bárhol a fájlnévben)
|
|
33
|
+
/credential/i, // *credential* (bárhol a fájlnévben)
|
|
34
|
+
];
|
|
35
|
+
/** A secret-mappák (az útvonal BÁRMELY szegmense — pl. `secrets/`, `.ssh/`). */
|
|
36
|
+
static SECRET_DIR_SEGMENTS = ['secrets', '.ssh', '.gnupg'];
|
|
37
|
+
/**
|
|
38
|
+
* Secret-e a (projekt-relatív, `/`-normalizált) útvonal — fájlnév-minta VAGY secret-mappa-
|
|
39
|
+
* szegmens alapján (dsgn-004 §6.3). `true` → SOHA nem ingestálódik (`filesSkipped`, audit).
|
|
40
|
+
*/
|
|
41
|
+
static isSecret(relativePath) {
|
|
42
|
+
const normalized = relativePath.replace(/\\/g, '/');
|
|
43
|
+
const segments = normalized.split('/').filter((segment) => segment.length);
|
|
44
|
+
const fileName = segments[segments.length - 1] ?? normalized;
|
|
45
|
+
// (1) Secret-mappa az útvonal bármely (nem-utolsó) szegmensében.
|
|
46
|
+
const dirSegments = segments.slice(0, -1).map((segment) => segment.toLowerCase());
|
|
47
|
+
if (dirSegments.some((segment) => FAM_SecretExclude_Util.SECRET_DIR_SEGMENTS.includes(segment))) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// (2) Secret-fájlnév-minta.
|
|
51
|
+
return FAM_SecretExclude_Util.SECRET_FILENAME_PATTERNS.some((pattern) => pattern.test(fileName));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.FAM_SecretExclude_Util = FAM_SecretExclude_Util;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_SlidingChunker_Util = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_SlidingChunker_Util` (SP-4.2, dsgn-004 §3.3) — generic sliding-window chunker minden dedikált
|
|
6
|
+
* stratégia nélküli `documents`/`codebase` fájlra (config, JSON, plain text, **`.html` markup**,
|
|
7
|
+
* `.txt`, `.rst`). A CCAP `PS_SimpleChunker_Util` projekt-lokális portja: bekezdés-határra
|
|
8
|
+
* (dupla-újsor) próbál osztani, majd a chunk-méret-plafonig épít, overlap-pal lép tovább. A
|
|
9
|
+
* break-pointokat sor-/bekezdés-határra igazítja (NEM nyers karakter-vágás), de hierarchia-megőrzés
|
|
10
|
+
* NÉLKÜL (`headingPath = []`). Méret a config-ból (`chunk.generic.*`), default `1200/150`.
|
|
11
|
+
*
|
|
12
|
+
* `chunkType = 'generic'`. A `position.charStart`/`charEnd` a forrás-szöveg tényleges karakter-
|
|
13
|
+
* tartományára mutat (a bekezdés-pozíció `indexOf`-fal feloldva, a CCAP-minta szerint).
|
|
14
|
+
*/
|
|
15
|
+
class FAM_SlidingChunker_Util {
|
|
16
|
+
/**
|
|
17
|
+
* Egy strukturálatlan szöveg chunkolása (dsgn-004 §3.3). Üres → üres lista (a dispatcher
|
|
18
|
+
* tiny-fallback-je veszi át). Bekezdés-alapú összeépítés overlap-pal; egy óriás-bekezdés a
|
|
19
|
+
* `maxSize * 1.5` felett kényszer-emittál (ne fusson el a memória).
|
|
20
|
+
*/
|
|
21
|
+
static chunkText(text, sizing) {
|
|
22
|
+
if (!text || !text.trim()) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const paragraphs = text.split(/\r?\n\r?\n/).filter((paragraph) => paragraph.trim().length);
|
|
26
|
+
const chunks = [];
|
|
27
|
+
let currentText = '';
|
|
28
|
+
let currentCharStart = 0;
|
|
29
|
+
let charCursor = 0;
|
|
30
|
+
for (const paragraph of paragraphs) {
|
|
31
|
+
const paragraphStart = text.indexOf(paragraph, charCursor);
|
|
32
|
+
if (paragraphStart >= 0) {
|
|
33
|
+
charCursor = paragraphStart;
|
|
34
|
+
}
|
|
35
|
+
if (currentText.length && currentText.length + paragraph.length + 2 > sizing.maxSize) {
|
|
36
|
+
FAM_SlidingChunker_Util.emit(chunks, currentText.trim(), currentCharStart, sizing.minSize);
|
|
37
|
+
if (currentText.length > sizing.overlap) {
|
|
38
|
+
currentText = currentText.slice(-sizing.overlap);
|
|
39
|
+
currentCharStart = Math.max(0, charCursor - sizing.overlap);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
currentText = '';
|
|
43
|
+
currentCharStart = charCursor;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!currentText) {
|
|
47
|
+
currentCharStart = charCursor;
|
|
48
|
+
}
|
|
49
|
+
currentText += (currentText ? '\n\n' : '') + paragraph;
|
|
50
|
+
charCursor = paragraphStart >= 0 ? paragraphStart + paragraph.length : charCursor + paragraph.length;
|
|
51
|
+
// Egy óriás-bekezdés is túl nagy → kényszer-emit.
|
|
52
|
+
if (currentText.length > sizing.maxSize * 1.5) {
|
|
53
|
+
FAM_SlidingChunker_Util.emit(chunks, currentText.trim(), currentCharStart, sizing.minSize);
|
|
54
|
+
currentText = '';
|
|
55
|
+
currentCharStart = charCursor;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (currentText.trim().length >= sizing.minSize) {
|
|
59
|
+
FAM_SlidingChunker_Util.emit(chunks, currentText.trim(), currentCharStart, sizing.minSize);
|
|
60
|
+
}
|
|
61
|
+
return chunks;
|
|
62
|
+
}
|
|
63
|
+
/** Egy chunk hozzáadása `chunkType='generic'`-kel (zaj-szűrés a `minSize`-ra). */
|
|
64
|
+
static emit(chunks, content, charStart, minSize) {
|
|
65
|
+
if (content.length < minSize) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
chunks.push({
|
|
69
|
+
content: content,
|
|
70
|
+
position: { charStart: charStart, charEnd: charStart + content.length },
|
|
71
|
+
chunkType: 'generic',
|
|
72
|
+
headingPath: [],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.FAM_SlidingChunker_Util = FAM_SlidingChunker_Util;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_TsChunker_Util = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_TsChunker_Util` (SP-4.2, dsgn-004 §3.1, **BFR-AM-005 workaround**) — TypeScript-szemantikus
|
|
6
|
+
* chunker `.ts/.tsx/.mts/.js/.jsx`-re. A CCAP `PS_TypeScriptChunker_Util` regex/brace-depth
|
|
7
|
+
* megközelítésének projekt-lokális portja: import-csoport → top-level deklarációk (class/interface/
|
|
8
|
+
* enum/type/function/const-export) → osztály-blokk (a metódus-bontás brace-depth-fel a nagy-blokk
|
|
9
|
+
* ágon történik) → JSDoc + `@Decorator` a következő kód-chunkkal együtt → nagy blokk soronkénti
|
|
10
|
+
* sliding-window-ra bomlik. A méret-konstansok a config-ból (`chunk.ts.*`), default `1500/100/50`.
|
|
11
|
+
*
|
|
12
|
+
* **BFR-AM-005 (dsgn-004 §3.1 megjegyzés):** a regex/brace-depth téved string-literálban lévő `{`/`}`
|
|
13
|
+
* vagy template-literál esetén — a `countBraces` konzervatívan kihagyja a string-/line-comment-
|
|
14
|
+
* tartalmat, és a tiny-fallback safety-net (a dispatcher) elkapja a degenerált eseteket. Az
|
|
15
|
+
* AST-pontos darabolás (tree-sitter / TS-compiler) bedrock-szinten készül (MP-15).
|
|
16
|
+
*
|
|
17
|
+
* **headingPath = kód-symbol-lánc** (dsgn-001 megengedi: "MD heading-lánc VAGY kód-symbol-lánc"):
|
|
18
|
+
* pl. `['class:FooService']`; a nagy-blokk sub-chunkjai öröklik a szülő symbol-láncát + a
|
|
19
|
+
* `chunkType`-ot.
|
|
20
|
+
*/
|
|
21
|
+
class FAM_TsChunker_Util {
|
|
22
|
+
static IMPORT_LINE = /^import\s/;
|
|
23
|
+
static EXPORT_FROM_LINE = /^export\s.*\sfrom\s/;
|
|
24
|
+
static CLASS_START = /^(?:export\s+)?(?:default\s+)?(?:abstract\s+)?class\s+(\w+)/;
|
|
25
|
+
static INTERFACE_START = /^(?:export\s+)?interface\s+(\w+)/;
|
|
26
|
+
static ENUM_START = /^(?:export\s+)?(?:const\s+)?enum\s+(\w+)/;
|
|
27
|
+
static TYPE_ALIAS_START = /^(?:export\s+)?type\s+(\w+)\s*=/;
|
|
28
|
+
static STANDALONE_FUNCTION = /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(\w+)/;
|
|
29
|
+
static CONST_EXPORT = /^(?:export\s+)?const\s+(\w+)/;
|
|
30
|
+
static DECORATOR_LINE = /^\s*@\w+/;
|
|
31
|
+
static JSDOC_OPEN = /^\s*\/\*\*/;
|
|
32
|
+
static JSDOC_CLOSE = /\*\//;
|
|
33
|
+
/**
|
|
34
|
+
* Egy TS/JS forrás chunkolása szemantikus blokkok mentén (dsgn-004 §3.1). Üres / whitespace-only
|
|
35
|
+
* tartalom → üres lista (a dispatcher tiny-fallback-je veszi át). A nagy (max-size feletti)
|
|
36
|
+
* blokkok soronkénti sliding-window-ra bomlanak (örökölt `chunkType` + symbol-`headingPath`).
|
|
37
|
+
*/
|
|
38
|
+
static chunkText(text, sizing) {
|
|
39
|
+
if (!text || !text.trim()) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const lines = text.split(/\r?\n/);
|
|
43
|
+
const blocks = FAM_TsChunker_Util.identifyBlocks(lines);
|
|
44
|
+
const chunks = [];
|
|
45
|
+
for (const block of blocks) {
|
|
46
|
+
const blockText = block.lines.join('\n');
|
|
47
|
+
const headingPath = FAM_TsChunker_Util.buildHeadingPath(block);
|
|
48
|
+
if (blockText.length <= sizing.maxSize) {
|
|
49
|
+
if (blockText.trim().length >= sizing.minSize) {
|
|
50
|
+
chunks.push({
|
|
51
|
+
content: blockText,
|
|
52
|
+
position: {
|
|
53
|
+
charStart: block.charStart,
|
|
54
|
+
charEnd: block.charStart + blockText.length,
|
|
55
|
+
lineStart: block.lineStart,
|
|
56
|
+
lineEnd: block.lineEnd,
|
|
57
|
+
},
|
|
58
|
+
chunkType: block.blockType,
|
|
59
|
+
headingPath: headingPath,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Túl nagy blokk → soronkénti sliding-window (örökölt chunkType + symbol-lánc).
|
|
65
|
+
chunks.push(...FAM_TsChunker_Util.splitLargeBlock(block, sizing, headingPath));
|
|
66
|
+
}
|
|
67
|
+
return chunks;
|
|
68
|
+
}
|
|
69
|
+
/** Pass 1: top-level blokkok azonosítása soronkénti feldolgozással (brace-depth tracking). */
|
|
70
|
+
static identifyBlocks(lines) {
|
|
71
|
+
const blocks = [];
|
|
72
|
+
let currentBlock = undefined;
|
|
73
|
+
let braceDepth = 0;
|
|
74
|
+
let inBlockBody = false;
|
|
75
|
+
let jsdocBuffer = [];
|
|
76
|
+
let jsdocStartLine = 0;
|
|
77
|
+
let decoratorBuffer = [];
|
|
78
|
+
let inJsdoc = false;
|
|
79
|
+
let charCursor = 0;
|
|
80
|
+
let importLines = [];
|
|
81
|
+
let importStartLine = -1;
|
|
82
|
+
let importCharStart = 0;
|
|
83
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
84
|
+
const line = lines[lineIdx];
|
|
85
|
+
const trimmed = line.trimStart();
|
|
86
|
+
// -- JSDoc (a következő kód-chunkkal együtt marad, dsgn-004 §3.1 (4)) --
|
|
87
|
+
if (!inJsdoc && FAM_TsChunker_Util.JSDOC_OPEN.test(trimmed)) {
|
|
88
|
+
inJsdoc = true;
|
|
89
|
+
jsdocBuffer = [line];
|
|
90
|
+
jsdocStartLine = lineIdx;
|
|
91
|
+
if (FAM_TsChunker_Util.JSDOC_CLOSE.test(trimmed)) {
|
|
92
|
+
inJsdoc = false;
|
|
93
|
+
}
|
|
94
|
+
charCursor += line.length + 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (inJsdoc) {
|
|
98
|
+
jsdocBuffer.push(line);
|
|
99
|
+
if (FAM_TsChunker_Util.JSDOC_CLOSE.test(trimmed)) {
|
|
100
|
+
inJsdoc = false;
|
|
101
|
+
}
|
|
102
|
+
charCursor += line.length + 1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// -- Blokk body-jában (brace-depth tracking) --
|
|
106
|
+
if (inBlockBody && currentBlock) {
|
|
107
|
+
currentBlock.lines.push(line);
|
|
108
|
+
currentBlock.lineEnd = lineIdx + 1;
|
|
109
|
+
braceDepth += FAM_TsChunker_Util.countBraces(line);
|
|
110
|
+
if (braceDepth <= 0) {
|
|
111
|
+
inBlockBody = false;
|
|
112
|
+
blocks.push(currentBlock);
|
|
113
|
+
currentBlock = undefined;
|
|
114
|
+
braceDepth = 0;
|
|
115
|
+
}
|
|
116
|
+
charCursor += line.length + 1;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
// -- Decorator (a következő kód-chunkkal együtt) --
|
|
120
|
+
if (FAM_TsChunker_Util.DECORATOR_LINE.test(trimmed)) {
|
|
121
|
+
decoratorBuffer.push(line);
|
|
122
|
+
charCursor += line.length + 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// -- Import-csoport (dsgn-004 §3.1 (1)) --
|
|
126
|
+
if (FAM_TsChunker_Util.IMPORT_LINE.test(trimmed) || FAM_TsChunker_Util.EXPORT_FROM_LINE.test(trimmed)) {
|
|
127
|
+
jsdocBuffer = [];
|
|
128
|
+
decoratorBuffer = [];
|
|
129
|
+
if (importStartLine === -1) {
|
|
130
|
+
importStartLine = lineIdx;
|
|
131
|
+
importCharStart = charCursor;
|
|
132
|
+
}
|
|
133
|
+
importLines.push(line);
|
|
134
|
+
charCursor += line.length + 1;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// -- Import-csoport lezárása (más jön) --
|
|
138
|
+
if (importLines.length) {
|
|
139
|
+
blocks.push({
|
|
140
|
+
blockType: 'imports',
|
|
141
|
+
lines: importLines,
|
|
142
|
+
lineStart: importStartLine + 1,
|
|
143
|
+
lineEnd: importStartLine + importLines.length,
|
|
144
|
+
charStart: importCharStart,
|
|
145
|
+
});
|
|
146
|
+
importLines = [];
|
|
147
|
+
importStartLine = -1;
|
|
148
|
+
}
|
|
149
|
+
// -- Üres sor → pufferek ürítése --
|
|
150
|
+
if (!trimmed) {
|
|
151
|
+
jsdocBuffer = [];
|
|
152
|
+
decoratorBuffer = [];
|
|
153
|
+
charCursor += line.length + 1;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// -- Top-level deklaráció detektálás --
|
|
157
|
+
const detected = FAM_TsChunker_Util.detectDeclaration(trimmed);
|
|
158
|
+
const blockLines = [...jsdocBuffer, ...decoratorBuffer, line];
|
|
159
|
+
const blockStartLine = jsdocBuffer.length ? jsdocStartLine + 1 : lineIdx + 1;
|
|
160
|
+
const prefixChars = [...jsdocBuffer, ...decoratorBuffer]
|
|
161
|
+
.reduce((sum, bufferLine) => sum + bufferLine.length + 1, 0);
|
|
162
|
+
const blockCharStart = Math.max(0, charCursor - prefixChars);
|
|
163
|
+
braceDepth = FAM_TsChunker_Util.countBraces(line);
|
|
164
|
+
if (braceDepth > 0) {
|
|
165
|
+
// Több-soros blokk-body folytatódik.
|
|
166
|
+
inBlockBody = true;
|
|
167
|
+
currentBlock = {
|
|
168
|
+
blockType: detected.blockType,
|
|
169
|
+
lines: blockLines,
|
|
170
|
+
lineStart: blockStartLine,
|
|
171
|
+
lineEnd: lineIdx + 1,
|
|
172
|
+
charStart: blockCharStart,
|
|
173
|
+
entityName: detected.entityName,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Egy-soros blokk (type-alias / rövid const).
|
|
178
|
+
if (blockLines.join('\n').trim().length) {
|
|
179
|
+
blocks.push({
|
|
180
|
+
blockType: detected.blockType,
|
|
181
|
+
lines: blockLines,
|
|
182
|
+
lineStart: blockStartLine,
|
|
183
|
+
lineEnd: lineIdx + 1,
|
|
184
|
+
charStart: blockCharStart,
|
|
185
|
+
entityName: detected.entityName,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
braceDepth = 0;
|
|
189
|
+
}
|
|
190
|
+
jsdocBuffer = [];
|
|
191
|
+
decoratorBuffer = [];
|
|
192
|
+
charCursor += line.length + 1;
|
|
193
|
+
}
|
|
194
|
+
// -- Maradék import-csoport --
|
|
195
|
+
if (importLines.length) {
|
|
196
|
+
blocks.push({
|
|
197
|
+
blockType: 'imports',
|
|
198
|
+
lines: importLines,
|
|
199
|
+
lineStart: importStartLine + 1,
|
|
200
|
+
lineEnd: importStartLine + importLines.length,
|
|
201
|
+
charStart: importCharStart,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
// -- Maradék nyitott blokk (lezáratlan brace) --
|
|
205
|
+
if (currentBlock) {
|
|
206
|
+
blocks.push(currentBlock);
|
|
207
|
+
}
|
|
208
|
+
return blocks;
|
|
209
|
+
}
|
|
210
|
+
/** Egy sor → a kezdő-deklaráció típusa + neve (a `chunkType` + symbol-`headingPath`-hoz). */
|
|
211
|
+
static detectDeclaration(trimmed) {
|
|
212
|
+
const classMatch = trimmed.match(FAM_TsChunker_Util.CLASS_START);
|
|
213
|
+
if (classMatch) {
|
|
214
|
+
return { blockType: 'class', entityName: classMatch[1] };
|
|
215
|
+
}
|
|
216
|
+
const ifaceMatch = trimmed.match(FAM_TsChunker_Util.INTERFACE_START);
|
|
217
|
+
if (ifaceMatch) {
|
|
218
|
+
return { blockType: 'interface', entityName: ifaceMatch[1] };
|
|
219
|
+
}
|
|
220
|
+
const enumMatch = trimmed.match(FAM_TsChunker_Util.ENUM_START);
|
|
221
|
+
if (enumMatch) {
|
|
222
|
+
return { blockType: 'enum', entityName: enumMatch[1] };
|
|
223
|
+
}
|
|
224
|
+
const typeMatch = trimmed.match(FAM_TsChunker_Util.TYPE_ALIAS_START);
|
|
225
|
+
if (typeMatch) {
|
|
226
|
+
return { blockType: 'type', entityName: typeMatch[1] };
|
|
227
|
+
}
|
|
228
|
+
const funcMatch = trimmed.match(FAM_TsChunker_Util.STANDALONE_FUNCTION);
|
|
229
|
+
if (funcMatch) {
|
|
230
|
+
return { blockType: 'function', entityName: funcMatch[1] };
|
|
231
|
+
}
|
|
232
|
+
const constMatch = trimmed.match(FAM_TsChunker_Util.CONST_EXPORT);
|
|
233
|
+
if (constMatch) {
|
|
234
|
+
return { blockType: 'function', entityName: constMatch[1] };
|
|
235
|
+
}
|
|
236
|
+
return { blockType: 'generic' };
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Brace-mélység egy sorban (nyitó − záró `{}`), a string-literálok + line-comment kihagyásával
|
|
240
|
+
* (BFR-AM-005 konzervatív megközelítés — NEM AST). Template-literál `${...}` belsejét nem
|
|
241
|
+
* elemzi mélyen; a degenerált eseteket a tiny-fallback safety-net + a max-size sliding fedi.
|
|
242
|
+
*/
|
|
243
|
+
static countBraces(line) {
|
|
244
|
+
let depth = 0;
|
|
245
|
+
let inString = false;
|
|
246
|
+
let stringChar = '';
|
|
247
|
+
for (let i = 0; i < line.length; i++) {
|
|
248
|
+
const char = line[i];
|
|
249
|
+
const prev = i > 0 ? line[i - 1] : '';
|
|
250
|
+
if (!inString && char === '/' && line[i + 1] === '/') {
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
if (!inString && (char === '"' || char === '\'' || char === '`')) {
|
|
254
|
+
inString = true;
|
|
255
|
+
stringChar = char;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (inString && char === stringChar && prev !== '\\') {
|
|
259
|
+
inString = false;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (inString) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (char === '{') {
|
|
266
|
+
depth++;
|
|
267
|
+
}
|
|
268
|
+
if (char === '}') {
|
|
269
|
+
depth--;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return depth;
|
|
273
|
+
}
|
|
274
|
+
/** Nagy blokk soronkénti sliding-window-ja (örökölt chunkType + symbol-`headingPath`). */
|
|
275
|
+
static splitLargeBlock(block, sizing, headingPath) {
|
|
276
|
+
const chunks = [];
|
|
277
|
+
const lines = block.lines;
|
|
278
|
+
const overlapLines = Math.max(1, Math.floor(sizing.overlap / 80));
|
|
279
|
+
let startIdx = 0;
|
|
280
|
+
while (startIdx < lines.length) {
|
|
281
|
+
let currentText = '';
|
|
282
|
+
let endIdx = startIdx;
|
|
283
|
+
while (endIdx < lines.length) {
|
|
284
|
+
const candidate = currentText + (currentText ? '\n' : '') + lines[endIdx];
|
|
285
|
+
if (candidate.length > sizing.maxSize && currentText.length) {
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
currentText = candidate;
|
|
289
|
+
endIdx++;
|
|
290
|
+
}
|
|
291
|
+
if (currentText.trim().length >= sizing.minSize) {
|
|
292
|
+
chunks.push({
|
|
293
|
+
content: currentText,
|
|
294
|
+
position: {
|
|
295
|
+
charStart: block.charStart,
|
|
296
|
+
charEnd: block.charStart + currentText.length,
|
|
297
|
+
lineStart: block.lineStart + startIdx,
|
|
298
|
+
lineEnd: block.lineStart + endIdx - 1,
|
|
299
|
+
},
|
|
300
|
+
chunkType: block.blockType,
|
|
301
|
+
headingPath: headingPath,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
startIdx = Math.max(startIdx + 1, endIdx - overlapLines);
|
|
305
|
+
}
|
|
306
|
+
return chunks;
|
|
307
|
+
}
|
|
308
|
+
/** A symbol-`headingPath` egy blokkhoz (`['class:Foo']`); generic / névtelen → üres tömb. */
|
|
309
|
+
static buildHeadingPath(block) {
|
|
310
|
+
if (block.entityName && block.blockType !== 'generic' && block.blockType !== 'imports') {
|
|
311
|
+
return [`${block.blockType}:${block.entityName}`];
|
|
312
|
+
}
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
exports.FAM_TsChunker_Util = FAM_TsChunker_Util;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_Chunker_ControlService = void 0;
|
|
4
|
+
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
5
|
+
const fam_file_routing_util_1 = require("../_collections/fam-file-routing.util");
|
|
6
|
+
const fam_ts_chunker_util_1 = require("../_collections/fam-ts-chunker.util");
|
|
7
|
+
const fam_md_chunker_util_1 = require("../_collections/fam-md-chunker.util");
|
|
8
|
+
const fam_sliding_chunker_util_1 = require("../_collections/fam-sliding-chunker.util");
|
|
9
|
+
/**
|
|
10
|
+
* `FAM_Chunker_ControlService` (SP-4.2, dsgn-004 §3) — a chunker-dispatch. A route + kiterjesztés
|
|
11
|
+
* alapján stratégiát választ (TS-semantic | markdown | generic-sliding), a méret-konstansokat a
|
|
12
|
+
* config-ból oldja fel (`resolveConfig(key, {table, scopePath})`, dsgn-007 — default a CCAP-érték),
|
|
13
|
+
* a chunker nyers kimenetét (`FAM_RawChunk[]`) a `FAM_Entry` chunk-mezőire (`chunkIndex`/`chunkTotal`)
|
|
14
|
+
* normalizálja, és a **tiny-file fallback**-et alkalmazza (ha egyetlen stratégia sem ad chunkot
|
|
15
|
+
* VAGY a fájl a `chunk.fallbackWholeFileUnder` alatt van → 1 db `chunkType='fallback'` chunk, hogy
|
|
16
|
+
* semmi ne essen ki, dsgn-004 §3.4).
|
|
17
|
+
*
|
|
18
|
+
* Singleton (a config-resolve / dispatch állapot-mentes; az egységes belépés a hot-path-nak).
|
|
19
|
+
*/
|
|
20
|
+
class FAM_Chunker_ControlService {
|
|
21
|
+
static _instance;
|
|
22
|
+
static getInstance() {
|
|
23
|
+
if (!FAM_Chunker_ControlService._instance) {
|
|
24
|
+
FAM_Chunker_ControlService._instance = new FAM_Chunker_ControlService();
|
|
25
|
+
}
|
|
26
|
+
return FAM_Chunker_ControlService._instance;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Egy fájl tartalmának chunkolása a route + kiterjesztés szerinti stratégiával (dsgn-004 §3). A
|
|
30
|
+
* méret-konstansok a config-ból (`table`+`scopePath` kontextusra) feloldva; a tiny-fallback a
|
|
31
|
+
* `chunk.fallbackWholeFileUnder` alatt VAGY üres-chunklista esetén. A `chunkIndex`/`chunkTotal` a
|
|
32
|
+
* teljes batch-re konzisztens (a delta-kulcshoz, SP-4.3).
|
|
33
|
+
*/
|
|
34
|
+
async chunkFile(set) {
|
|
35
|
+
const strategy = fam_file_routing_util_1.FAM_FileRouting_Util.chunkStrategyFor(set.relativePath);
|
|
36
|
+
const fallbackUnder = await this.resolveNumber('chunk.fallbackWholeFileUnder', set.table, set.scopePath, 50);
|
|
37
|
+
// Tiny-file fallback (a `fallbackWholeFileUnder` alatt → 1 chunk, dsgn-004 §3.4).
|
|
38
|
+
if (set.content.trim().length > 0 && set.content.length < fallbackUnder) {
|
|
39
|
+
return [this.fallbackChunk(set.content)];
|
|
40
|
+
}
|
|
41
|
+
const sizing = await this.resolveSizing(strategy, set.table, set.scopePath);
|
|
42
|
+
const rawChunks = this.runStrategy(strategy, set.content, sizing);
|
|
43
|
+
// Üres-chunklista de NEM üres tartalom → tiny-fallback (semmi ne essen ki).
|
|
44
|
+
if (!rawChunks.length) {
|
|
45
|
+
if (!set.content.trim().length) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
return [this.fallbackChunk(set.content)];
|
|
49
|
+
}
|
|
50
|
+
return this.assignIndices(rawChunks);
|
|
51
|
+
}
|
|
52
|
+
/** A stratégia futtatása (TS / MD / generic). */
|
|
53
|
+
runStrategy(strategy, content, sizing) {
|
|
54
|
+
if (strategy === 'ts') {
|
|
55
|
+
return fam_ts_chunker_util_1.FAM_TsChunker_Util.chunkText(content, sizing);
|
|
56
|
+
}
|
|
57
|
+
if (strategy === 'md') {
|
|
58
|
+
return fam_md_chunker_util_1.FAM_MdChunker_Util.chunkText(content, sizing);
|
|
59
|
+
}
|
|
60
|
+
return fam_sliding_chunker_util_1.FAM_SlidingChunker_Util.chunkText(content, sizing);
|
|
61
|
+
}
|
|
62
|
+
/** A teljes tartalom egyetlen `fallback`-chunkként (tiny-file fallback, dsgn-004 §3.4). */
|
|
63
|
+
fallbackChunk(content) {
|
|
64
|
+
const trimmed = content.trim();
|
|
65
|
+
return {
|
|
66
|
+
content: trimmed,
|
|
67
|
+
chunkIndex: 0,
|
|
68
|
+
chunkTotal: 1,
|
|
69
|
+
chunkType: 'fallback',
|
|
70
|
+
position: { charStart: 0, charEnd: content.length },
|
|
71
|
+
headingPath: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** A nyers chunkokra a 0-alapú `chunkIndex` + a közös `chunkTotal` (a delta-kulcshoz). */
|
|
75
|
+
assignIndices(rawChunks) {
|
|
76
|
+
const total = rawChunks.length;
|
|
77
|
+
return rawChunks.map((raw, index) => ({
|
|
78
|
+
content: raw.content,
|
|
79
|
+
chunkIndex: index,
|
|
80
|
+
chunkTotal: total,
|
|
81
|
+
chunkType: raw.chunkType,
|
|
82
|
+
position: raw.position,
|
|
83
|
+
headingPath: raw.headingPath,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
/** A stratégia méret-konstansai a config-ból (default a CCAP-érték, dsgn-007 §4.2). */
|
|
87
|
+
async resolveSizing(strategy, table, scopePath) {
|
|
88
|
+
if (strategy === 'ts') {
|
|
89
|
+
return {
|
|
90
|
+
maxSize: await this.resolveNumber('chunk.ts.maxSize', table, scopePath, 1500),
|
|
91
|
+
overlap: await this.resolveNumber('chunk.ts.overlap', table, scopePath, 100),
|
|
92
|
+
minSize: await this.resolveNumber('chunk.ts.minSize', table, scopePath, 50),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (strategy === 'md') {
|
|
96
|
+
return {
|
|
97
|
+
maxSize: await this.resolveNumber('chunk.md.maxSize', table, scopePath, 1200),
|
|
98
|
+
overlap: await this.resolveNumber('chunk.md.overlap', table, scopePath, 150),
|
|
99
|
+
minSize: await this.resolveNumber('chunk.md.minSize', table, scopePath, 80),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
maxSize: await this.resolveNumber('chunk.generic.maxSize', table, scopePath, 1200),
|
|
104
|
+
overlap: await this.resolveNumber('chunk.generic.overlap', table, scopePath, 150),
|
|
105
|
+
minSize: await this.resolveNumber('chunk.generic.minSize', table, scopePath, 50),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Egy numerikus config-kulcs feloldása (table+scope kontextus); nem-szám → a builtin fallback. */
|
|
109
|
+
async resolveNumber(key, table, scopePath, fallback) {
|
|
110
|
+
const resolved = await config_control_service_1.FAM_Config_ControlService.getInstance().resolve(key, { table: table, scopePath: scopePath });
|
|
111
|
+
return typeof resolved.value === 'number' ? resolved.value : fallback;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.FAM_Chunker_ControlService = FAM_Chunker_ControlService;
|