@untangle-code/chunker 0.0.0-stage → 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.
Files changed (75) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +90 -0
  3. package/README.md +206 -2
  4. package/THIRD_PARTY_LICENSES.md +24 -0
  5. package/dist/byte-text.d.ts +39 -0
  6. package/dist/byte-text.d.ts.map +1 -0
  7. package/dist/byte-text.js +59 -0
  8. package/dist/byte-text.js.map +1 -0
  9. package/dist/chunker.d.ts +11 -0
  10. package/dist/chunker.d.ts.map +1 -0
  11. package/dist/chunker.js +112 -0
  12. package/dist/chunker.js.map +1 -0
  13. package/dist/config.d.ts +17 -0
  14. package/dist/config.d.ts.map +1 -0
  15. package/dist/config.js +28 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/context.d.ts +48 -0
  18. package/dist/context.d.ts.map +1 -0
  19. package/dist/context.js +0 -0
  20. package/dist/context.js.map +1 -0
  21. package/dist/coverage.d.ts +44 -0
  22. package/dist/coverage.d.ts.map +1 -0
  23. package/dist/coverage.js +95 -0
  24. package/dist/coverage.js.map +1 -0
  25. package/dist/index.d.ts +23 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +28 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/minified-detection.d.ts +27 -0
  30. package/dist/minified-detection.d.ts.map +1 -0
  31. package/dist/minified-detection.js +46 -0
  32. package/dist/minified-detection.js.map +1 -0
  33. package/dist/prepared-text.d.ts +48 -0
  34. package/dist/prepared-text.d.ts.map +1 -0
  35. package/dist/prepared-text.js +57 -0
  36. package/dist/prepared-text.js.map +1 -0
  37. package/dist/split.d.ts +36 -0
  38. package/dist/split.d.ts.map +1 -0
  39. package/dist/split.js +126 -0
  40. package/dist/split.js.map +1 -0
  41. package/dist/strategies/ast-chunk-strategy.d.ts +52 -0
  42. package/dist/strategies/ast-chunk-strategy.d.ts.map +1 -0
  43. package/dist/strategies/ast-chunk-strategy.js +0 -0
  44. package/dist/strategies/ast-chunk-strategy.js.map +1 -0
  45. package/dist/strategies/line-fallback-strategy.d.ts +4 -0
  46. package/dist/strategies/line-fallback-strategy.d.ts.map +1 -0
  47. package/dist/strategies/line-fallback-strategy.js +79 -0
  48. package/dist/strategies/line-fallback-strategy.js.map +1 -0
  49. package/dist/strategies/minified-strategy.d.ts +4 -0
  50. package/dist/strategies/minified-strategy.d.ts.map +1 -0
  51. package/dist/strategies/minified-strategy.js +42 -0
  52. package/dist/strategies/minified-strategy.js.map +1 -0
  53. package/dist/token-estimator.d.ts +28 -0
  54. package/dist/token-estimator.d.ts.map +1 -0
  55. package/dist/token-estimator.js +37 -0
  56. package/dist/token-estimator.js.map +1 -0
  57. package/dist/types.d.ts +114 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +20 -0
  60. package/dist/types.js.map +1 -0
  61. package/package.json +61 -3
  62. package/src/byte-text.ts +85 -0
  63. package/src/chunker.ts +142 -0
  64. package/src/config.ts +32 -0
  65. package/src/context.ts +0 -0
  66. package/src/coverage.ts +143 -0
  67. package/src/index.ts +63 -0
  68. package/src/minified-detection.ts +66 -0
  69. package/src/prepared-text.ts +118 -0
  70. package/src/split.ts +164 -0
  71. package/src/strategies/ast-chunk-strategy.ts +0 -0
  72. package/src/strategies/line-fallback-strategy.ts +108 -0
  73. package/src/strategies/minified-strategy.ts +49 -0
  74. package/src/token-estimator.ts +48 -0
  75. package/src/types.ts +138 -0
package/NOTICE ADDED
@@ -0,0 +1,90 @@
1
+ # NOTICE
2
+
3
+ Copyright (C) 2026 Luke Bölling and contributors.
4
+
5
+ This attribution covers original Untangle work. Contributors retain copyright in
6
+ their contributions. Third-party packages, grammars and other materials retain
7
+ their own copyright holders, licenses and notices; this line does not replace them.
8
+
9
+ ## License — binding decision
10
+
11
+ Untangle is licensed under the **GNU Affero General Public License, Version 3
12
+ (AGPL-3.0-only)**. See [`LICENSE`](./LICENSE).
13
+
14
+ This decision is **final, not provisional**, and was explicitly instructed by
15
+ the project owner. No contributor, agent or tool may change it without a new
16
+ explicit instruction.
17
+
18
+ The repository-wide license covers Untangle's first-party source code, scripts,
19
+ tests, fixtures, configuration, documentation, website and brand artwork, including
20
+ the logo and 3D yarn illustrations. Every workspace `package.json` declares
21
+ `"license": "AGPL-3.0-only"`. There is no separately licensed enterprise component
22
+ or separate permissive license for documentation or brand assets.
23
+
24
+ Third-party code, embedded grammars, dependency assets and their notices retain
25
+ their original licenses. Their inclusion does not relicense those works. The
26
+ repository license does not grant trademark rights or change the license of a
27
+ repository analyzed with Untangle. See the repository's `docs/LICENSING.md` for
28
+ scope and verification details.
29
+
30
+ This resolves the license question left open in `vision.md` §14 and completes
31
+ `product.md` §16, item 2.
32
+
33
+ ## Rationale
34
+
35
+ - **Network copyleft fits the intended product model.** AGPL §13 sets source
36
+ availability requirements for modified versions used through a computer network.
37
+ The current local CLI/stdio-MCP entry and future shared-server modes share the
38
+ same license.
39
+ - **Local-first analysis has a clear boundary.** Running Untangle against a codebase
40
+ does not replace that codebase's own license with Untangle's license.
41
+ - **First-party components share one license.** Implemented components and future
42
+ official plugins use this repository's licensing policy. Planned integrations do
43
+ not establish implemented or published products.
44
+
45
+ ## Deliberately accepted consequence
46
+
47
+ The project deliberately chooses AGPL rather than a permissive embedding license.
48
+ Redistribution, modifications and combined works must be assessed against the
49
+ actual terms in `LICENSE`, including applicable Corresponding Source requirements.
50
+ No permissive exception, commercial dual license or special contract-only package
51
+ license is granted or promised by this notice.
52
+
53
+ ## Dependencies
54
+
55
+ Third-party runtime dependencies are checked against the allowlist in
56
+ `scripts/check-licenses.mjs`. Their licenses and copyright notices remain unchanged
57
+ and traceable through package `THIRD_PARTY_LICENSES.md` files, grammar provenance,
58
+ the lockfile and release SBOM. The allowlist includes MIT / Apache-2.0 / BSD / ISC
59
+ and other explicitly listed licenses; it is not a license grant for dependencies.
60
+
61
+ The allowlist in `scripts/check-licenses.mjs` applies to **dependencies**, not
62
+ workspace packages. Untangle's own packages carry AGPL-3.0-only and are excluded
63
+ from that check.
64
+
65
+ ## Clean room
66
+
67
+ Untangle is an independent implementation with its own data models, APIs, tool
68
+ names, tests and sources. Feature ideas may be derived from publicly observable
69
+ behavior and generally known techniques. Third-party source code is not copied,
70
+ including from AGPL projects even where the license would permit it.
71
+
72
+ ## Contribution terms
73
+
74
+ Pull requests are welcome under the repository's existing AGPL-3.0-only license.
75
+ No separate CLA, copyright assignment or DCO sign-off is required. Contributors
76
+ must have the right to submit their work under those terms. See CONTRIBUTING.md.
77
+
78
+ ## Release decisions
79
+
80
+ - [x] Copyright attribution: Luke Bölling, with contributors retaining their rights.
81
+ - [x] Contributions use AGPL-3.0-only without an additional CLA or DCO gate.
82
+
83
+ ## Open legal items before public beta
84
+
85
+ - [ ] Trademark search for the product name `Untangle`.
86
+ - [ ] Registration and legal review of the npm scope `@untangle-code`.
87
+ - [ ] Verify source availability and notices for each released network daemon/server
88
+ mode (WP-59). The current stdio MCP `untangle_about` already reports the license,
89
+ source repository and build commit when supplied; metadata alone does not prove
90
+ that a concrete deployed version's source is available.
package/README.md CHANGED
@@ -1,3 +1,207 @@
1
- # Temporary Holding Version
1
+ # @untangle-code/chunker
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Lossless token-budgeted AST chunking for Untangle (work following spec.md §8, architecture.md §6.6
4
+ and §14). Internal private package, not published to npm.
5
+
6
+ Normative sources: [`spec.md`](../../spec.md) §8 (takes precedence),
7
+ [`architecture.md`](../../architecture.md) §6.6, §14, and [`vision.md`](../../vision.md) principles
8
+ 8.4 ("no silent data loss") and 8.5 ("stable identity over source position").
9
+
10
+ Its only runtime dependency is `@untangle-code/domain` (`workspace:*`). Production imports neither
11
+ `parser-registry` nor other Untangle packages; it consumes only language-neutral `LanguageAdapter`
12
+ output (`ParseResult`, `AstRegion[]`, `ExtractedSymbol[]`). The seam's region contract is documented
13
+ normatively on `AstRegion` in `packages/domain/src/analysis.ts`. The sole exception is
14
+ `@untangle-code/parser-registry` as a **devDependency** for a real seam integration test
15
+ (`src/integration.test.ts`) that parses actual TypeScript through the adapter rather than using
16
+ hand-built region trees.
17
+
18
+ <a id="was-dieses-package-tut"></a>
19
+
20
+ ## Responsibilities
21
+
22
+ `chunkFile()` (the main entry point in `src/index.ts`) receives file identity, raw content and
23
+ language-adapter output, returning `CodeChunkDraft[]` and a complete coverage ledger.
24
+ Three strategies follow architecture.md §6.6:
25
+
26
+ ```text
27
+ AstChunkStrategy — spec.md §8.3 steps 1–8 (normal case, successfully parsed file)
28
+ LineFallbackStrategy — spec.md §8.3 step 9 (parse error / no usable regions)
29
+ MinifiedStrategy — spec.md §8.3 step 10 (minified content)
30
+ ```
31
+
32
+ All use the same `BuildContext`/`emitChunk` core (`src/context.ts`), producing every `CodeChunkDraft`
33
+ with identical `chunkId` derivation (`domain.buildChunkId`), prepared-text construction and token
34
+ estimation. After each strategy, the orchestrator (`src/chunker.ts`) fills remaining byte gaps
35
+ with `fallback_lines` and checks the lossless invariant again through `domain.validateCoverage`.
36
+ This is defense in depth; a correct strategy must never leave a gap.
37
+
38
+ <a id="modulübersicht"></a>
39
+
40
+ ## Modules
41
+
42
+ | Module | Contents |
43
+ |---|---|
44
+ | `chunker.ts` | `chunkFile()`: strategy selection, gap filling, final coverage check |
45
+ | `config.ts` | Defaults and `resolveChunkerConfig()` |
46
+ | `context.ts` | `BuildContext`, sole `CodeChunkDraft` factory `emitChunk()`, `estimateTokensWithHeader()` |
47
+ | `byte-text.ts` | Byte-exact slicing and line indexes: `LineIndex`, `sliceBytes`, `safeUtf8Boundary` |
48
+ | `split.ts` | `flattenToAtoms()` (AST block splitting and statement grouping, steps 6–7), `splitByTokenBoundary()` (safe boundaries, step 8) |
49
+ | `token-estimator.ts` | Conservative offline `estimateTokens()` (architecture.md §14.4) |
50
+ | `minified-detection.ts` | Step-10 heuristic `detectMinifiedContent()` |
51
+ | `prepared-text.ts` | `buildPreparedDenseText()`/`buildBm25Text()` (spec.md §8.6) |
52
+ | `coverage.ts` | `chunkedEntry()`/`excludedEntry()`/`assertLosslessCoverage()`; the validator itself is `domain.validateCoverage()` |
53
+ | `strategies/ast-chunk-strategy.ts` | Classes, symbols and fragments (spec.md §8.3 steps 1–8) |
54
+ | `strategies/line-fallback-strategy.ts` | Overlapping line windows for parse failures |
55
+ | `strategies/minified-strategy.ts` | Token/byte-safe splitting without AST structure |
56
+ | `types.ts` | `ChunkerConfig`, `ChunkFileParams`, `ChunkFileResult` and related types |
57
+
58
+ `src/index.ts` is the only public export (rule 10). Internal modules (`context.ts`, `split.ts`,
59
+ `test-helpers.ts`, …) are not public API and are imported directly only by co-located tests.
60
+
61
+ <a id="nutzung"></a>
62
+
63
+ ## Usage
64
+
65
+ ```ts
66
+ import { chunkFile } from "@untangle-code/chunker";
67
+
68
+ const result = chunkFile({
69
+ identity: { workspaceId, repoId, refId, fileId, filePath, languageId },
70
+ content: sourceText,
71
+ parseResult, // from LanguageAdapter.parse()
72
+ regions, // from LanguageAdapter.extractChunks(ast)
73
+ symbols, // from LanguageAdapter.extractSymbols(ast)
74
+ config: {
75
+ preparedText: { documentPrefix, repoLabel, refName }, // Required; other settings have defaults
76
+ },
77
+ });
78
+ // result.drafts: CodeChunkDraft[] (including preparedDenseText/bm25Text/tokenEstimate)
79
+ // result.coverage: CoverageEntry[] (Lossless evidence, spec.md §8.4)
80
+ // result.strategy: "ast" | "line_fallback" | "minified"
81
+ ```
82
+
83
+ <a id="entscheidungen"></a>
84
+
85
+ ## Decisions
86
+
87
+ Choices left open by spec.md/architecture.md (rule 9); downstream packages should extend additively
88
+ rather than redefine contracts locally:
89
+
90
+ 1. **`AstRegion.scopeId` is ignored; the chunker calculates `symbolId`.** During
91
+ `LanguageAdapter.extractChunks`, an adapter knows only `SourceFile` (fileId/repoId/refId), not
92
+ `workspaceId`, and therefore cannot derive a spec-conformant `symbol_id` (spec.md §5.5 requires
93
+ four components including `workspace_id`). The chunker receives `workspaceId` through
94
+ `ChunkFileIdentity` and uses `domain.buildSymbolId` with the region's `ExtractedSymbol`, linked
95
+ by `AstRegion.symbolFqn === ExtractedSymbol.qualifiedName`. Here `AstRegion.scopeId` is a
96
+ required adapter-populated field with no local semantic meaning.
97
+
98
+ 2. **Minified versus parse-error order.** spec.md §8.3 lists step 9 (parse failure) before step
99
+ 10 (minified), without defining priority when either is used as fallback only if the other is
100
+ absent. `chunkFile()` checks minification FIRST from content, independently of parse success.
101
+ Minified files can parse successfully yet yield unusable structure, such as one huge top-level
102
+ statement; AST chunking would be pointless even without parser errors.
103
+
104
+ 3. **`class_overview` content is synthesized (`synthesized: true`) and proves NO range coverage
105
+ beyond the header.** To expose member names without method bodies,
106
+ `buildClassOverviewContent()` builds documentation comment, class signature and member
107
+ names/signatures, never bodies. Draft `range` retains the entire class for navigation;
108
+ `content_hash` describes synthesized text. Coverage declares ONLY the header (class start to
109
+ first member) as "chunked", because the overview actually carries that documentation/signature.
110
+ Remaining class bytes not covered by member chunks—inter-method comments, unmodeled constructs,
111
+ delimiters—are explicit: meaningful gaps become real class `symbol_fragment` chunks with
112
+ `symbolId`, `fragmentIndex`/`fragmentCount` and signature headers (spec.md §8.5); pure
113
+ delimiter/whitespace gaps are logged as "excluded" with reasons (spec.md §8.4). Synthesized
114
+ chunks must never hide gaps: the wave-0 defect (finding 2) is now protected by
115
+ `src/integration.test.ts`. If the member list itself exceeds the token budget, it is capped
116
+ with "... and N more members", never silently emitted over budget. A class without member
117
+ regions becomes a REAL byte chunk rather than synthesized content.
118
+
119
+ 4. **Small properties are grouped only across consecutive `property_group` child regions.**
120
+ Adapters mark each field/property individually with `chunkKind: "property_group"`. The chunker
121
+ greedily merges consecutive runs under the token budget (spec.md §8.3 step 5). Properties
122
+ interrupted by other members stay in separate groups; source is not reordered across the file.
123
+
124
+ 5. **`metricsScope`/`metricsScopeId` per chunk kind**, not prescribed normatively:
125
+ `class_overview`, `function`/`method`/`constructor`/`interface`/`type`/`enum`/`symbol_fragment`
126
+ use `"symbol"` (the single associated symbol ID); `module`/`file_preamble`/`comment`/
127
+ `configuration` without associated symbols use `"file"`; `property_group`/`fallback_lines`/
128
+ `minified_fragment` use `"chunk"` because no single symbol owns the chunk.
129
+
130
+ 6. **`chunk_ordinal`** uses one counter per `(scopeId, chunkKind)` pair (`nextOrdinal` in
131
+ `context.ts`). For `symbol_fragment`, it equals `fragmentIndex` because a symbol's fragments
132
+ are emitted consecutively in source order under one `scopeId`. This also explains stable
133
+ `chunk_id` under line-only shifts (spec.md §5.6): `symbolId` does not change (lines are not
134
+ identity inputs, §5.5), and fragment structure depends on symbol *content*, not absolute position.
135
+
136
+ 7. **Token estimate:** 3 characters/token (rather than the more realistic code range ~3.5–4.5),
137
+ plus small per-line overhead. Deliberate overestimation (architecture.md §14.4), documented in
138
+ `token-estimator.ts`; no network tokenizer, entirely offline and deterministic.
139
+
140
+ 8. **Minification detection** is a pure content heuristic using file size and mean/maximum line
141
+ length, all configurable. It does not rely on conventions such as `.min.js`, which generated
142
+ code does not always follow.
143
+
144
+ 9. **`LineFallbackStrategy` overlap** uses line windows with `linesPerChunk`/`overlapLines`, default
145
+ 60/8, as explicitly requested. `MinifiedStrategy` also supports configurable `overlapBytes`,
146
+ default 0, because the spec does not explicitly require overlap there; it adds retrieval context.
147
+
148
+ 10. **Safe split boundaries** prefer a newline within a 120-byte lookback, never shrinking a
149
+ fragment by more than half to do so, then fall back to UTF-8-safe byte boundaries—never
150
+ continuation bytes. A dedicated multibyte-character test verifies this.
151
+
152
+ 11. **`module` regions are ALWAYS unpacked**, never emitted wholesale as single-file chunks.
153
+ Adapters provide one `module` root region (the `AstRegion` contract in domain's `analysis.ts`);
154
+ `dispatchRegion` handles `chunkKind` at every depth. Symbol chunks (spec.md §8.3 steps 3–5)
155
+ therefore exist even below the token budget. Module bytes between symbol regions, including
156
+ top-level statements and free comments, become real `module` chunks; pure whitespace gaps
157
+ are excluded with reasons. Only a `module` without children—a statement-only file—is emitted
158
+ as a complete content chunk or split into fragments.
159
+
160
+ <a id="bekannte-grenzen--offene-punkte"></a>
161
+
162
+ ## Known limits / open issues
163
+
164
+ See `openIssues` in the work unit's completion report.
165
+
166
+ <a id="e4-ehrlichkeitspruefung-wp-165"></a>
167
+
168
+ ## E4 honesty check (WP-165)
169
+
170
+ `src/honesty.test.ts` runs `assertHonestUnderDamage` from testkit against the real
171
+ parser-registry → `chunkFile` seam. Six damage kinds—truncation, broken structure, removed required
172
+ fields, size beyond every limit, concurrency and abort signals—must each produce either a visible
173
+ finding or a demonstrably unchanged result.
174
+
175
+ **Found and fixed: new `ChunkFileResult.degradations`.** When an adapter returned NO regions after
176
+ `parseQuality: "complete"`, `chunkFile` silently used line fallback. Byte coverage stayed complete
177
+ (`assertLosslessCoverage` passed), and quality stayed `"complete"`, yet no chunk had a symbol
178
+ assignment. Callers inferred structural analysis that never happened: "completeness claimed,
179
+ subtree discarded". Every deviation from the AST strategy now produces a `ChunkDegradation` with
180
+ `ast_regions_missing`, `parse_failed` or `minified_content`. Regression tests are in `src/honesty.test.ts`.
181
+
182
+ <a id="bekannte-grenzen-e4"></a>
183
+
184
+ ### Known limits (E4)
185
+
186
+ - **`chunkFile()` accepts no `AbortSignal`.** It is synchronous and pure; spec.md §22 requires
187
+ cancellation support for public APIs. Damage kind `abort_signal` is therefore explicitly
188
+ waived for this reason in the honesty check and recorded in its skip ledger, never silently
189
+ omitted. Cancellation would be meaningful only between strategy steps and would change the
190
+ signature, remaining a separate task.
191
+
192
+ <a id="lizenz"></a>
193
+
194
+ ## License
195
+
196
+
197
+ **AGPL-3.0-only.** This package includes the complete license in [`LICENSE`](./LICENSE), the
198
+ license-choice rationale in [`NOTICE`](./NOTICE), and licenses for all third-party production
199
+ dependencies in [`THIRD_PARTY_LICENSES.md`](./THIRD_PARTY_LICENSES.md). All three ship in the npm
200
+ tarball: AGPL-3.0-only §4 and §5 require recipients to receive the license; a README alone is not
201
+ a copy of the license.
202
+
203
+ Local use alone does not trigger an obligation. Copyleft obligations apply to redistribution and
204
+ providing a network service (AGPL §13).
205
+
206
+ Corresponding TypeScript source ships under `src/` in the same tarball; the complete repository
207
+ is available at <https://github.com/lboel/untangle>.
@@ -0,0 +1,24 @@
1
+ # Third-party licenses — @untangle-code/chunker
2
+
3
+ Generated by `pnpm third-party-licenses` from this package's installed
4
+ production dependency tree: transitive `dependencies`, across
5
+ workspace edges, with no `devDependencies` included. The generator reads
6
+ each resolved package's `package.json` in `node_modules`; nothing is maintained by hand
7
+ or estimated.
8
+
9
+ Do not edit manually: `pnpm third-party-licenses --check` fails on drift,
10
+ and `pnpm check-publishable` rejects packages without a current file.
11
+
12
+ This file exists because `docs/BUILD_PLAN.md`, risk R6, makes
13
+ per-package `THIRD_PARTY_LICENSES.md` a release gate for npm publication.
14
+
15
+ Untangle itself is **AGPL-3.0-only** (see `LICENSE` and `NOTICE` in
16
+ this package). The packages below are third-party works; their own
17
+ licenses and copyright notices continue to apply unchanged. Workspace
18
+ packages (`@untangle-code/*`) are not listed here; they belong to the same
19
+ work and use the same AGPL-3.0-only license.
20
+
21
+ ## Result: no third-party dependencies
22
+
23
+ This package has no production dependencies outside the workspace.
24
+ The list is empty because none exist, not because they were not checked.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Byte-accurate text slicing and line/byte conversion (spec.md §7.1's
3
+ * `SourceRange` convention: byte offsets are 0-based half-open, line numbers
4
+ * 1-based inclusive).
5
+ *
6
+ * The chunker MUST slice chunk content by byte offset, not by JS string
7
+ * (UTF-16) index, so that `CodeChunkPayloadV1.start_byte`/`end_byte` are
8
+ * exact and so that content is byte-identical to the source file on disk.
9
+ * Everything here operates on a `Buffer` built once per file.
10
+ */
11
+ import type { SourceRange } from "@untangle-code/domain";
12
+ /** Precomputed line-start byte offsets for one file's content. */
13
+ export interface LineIndex {
14
+ buffer: Buffer;
15
+ /** Byte offset of the first byte of each line; `lineStarts[0] === 0`. */
16
+ lineStarts: number[];
17
+ }
18
+ /** Builds a `LineIndex` from full file content (decoded UTF-8 string). */
19
+ export declare function buildLineIndex(content: string): LineIndex;
20
+ /** 1-based line number containing the given 0-based byte offset. */
21
+ export declare function lineNumberForByteOffset(index: LineIndex, byteOffset: number): number;
22
+ /**
23
+ * Builds a `SourceRange` for a byte span, deriving line numbers from the
24
+ * index. `endByte` is exclusive; for a non-empty span the end line is the
25
+ * line containing the last included byte (`endByte - 1`).
26
+ */
27
+ export declare function rangeForByteSpan(index: LineIndex, startByte: number, endByte: number): SourceRange;
28
+ /** Decodes the UTF-8 bytes `[startByte, endByte)` of the file as a string. */
29
+ export declare function sliceBytes(index: LineIndex, startByte: number, endByte: number): string;
30
+ /** True if `byte` is a UTF-8 continuation byte (`10xxxxxx`). */
31
+ export declare function isUtf8ContinuationByte(byte: number): boolean;
32
+ /**
33
+ * Returns the largest offset `<= target` (and `>= 0`) that does not fall in
34
+ * the middle of a multi-byte UTF-8 sequence, i.e. `buffer[offset]` (if any)
35
+ * is not a continuation byte. Used to keep token-boundary splits byte-safe
36
+ * (architecture.md §14.4) even when the file mixes multi-byte characters.
37
+ */
38
+ export declare function safeUtf8Boundary(buffer: Buffer, target: number): number;
39
+ //# sourceMappingURL=byte-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byte-text.d.ts","sourceRoot":"","sources":["../src/byte-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CASzD;AAED,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAapF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,WAAW,CAIb;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED,gEAAgE;AAChE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAMvE"}
@@ -0,0 +1,59 @@
1
+ /** Builds a `LineIndex` from full file content (decoded UTF-8 string). */
2
+ export function buildLineIndex(content) {
3
+ const buffer = Buffer.from(content, "utf8");
4
+ const lineStarts = [0];
5
+ for (let i = 0; i < buffer.length; i += 1) {
6
+ if (buffer[i] === 0x0a) {
7
+ lineStarts.push(i + 1);
8
+ }
9
+ }
10
+ return { buffer, lineStarts };
11
+ }
12
+ /** 1-based line number containing the given 0-based byte offset. */
13
+ export function lineNumberForByteOffset(index, byteOffset) {
14
+ const { lineStarts } = index;
15
+ let lo = 0;
16
+ let hi = lineStarts.length - 1;
17
+ while (lo < hi) {
18
+ const mid = Math.ceil((lo + hi) / 2);
19
+ if (lineStarts[mid] <= byteOffset) {
20
+ lo = mid;
21
+ }
22
+ else {
23
+ hi = mid - 1;
24
+ }
25
+ }
26
+ return lo + 1;
27
+ }
28
+ /**
29
+ * Builds a `SourceRange` for a byte span, deriving line numbers from the
30
+ * index. `endByte` is exclusive; for a non-empty span the end line is the
31
+ * line containing the last included byte (`endByte - 1`).
32
+ */
33
+ export function rangeForByteSpan(index, startByte, endByte) {
34
+ const startLine = lineNumberForByteOffset(index, startByte);
35
+ const endLine = endByte > startByte ? lineNumberForByteOffset(index, endByte - 1) : startLine;
36
+ return { startLine, endLine, startByte, endByte };
37
+ }
38
+ /** Decodes the UTF-8 bytes `[startByte, endByte)` of the file as a string. */
39
+ export function sliceBytes(index, startByte, endByte) {
40
+ return index.buffer.toString("utf8", startByte, endByte);
41
+ }
42
+ /** True if `byte` is a UTF-8 continuation byte (`10xxxxxx`). */
43
+ export function isUtf8ContinuationByte(byte) {
44
+ return (byte & 0b1100_0000) === 0b1000_0000;
45
+ }
46
+ /**
47
+ * Returns the largest offset `<= target` (and `>= 0`) that does not fall in
48
+ * the middle of a multi-byte UTF-8 sequence, i.e. `buffer[offset]` (if any)
49
+ * is not a continuation byte. Used to keep token-boundary splits byte-safe
50
+ * (architecture.md §14.4) even when the file mixes multi-byte characters.
51
+ */
52
+ export function safeUtf8Boundary(buffer, target) {
53
+ let offset = Math.max(0, Math.min(target, buffer.length));
54
+ while (offset > 0 && offset < buffer.length && isUtf8ContinuationByte(buffer[offset])) {
55
+ offset -= 1;
56
+ }
57
+ return offset;
58
+ }
59
+ //# sourceMappingURL=byte-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byte-text.js","sourceRoot":"","sources":["../src/byte-text.ts"],"names":[],"mappings":"AAmBA,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAa,CAAC,CAAC,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,uBAAuB,CAAC,KAAgB,EAAE,UAAkB;IAC1E,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC7B,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/B,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACrC,IAAI,UAAU,CAAC,GAAG,CAAE,IAAI,UAAU,EAAE,CAAC;YACnC,EAAE,GAAG,GAAG,CAAC;QACX,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,EAAE,GAAG,CAAC,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAgB,EAChB,SAAiB,EACjB,OAAe;IAEf,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACpD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,UAAU,CAAC,KAAgB,EAAE,SAAiB,EAAE,OAAe;IAC7E,OAAO,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,KAAK,WAAW,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,MAAc;IAC7D,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,OAAO,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAE,CAAC,EAAE,CAAC;QACvF,MAAM,IAAI,CAAC,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ChunkFileParams, ChunkFileResult } from "./types.js";
2
+ /**
3
+ * Chunks one file per spec.md §8.3: minified detection takes priority over
4
+ * AST chunking (a minified file may still parse "successfully" but produces
5
+ * no meaningful symbol structure), then a failed/empty parse falls back to
6
+ * line windows, and otherwise the AST strategy runs (README "Entscheidungen"
7
+ * documents this ordering as an Untangle decision — the spec numbers 9/10
8
+ * as trailing fallback steps without fixing their relative priority).
9
+ */
10
+ export declare function chunkFile(params: ChunkFileParams): ChunkFileResult;
11
+ //# sourceMappingURL=chunker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../src/chunker.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAoB,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA+BrF;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAmFlE"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Top-level chunking entry point (spec.md §8.3): picks a strategy, runs it,
3
+ * and — regardless of which strategy ran — guarantees the lossless coverage
4
+ * invariant (spec.md §8.4) before returning: any residual gap is filled with
5
+ * `fallback_lines` chunks, then the result is validated one last time and
6
+ * `ANALYSIS_INCOMPLETE` is thrown if that somehow still fails (defense in
7
+ * depth; a correct strategy should never leave a gap).
8
+ */
9
+ import { classifyTextFormat, validateCoverage } from "@untangle-code/domain";
10
+ import { resolveChunkerConfig } from "./config.js";
11
+ import { contentOf, createBuildContext, emitChunk } from "./context.js";
12
+ import { assertLosslessCoverage, assertTruthfulLedger, chunkedEntry } from "./coverage.js";
13
+ import { detectMinifiedContent } from "./minified-detection.js";
14
+ import { splitByTokenBoundary } from "./split.js";
15
+ import { runAstChunkStrategy } from "./strategies/ast-chunk-strategy.js";
16
+ import { runLineFallbackStrategy } from "./strategies/line-fallback-strategy.js";
17
+ import { runMinifiedStrategy } from "./strategies/minified-strategy.js";
18
+ const GAP_HEADER = { signature: "fallback_lines" };
19
+ /** Fills any residual byte gap with `fallback_lines` chunks (defense in depth for every strategy). */
20
+ function fillGaps(ctx, drafts, coverage) {
21
+ const gaps = validateCoverage(coverage, ctx.fileRange).gaps;
22
+ for (const gap of gaps) {
23
+ const range = { ...gap, startLine: -1, endLine: -1 };
24
+ for (const piece of splitByTokenBoundary(ctx, range, GAP_HEADER)) {
25
+ const draft = emitChunk(ctx, {
26
+ range: piece,
27
+ content: contentOf(ctx, piece),
28
+ scopeId: ctx.identity.fileId,
29
+ chunkKind: "fallback_lines",
30
+ header: {
31
+ signature: "fallback_lines",
32
+ metricsScope: "chunk",
33
+ metricsScopeId: ctx.identity.fileId,
34
+ },
35
+ });
36
+ drafts.push(draft);
37
+ coverage.push(chunkedEntry(draft.range, draft.chunkId));
38
+ }
39
+ }
40
+ }
41
+ /**
42
+ * Chunks one file per spec.md §8.3: minified detection takes priority over
43
+ * AST chunking (a minified file may still parse "successfully" but produces
44
+ * no meaningful symbol structure), then a failed/empty parse falls back to
45
+ * line windows, and otherwise the AST strategy runs (README "Entscheidungen"
46
+ * documents this ordering as an Untangle decision — the spec numbers 9/10
47
+ * as trailing fallback steps without fixing their relative priority).
48
+ */
49
+ export function chunkFile(params) {
50
+ const config = resolveChunkerConfig(params.config);
51
+ const ctx = createBuildContext(params.identity, params.content, config, params.parseResult.parseQuality);
52
+ const isMinified = params.isMinified ??
53
+ detectMinifiedContent(params.content, config.minifiedDetection, classifyTextFormat(params.identity.filePath));
54
+ const hasUsableAst = params.parseResult.parseQuality !== "failed" && params.regions.length > 0;
55
+ let drafts;
56
+ let coverage;
57
+ let strategy;
58
+ // E4 (WP-165): jede Abweichung von der AST-Strategie bekommt hier einen
59
+ // benannten Grund. Ohne ihn ist die Abstufung unsichtbar -- die Bytes sind
60
+ // lueckenlos erfasst, `parseQuality` kann `"complete"` lauten, und trotzdem
61
+ // hat kein Chunk eine Symbolzuordnung.
62
+ const degradations = [];
63
+ if (isMinified) {
64
+ const result = runMinifiedStrategy(ctx);
65
+ drafts = result.drafts;
66
+ coverage = result.coverage;
67
+ strategy = "minified";
68
+ degradations.push({
69
+ reason: "minified_content",
70
+ strategy: "minified",
71
+ detail: `content was classified as minified (${params.isMinified === undefined ? "auto-detected" : "declared by the caller"}), so it was cut into byte windows; no symbol association exists for this file`,
72
+ });
73
+ }
74
+ else if (!hasUsableAst) {
75
+ const result = runLineFallbackStrategy(ctx);
76
+ drafts = result.drafts;
77
+ coverage = result.coverage;
78
+ strategy = "line_fallback";
79
+ degradations.push(params.parseResult.parseQuality === "failed"
80
+ ? {
81
+ reason: "parse_failed",
82
+ strategy: "line_fallback",
83
+ detail: `the parse failed (${String(params.parseResult.errorNodeCount)} error node(s), ${String(params.parseResult.missingNodeCount)} missing node(s)), so the file was cut into line windows; no symbol association exists for this file`,
84
+ }
85
+ : {
86
+ reason: "ast_regions_missing",
87
+ strategy: "line_fallback",
88
+ detail: `the parse reported "${params.parseResult.parseQuality}" but the language adapter delivered no chunk regions, so the file was cut into line windows; no symbol association exists for this file although the parse claims success`,
89
+ });
90
+ }
91
+ else {
92
+ const result = runAstChunkStrategy(ctx, params.regions, params.symbols);
93
+ drafts = result.drafts;
94
+ coverage = result.coverage;
95
+ strategy = "ast";
96
+ }
97
+ fillGaps(ctx, drafts, coverage);
98
+ // Two-sided machine check (spec.md §8.4): the ledger must be COMPLETE
99
+ // (assertLosslessCoverage) and every claim in it must be TRUE
100
+ // (assertTruthfulLedger) — a synthesized chunk may never stand in for
101
+ // bytes its content does not literally carry.
102
+ assertTruthfulLedger(ctx.lineIndex, drafts, coverage, params.identity.filePath);
103
+ assertLosslessCoverage(coverage, ctx.fileRange, params.identity.filePath);
104
+ return {
105
+ drafts,
106
+ coverage,
107
+ strategy,
108
+ parseQuality: params.parseResult.parseQuality,
109
+ degradations,
110
+ };
111
+ }
112
+ //# sourceMappingURL=chunker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.js","sourceRoot":"","sources":["../src/chunker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAGxE,MAAM,UAAU,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAW,CAAC;AAE5D,sGAAsG;AACtG,SAAS,QAAQ,CACf,GAA0C,EAC1C,MAAwB,EACxB,QAAyB;IAEzB,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAgB,EAAE,GAAG,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;QAClE,KAAK,MAAM,KAAK,IAAI,oBAAoB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC;YACjE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE;gBAC3B,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC;gBAC9B,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;gBAC5B,SAAS,EAAE,gBAAgB;gBAC3B,MAAM,EAAE;oBACN,SAAS,EAAE,gBAAgB;oBAC3B,YAAY,EAAE,OAAO;oBACrB,cAAc,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;iBACpC;aACF,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,MAAuB;IAC/C,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,kBAAkB,CAC5B,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,OAAO,EACd,MAAM,EACN,MAAM,CAAC,WAAW,CAAC,YAAY,CAChC,CAAC;IAEF,MAAM,UAAU,GACd,MAAM,CAAC,UAAU;QACjB,qBAAqB,CACnB,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,iBAAiB,EACxB,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC7C,CAAC;IACJ,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAE/F,IAAI,MAAwB,CAAC;IAC7B,IAAI,QAAyB,CAAC;IAC9B,IAAI,QAAqC,CAAC;IAC1C,wEAAwE;IACxE,2EAA2E;IAC3E,4EAA4E;IAC5E,uCAAuC;IACvC,MAAM,YAAY,GAAuB,EAAE,CAAC;IAE5C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QACvB,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC3B,QAAQ,GAAG,UAAU,CAAC;QACtB,YAAY,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,kBAAkB;YAC1B,QAAQ,EAAE,UAAU;YACpB,MAAM,EAAE,uCACN,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,wBACtD,gFAAgF;SACjF,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QACvB,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC3B,QAAQ,GAAG,eAAe,CAAC;QAC3B,YAAY,CAAC,IAAI,CACf,MAAM,CAAC,WAAW,CAAC,YAAY,KAAK,QAAQ;YAC1C,CAAC,CAAC;gBACE,MAAM,EAAE,cAAc;gBACtB,QAAQ,EAAE,eAAe;gBACzB,MAAM,EAAE,qBAAqB,MAAM,CACjC,MAAM,CAAC,WAAW,CAAC,cAAc,CAClC,mBAAmB,MAAM,CACxB,MAAM,CAAC,WAAW,CAAC,gBAAgB,CACpC,sGAAsG;aACxG;YACH,CAAC,CAAC;gBACE,MAAM,EAAE,qBAAqB;gBAC7B,QAAQ,EAAE,eAAe;gBACzB,MAAM,EAAE,uBAAuB,MAAM,CAAC,WAAW,CAAC,YAAY,4KAA4K;aAC3O,CACN,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACxE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QACvB,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC3B,QAAQ,GAAG,KAAK,CAAC;IACnB,CAAC;IAED,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChC,sEAAsE;IACtE,8DAA8D;IAC9D,sEAAsE;IACtE,8CAA8C;IAC9C,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChF,sBAAsB,CAAC,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE1E,OAAO;QACL,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,YAAY;QAC7C,YAAY;KACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ChunkerConfig, ChunkerConfigInput } from "./types.js";
2
+ /**
3
+ * Untangle decision (README "Entscheidungen"): 800 tokens is the default
4
+ * per-chunk budget — generous enough for a typical method or a small class
5
+ * overview, small enough that four to six chunks fit a model's context
6
+ * alongside a query and other results.
7
+ */
8
+ export declare const DEFAULT_MAX_TOKENS_PER_CHUNK = 800;
9
+ export declare const DEFAULT_LINE_FALLBACK_CONFIG: {
10
+ linesPerChunk: number;
11
+ overlapLines: number;
12
+ };
13
+ export declare const DEFAULT_MINIFIED_STRATEGY_CONFIG: {
14
+ overlapBytes: number;
15
+ };
16
+ export declare function resolveChunkerConfig(input: ChunkerConfigInput): ChunkerConfig;
17
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD,eAAO,MAAM,4BAA4B;;;CAAyC,CAAC;AACnF,eAAO,MAAM,gCAAgC;;CAAsB,CAAC;AAEpE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAW7E"}
package/dist/config.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Default chunker configuration and resolution of caller overrides.
3
+ */
4
+ import { CHUNKER_VERSION, PARSER_VERSION } from "@untangle-code/domain";
5
+ import { DEFAULT_MINIFIED_DETECTION_CONFIG } from "./minified-detection.js";
6
+ import { DEFAULT_TOKEN_ESTIMATOR_CONFIG } from "./token-estimator.js";
7
+ /**
8
+ * Untangle decision (README "Entscheidungen"): 800 tokens is the default
9
+ * per-chunk budget — generous enough for a typical method or a small class
10
+ * overview, small enough that four to six chunks fit a model's context
11
+ * alongside a query and other results.
12
+ */
13
+ export const DEFAULT_MAX_TOKENS_PER_CHUNK = 800;
14
+ export const DEFAULT_LINE_FALLBACK_CONFIG = { linesPerChunk: 60, overlapLines: 8 };
15
+ export const DEFAULT_MINIFIED_STRATEGY_CONFIG = { overlapBytes: 0 };
16
+ export function resolveChunkerConfig(input) {
17
+ return {
18
+ chunkerVersion: input.chunkerVersion ?? CHUNKER_VERSION,
19
+ parserVersion: input.parserVersion ?? PARSER_VERSION,
20
+ maxTokensPerChunk: input.maxTokensPerChunk ?? DEFAULT_MAX_TOKENS_PER_CHUNK,
21
+ tokenEstimator: input.tokenEstimator ?? DEFAULT_TOKEN_ESTIMATOR_CONFIG,
22
+ preparedText: input.preparedText,
23
+ minifiedDetection: input.minifiedDetection ?? DEFAULT_MINIFIED_DETECTION_CONFIG,
24
+ lineFallback: input.lineFallback ?? DEFAULT_LINE_FALLBACK_CONFIG,
25
+ minifiedStrategy: input.minifiedStrategy ?? DEFAULT_MINIFIED_STRATEGY_CONFIG,
26
+ };
27
+ }
28
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAE,iCAAiC,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AAGtE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEhD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,aAAa,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;AACnF,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;AAEpE,MAAM,UAAU,oBAAoB,CAAC,KAAyB;IAC5D,OAAO;QACL,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,eAAe;QACvD,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,cAAc;QACpD,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,IAAI,4BAA4B;QAC1E,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,8BAA8B;QACtE,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,IAAI,iCAAiC;QAC/E,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,4BAA4B;QAChE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,gCAAgC;KAC7E,CAAC;AACJ,CAAC"}