@untangle-code/chunker 0.0.0-stage → 0.1.1
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 +661 -0
- package/NOTICE +90 -0
- package/README.md +206 -2
- package/THIRD_PARTY_LICENSES.md +24 -0
- package/dist/byte-text.d.ts +39 -0
- package/dist/byte-text.d.ts.map +1 -0
- package/dist/byte-text.js +59 -0
- package/dist/byte-text.js.map +1 -0
- package/dist/chunker.d.ts +11 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +112 -0
- package/dist/chunker.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +48 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +0 -0
- package/dist/context.js.map +1 -0
- package/dist/coverage.d.ts +44 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +95 -0
- package/dist/coverage.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/minified-detection.d.ts +27 -0
- package/dist/minified-detection.d.ts.map +1 -0
- package/dist/minified-detection.js +46 -0
- package/dist/minified-detection.js.map +1 -0
- package/dist/prepared-text.d.ts +48 -0
- package/dist/prepared-text.d.ts.map +1 -0
- package/dist/prepared-text.js +57 -0
- package/dist/prepared-text.js.map +1 -0
- package/dist/split.d.ts +36 -0
- package/dist/split.d.ts.map +1 -0
- package/dist/split.js +126 -0
- package/dist/split.js.map +1 -0
- package/dist/strategies/ast-chunk-strategy.d.ts +52 -0
- package/dist/strategies/ast-chunk-strategy.d.ts.map +1 -0
- package/dist/strategies/ast-chunk-strategy.js +0 -0
- package/dist/strategies/ast-chunk-strategy.js.map +1 -0
- package/dist/strategies/line-fallback-strategy.d.ts +4 -0
- package/dist/strategies/line-fallback-strategy.d.ts.map +1 -0
- package/dist/strategies/line-fallback-strategy.js +79 -0
- package/dist/strategies/line-fallback-strategy.js.map +1 -0
- package/dist/strategies/minified-strategy.d.ts +4 -0
- package/dist/strategies/minified-strategy.d.ts.map +1 -0
- package/dist/strategies/minified-strategy.js +42 -0
- package/dist/strategies/minified-strategy.js.map +1 -0
- package/dist/token-estimator.d.ts +28 -0
- package/dist/token-estimator.d.ts.map +1 -0
- package/dist/token-estimator.js +37 -0
- package/dist/token-estimator.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -3
- package/src/byte-text.ts +85 -0
- package/src/chunker.ts +142 -0
- package/src/config.ts +32 -0
- package/src/context.ts +0 -0
- package/src/coverage.ts +143 -0
- package/src/index.ts +63 -0
- package/src/minified-detection.ts +66 -0
- package/src/prepared-text.ts +118 -0
- package/src/split.ts +164 -0
- package/src/strategies/ast-chunk-strategy.ts +0 -0
- package/src/strategies/line-fallback-strategy.ts +108 -0
- package/src/strategies/minified-strategy.ts +49 -0
- package/src/token-estimator.ts +48 -0
- 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
|
-
#
|
|
1
|
+
# @untangle-code/chunker
|
|
2
2
|
|
|
3
|
-
|
|
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"}
|
package/dist/chunker.js
ADDED
|
@@ -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":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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"}
|