@smeltjs/core 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/LICENSE +202 -0
- package/README.md +55 -0
- package/THIRD-PARTY.md +140 -0
- package/dist/apply.d.ts +145 -0
- package/dist/apply.d.ts.map +1 -0
- package/dist/apply.js +244 -0
- package/dist/apply.js.map +1 -0
- package/dist/cache/prefix.d.ts +164 -0
- package/dist/cache/prefix.d.ts.map +1 -0
- package/dist/cache/prefix.js +361 -0
- package/dist/cache/prefix.js.map +1 -0
- package/dist/cli/args.d.ts +71 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +308 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +161 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/config.d.ts +73 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +144 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/init.d.ts +56 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +449 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/report.d.ts +44 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +113 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/resolve.d.ts +103 -0
- package/dist/cli/resolve.d.ts.map +1 -0
- package/dist/cli/resolve.js +106 -0
- package/dist/cli/resolve.js.map +1 -0
- package/dist/cli/run.d.ts +99 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +351 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/detect.d.ts +16 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +18 -0
- package/dist/detect.js.map +1 -0
- package/dist/errors.d.ts +88 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +107 -0
- package/dist/errors.js.map +1 -0
- package/dist/hash.d.ts +13 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +16 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/lang/bash.d.ts +3 -0
- package/dist/lang/bash.d.ts.map +1 -0
- package/dist/lang/bash.js +28 -0
- package/dist/lang/bash.js.map +1 -0
- package/dist/lang/c.d.ts +3 -0
- package/dist/lang/c.d.ts.map +1 -0
- package/dist/lang/c.js +39 -0
- package/dist/lang/c.js.map +1 -0
- package/dist/lang/c_sharp.d.ts +3 -0
- package/dist/lang/c_sharp.d.ts.map +1 -0
- package/dist/lang/c_sharp.js +37 -0
- package/dist/lang/c_sharp.js.map +1 -0
- package/dist/lang/cpp.d.ts +3 -0
- package/dist/lang/cpp.d.ts.map +1 -0
- package/dist/lang/cpp.js +46 -0
- package/dist/lang/cpp.js.map +1 -0
- package/dist/lang/go.d.ts +3 -0
- package/dist/lang/go.d.ts.map +1 -0
- package/dist/lang/go.js +31 -0
- package/dist/lang/go.js.map +1 -0
- package/dist/lang/java.d.ts +3 -0
- package/dist/lang/java.d.ts.map +1 -0
- package/dist/lang/java.js +34 -0
- package/dist/lang/java.js.map +1 -0
- package/dist/lang/javascript.d.ts +3 -0
- package/dist/lang/javascript.d.ts.map +1 -0
- package/dist/lang/javascript.js +28 -0
- package/dist/lang/javascript.js.map +1 -0
- package/dist/lang/kotlin.d.ts +3 -0
- package/dist/lang/kotlin.d.ts.map +1 -0
- package/dist/lang/kotlin.js +37 -0
- package/dist/lang/kotlin.js.map +1 -0
- package/dist/lang/php.d.ts +3 -0
- package/dist/lang/php.d.ts.map +1 -0
- package/dist/lang/php.js +37 -0
- package/dist/lang/php.js.map +1 -0
- package/dist/lang/profile.d.ts +130 -0
- package/dist/lang/profile.d.ts.map +1 -0
- package/dist/lang/profile.js +2 -0
- package/dist/lang/profile.js.map +1 -0
- package/dist/lang/python.d.ts +3 -0
- package/dist/lang/python.d.ts.map +1 -0
- package/dist/lang/python.js +33 -0
- package/dist/lang/python.js.map +1 -0
- package/dist/lang/registry.d.ts +31 -0
- package/dist/lang/registry.d.ts.map +1 -0
- package/dist/lang/registry.js +89 -0
- package/dist/lang/registry.js.map +1 -0
- package/dist/lang/ruby.d.ts +3 -0
- package/dist/lang/ruby.d.ts.map +1 -0
- package/dist/lang/ruby.js +47 -0
- package/dist/lang/ruby.js.map +1 -0
- package/dist/lang/rust.d.ts +3 -0
- package/dist/lang/rust.d.ts.map +1 -0
- package/dist/lang/rust.js +45 -0
- package/dist/lang/rust.js.map +1 -0
- package/dist/lang/swift.d.ts +3 -0
- package/dist/lang/swift.d.ts.map +1 -0
- package/dist/lang/swift.js +34 -0
- package/dist/lang/swift.js.map +1 -0
- package/dist/lang/tsx.d.ts +4 -0
- package/dist/lang/tsx.d.ts.map +1 -0
- package/dist/lang/tsx.js +11 -0
- package/dist/lang/tsx.js.map +1 -0
- package/dist/lang/typescript.d.ts +18 -0
- package/dist/lang/typescript.d.ts.map +1 -0
- package/dist/lang/typescript.js +61 -0
- package/dist/lang/typescript.js.map +1 -0
- package/dist/net/policy.d.ts +49 -0
- package/dist/net/policy.d.ts.map +1 -0
- package/dist/net/policy.js +120 -0
- package/dist/net/policy.js.map +1 -0
- package/dist/plan/grammar.d.ts +38 -0
- package/dist/plan/grammar.d.ts.map +1 -0
- package/dist/plan/grammar.js +85 -0
- package/dist/plan/grammar.js.map +1 -0
- package/dist/plan/lexical.d.ts +51 -0
- package/dist/plan/lexical.d.ts.map +1 -0
- package/dist/plan/lexical.js +167 -0
- package/dist/plan/lexical.js.map +1 -0
- package/dist/plan/planners.d.ts +35 -0
- package/dist/plan/planners.d.ts.map +1 -0
- package/dist/plan/planners.js +26 -0
- package/dist/plan/planners.js.map +1 -0
- package/dist/plan/structural.d.ts +68 -0
- package/dist/plan/structural.d.ts.map +1 -0
- package/dist/plan/structural.js +458 -0
- package/dist/plan/structural.js.map +1 -0
- package/dist/repomap/cache.d.ts +53 -0
- package/dist/repomap/cache.d.ts.map +1 -0
- package/dist/repomap/cache.js +139 -0
- package/dist/repomap/cache.js.map +1 -0
- package/dist/repomap/map.d.ts +161 -0
- package/dist/repomap/map.d.ts.map +1 -0
- package/dist/repomap/map.js +302 -0
- package/dist/repomap/map.js.map +1 -0
- package/dist/repomap/rank.d.ts +52 -0
- package/dist/repomap/rank.d.ts.map +1 -0
- package/dist/repomap/rank.js +155 -0
- package/dist/repomap/rank.js.map +1 -0
- package/dist/repomap/tags.d.ts +47 -0
- package/dist/repomap/tags.d.ts.map +1 -0
- package/dist/repomap/tags.js +101 -0
- package/dist/repomap/tags.js.map +1 -0
- package/dist/retrieve.d.ts +14 -0
- package/dist/retrieve.d.ts.map +1 -0
- package/dist/retrieve.js +44 -0
- package/dist/retrieve.js.map +1 -0
- package/dist/stages.d.ts +22 -0
- package/dist/stages.d.ts.map +1 -0
- package/dist/stages.js +33 -0
- package/dist/stages.js.map +1 -0
- package/dist/stats.d.ts +40 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +37 -0
- package/dist/stats.js.map +1 -0
- package/dist/store-dir.d.ts +98 -0
- package/dist/store-dir.d.ts.map +1 -0
- package/dist/store-dir.js +379 -0
- package/dist/store-dir.js.map +1 -0
- package/dist/store.d.ts +36 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +56 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +298 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +68 -0
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { reconstruct } from '../apply.js';
|
|
4
|
+
import { CliUsageError, SmeltError } from '../errors.js';
|
|
5
|
+
import { createSmelter } from '../index.js';
|
|
6
|
+
import { buildRepoMap } from '../repomap/map.js';
|
|
7
|
+
import { MemoryElisionStore } from '../store.js';
|
|
8
|
+
import { DirectoryElisionStore } from '../store-dir.js';
|
|
9
|
+
import { CLI_NAME, cliUsage, parseSmeltArgs } from './args.js';
|
|
10
|
+
import { loadNearestConfig } from './config.js';
|
|
11
|
+
import { runInit } from './init.js';
|
|
12
|
+
import { formatMapReport, formatReport } from './report.js';
|
|
13
|
+
import { resolveMapRun, resolveRun, resolveStoreRun } from './resolve.js';
|
|
14
|
+
export { CLI_NAME, cliUsage, parseSmeltArgs } from './args.js';
|
|
15
|
+
export { formatMapReport, formatReport } from './report.js';
|
|
16
|
+
export { resolveMapRun, resolveRun, resolveStoreRun } from './resolve.js';
|
|
17
|
+
/**
|
|
18
|
+
* Exit codes, and why there are five of them.
|
|
19
|
+
*
|
|
20
|
+
* A CLI that returns 0 whatever happens is the shell-level version of a stub that
|
|
21
|
+
* returns `[]`: the caller cannot tell success from failure, so a pipeline built on it
|
|
22
|
+
* fails silently. **`overBudget` is the load-bearing one.** A plan that did not fit is
|
|
23
|
+
* not an error — smelt refused to cut the regions the caller asked to keep, which is
|
|
24
|
+
* correct — but it is also not success, and a script must be able to see the
|
|
25
|
+
* difference without parsing prose.
|
|
26
|
+
*/
|
|
27
|
+
export const EXIT = {
|
|
28
|
+
ok: 0,
|
|
29
|
+
overBudget: 1,
|
|
30
|
+
usage: 2,
|
|
31
|
+
refused: 3,
|
|
32
|
+
unexpected: 4,
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The `--json` envelope format, versioned like the marker for the same reason: this is
|
|
36
|
+
* a surface other programs parse, so a change to it has to be identifiable rather than
|
|
37
|
+
* silent.
|
|
38
|
+
*/
|
|
39
|
+
export const CLI_JSON_FORMAT = 'smelt-cli/v1';
|
|
40
|
+
/**
|
|
41
|
+
* The `smelt map --json` envelope format. Its own version line, because the two
|
|
42
|
+
* envelopes carry different structures and must be able to move independently —
|
|
43
|
+
* a map envelope has no elided bytes to carry, since a map elides nothing.
|
|
44
|
+
*/
|
|
45
|
+
export const CLI_MAP_JSON_FORMAT = 'smelt-map-cli/v1';
|
|
46
|
+
/**
|
|
47
|
+
* The `smelt stats --json` envelope format. Its own version line for the same reason
|
|
48
|
+
* `smelt map` has one: the two envelopes carry different structures and must move
|
|
49
|
+
* independently.
|
|
50
|
+
*/
|
|
51
|
+
export const CLI_STATS_JSON_FORMAT = 'smelt-stats-cli/v1';
|
|
52
|
+
/**
|
|
53
|
+
* The whole CLI, as a function that returns an exit code instead of calling `exit`.
|
|
54
|
+
*
|
|
55
|
+
* Smelted text goes to stdout and the report goes to stderr, so `smelt big.log
|
|
56
|
+
* --budget 4000 > small.log` leaves the human-readable part on the terminal and the
|
|
57
|
+
* payload in the file.
|
|
58
|
+
*/
|
|
59
|
+
export async function runCli(argv, io) {
|
|
60
|
+
try {
|
|
61
|
+
const invocation = parseSmeltArgs(argv);
|
|
62
|
+
switch (invocation.mode) {
|
|
63
|
+
case 'help':
|
|
64
|
+
io.stdout(cliUsage());
|
|
65
|
+
return EXIT.ok;
|
|
66
|
+
case 'version':
|
|
67
|
+
io.stdout(`${io.version}\n`);
|
|
68
|
+
return EXIT.ok;
|
|
69
|
+
case 'reconstruct':
|
|
70
|
+
return runReconstruct(readInput(invocation.file, io), io);
|
|
71
|
+
case 'init': {
|
|
72
|
+
if (io.initInput === undefined) {
|
|
73
|
+
throw new CliUsageError(`${CLI_NAME}: init is interactive, and this invocation has no interactive ` +
|
|
74
|
+
`input stream. Run \`${CLI_NAME} init\` from a terminal.`);
|
|
75
|
+
}
|
|
76
|
+
return await runInit({
|
|
77
|
+
input: io.initInput,
|
|
78
|
+
output: io.stdout,
|
|
79
|
+
cwd: io.cwd ?? process.cwd(),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
case 'smelt':
|
|
83
|
+
return await runSmelt(invocation, io);
|
|
84
|
+
case 'map':
|
|
85
|
+
return await runMap(invocation, io);
|
|
86
|
+
case 'retrieve':
|
|
87
|
+
return runRetrieve(invocation, io);
|
|
88
|
+
case 'stats':
|
|
89
|
+
return runStats(invocation, io);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error instanceof CliUsageError) {
|
|
94
|
+
io.stderr(`${error.message}\n`);
|
|
95
|
+
return EXIT.usage;
|
|
96
|
+
}
|
|
97
|
+
if (error instanceof SmeltError) {
|
|
98
|
+
io.stderr(`${error.name}: ${error.message}\n`);
|
|
99
|
+
return EXIT.refused;
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* One smelt run, executed straight-line over a {@link ResolvedRun}.
|
|
106
|
+
*
|
|
107
|
+
* All merging — flags versus `smelt.config.json` versus built-ins, including the
|
|
108
|
+
* budget-required refusal — happens in {@link resolveRun}, which is the only place
|
|
109
|
+
* precedence lives. This function reads the resolved object and never consults a flag
|
|
110
|
+
* or a config field directly.
|
|
111
|
+
*/
|
|
112
|
+
async function runSmelt(invocation, io) {
|
|
113
|
+
const run = resolveRun(invocation, loadNearestConfig(io.cwd ?? process.cwd()));
|
|
114
|
+
const inputText = readInput(run.file, io);
|
|
115
|
+
const source = run.file ?? '<stdin>';
|
|
116
|
+
const store = storeFor(run);
|
|
117
|
+
const smelter = createSmelter({
|
|
118
|
+
strategy: run.strategy,
|
|
119
|
+
...(store === undefined ? {} : { store }),
|
|
120
|
+
});
|
|
121
|
+
const options = {
|
|
122
|
+
budgetBytes: run.budgetBytes,
|
|
123
|
+
...(run.file === undefined ? {} : { path: run.file }),
|
|
124
|
+
...(run.language === undefined ? {} : { language: run.language }),
|
|
125
|
+
...(run.focus.length === 0 ? {} : { focus: run.focus }),
|
|
126
|
+
};
|
|
127
|
+
const result = await smelter.smelt(inputText, options);
|
|
128
|
+
if (run.json) {
|
|
129
|
+
io.stdout(`${JSON.stringify(envelope(result, smelter.store), null, 2)}\n`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
io.stdout(result.text);
|
|
133
|
+
}
|
|
134
|
+
io.stderr(formatReport({ result, source, budgetBytes: run.budgetBytes, inputText }));
|
|
135
|
+
return result.outputBytes > run.budgetBytes ? EXIT.overBudget : EXIT.ok;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* One `smelt map` run, executed straight-line over a {@link ResolvedMapRun} the same
|
|
139
|
+
* way {@link runSmelt} executes its {@link ResolvedRun} — the merge, including the
|
|
140
|
+
* budget-required refusal, lives in {@link resolveMapRun}.
|
|
141
|
+
*
|
|
142
|
+
* The map is deliberately **not** served through a planner strategy: `buildRepoMap`
|
|
143
|
+
* returns a {@link RepoMap}, not an `ElisionPlan` — nothing is elided, stored, or
|
|
144
|
+
* reversible — so it gets its own subcommand and its own envelope instead of a
|
|
145
|
+
* `--strategy` name that would lie about what comes back. And the exit code is
|
|
146
|
+
* {@link EXIT.ok} on every successful build: `EXIT.overBudget` exists because a smelt
|
|
147
|
+
* plan may refuse to cut kept regions and come back too big, while the map fits
|
|
148
|
+
* itself to the budget by construction — an over-budget map cannot happen, so no
|
|
149
|
+
* exit code pretends it can.
|
|
150
|
+
*/
|
|
151
|
+
async function runMap(invocation, io) {
|
|
152
|
+
const run = resolveMapRun(invocation, loadNearestConfig(io.cwd ?? process.cwd()));
|
|
153
|
+
assertDirectory(run.dir);
|
|
154
|
+
const map = await buildRepoMap({
|
|
155
|
+
root: run.dir,
|
|
156
|
+
budgetBytes: run.budgetBytes,
|
|
157
|
+
...(run.focus.length === 0 ? {} : { focus: run.focus }),
|
|
158
|
+
...(run.ignore === undefined ? {} : { ignore: run.ignore }),
|
|
159
|
+
...(run.cacheDir === undefined ? {} : { cacheDir: run.cacheDir }),
|
|
160
|
+
});
|
|
161
|
+
if (run.json) {
|
|
162
|
+
const mapEnvelope = { format: CLI_MAP_JSON_FORMAT, map };
|
|
163
|
+
io.stdout(`${JSON.stringify(mapEnvelope, null, 2)}\n`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
io.stdout(map.text);
|
|
167
|
+
}
|
|
168
|
+
io.stderr(formatMapReport({ map, source: run.dir, budgetSource: run.budgetSource }));
|
|
169
|
+
return EXIT.ok;
|
|
170
|
+
}
|
|
171
|
+
/** A directory the walk can start from, or a usage error naming what is wrong. */
|
|
172
|
+
function assertDirectory(dir) {
|
|
173
|
+
let isDirectory;
|
|
174
|
+
try {
|
|
175
|
+
isDirectory = statSync(dir).isDirectory();
|
|
176
|
+
}
|
|
177
|
+
catch (cause) {
|
|
178
|
+
throw new CliUsageError(`${CLI_NAME}: cannot read directory "${dir}": ` +
|
|
179
|
+
`${cause instanceof Error ? cause.message : String(cause)}`);
|
|
180
|
+
}
|
|
181
|
+
if (!isDirectory) {
|
|
182
|
+
throw new CliUsageError(`${CLI_NAME}: "${dir}" is not a directory. map reads a whole tree; for one ` +
|
|
183
|
+
`file, use \`${CLI_NAME} <file>\`.`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Construct the store a {@link ResolvedRun} decided on, or `undefined` for the
|
|
188
|
+
* library's own default (a fresh in-memory store). Pure construction — the decision,
|
|
189
|
+
* including path resolution, was already made in {@link resolveRun}.
|
|
190
|
+
*/
|
|
191
|
+
function storeFor(run) {
|
|
192
|
+
if (run.store.kind === 'memory')
|
|
193
|
+
return undefined;
|
|
194
|
+
return new DirectoryElisionStore(run.store.path);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* `smelt retrieve <hash>` — the marker's `retrieve("hash")`, as a real command.
|
|
198
|
+
*
|
|
199
|
+
* The exact original bytes go to stdout and **nothing else does**: no report, no
|
|
200
|
+
* trailing newline the store did not hold, no re-encoding. Trailing-newline fidelity
|
|
201
|
+
* is not pedantry — the retrieved bytes get spliced back into reasoning about the
|
|
202
|
+
* original, and an almost-right blob is the silent wrong answer this project refuses.
|
|
203
|
+
*
|
|
204
|
+
* The retrieval IS counted — that is the point. `store.retrieve()` journals the hit,
|
|
205
|
+
* so an agent working from pure shell moves the same `expansionRate` a tool-calling
|
|
206
|
+
* consumer moves, and over-pruning stays visible whichever loop is in use. Errors are
|
|
207
|
+
* the store's own, verbatim: `UnknownHashError` for a hash never elided,
|
|
208
|
+
* `StoreCorruptionError` for bytes that no longer hash to their name — distinct
|
|
209
|
+
* texts, both exiting {@link EXIT.refused}.
|
|
210
|
+
*/
|
|
211
|
+
function runRetrieve(invocation, io) {
|
|
212
|
+
const run = resolveStoreRun('retrieve', loadNearestConfig(io.cwd ?? process.cwd()));
|
|
213
|
+
const store = new DirectoryElisionStore(run.storePath);
|
|
214
|
+
const content = store.retrieve(invocation.hash);
|
|
215
|
+
io.stdout(content);
|
|
216
|
+
return EXIT.ok;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* `smelt stats` — the store's counters, without touching them.
|
|
220
|
+
*
|
|
221
|
+
* Reading stats does NOT count as a retrieval: `stats()` folds the journal and scans
|
|
222
|
+
* the blobs, journaling nothing, so watching the expansion rate can never move it —
|
|
223
|
+
* an observer that inflated its own metric would make the honest signal dishonest.
|
|
224
|
+
*
|
|
225
|
+
* The plain form is one `name value` per line, greppable and stable; `--json` emits
|
|
226
|
+
* the {@link RetrieveStats} verbatim in its own versioned envelope
|
|
227
|
+
* ({@link CLI_STATS_JSON_FORMAT}), like every other machine-read surface here.
|
|
228
|
+
*/
|
|
229
|
+
function runStats(invocation, io) {
|
|
230
|
+
const run = resolveStoreRun('stats', loadNearestConfig(io.cwd ?? process.cwd()));
|
|
231
|
+
const stats = new DirectoryElisionStore(run.storePath).stats();
|
|
232
|
+
if (invocation.json) {
|
|
233
|
+
const statsEnvelope = { format: CLI_STATS_JSON_FORMAT, stats };
|
|
234
|
+
io.stdout(`${JSON.stringify(statsEnvelope, null, 2)}\n`);
|
|
235
|
+
return EXIT.ok;
|
|
236
|
+
}
|
|
237
|
+
io.stdout([
|
|
238
|
+
`elisionsStored ${String(stats.elisionsStored)}`,
|
|
239
|
+
`bytesStored ${String(stats.bytesStored)}`,
|
|
240
|
+
`retrieveCalls ${String(stats.retrieveCalls)}`,
|
|
241
|
+
`uniqueRetrieved ${String(stats.uniqueRetrieved)}`,
|
|
242
|
+
`expansionRate ${String(stats.expansionRate)}`,
|
|
243
|
+
`allElisionsRetrieved ${String(stats.allElisionsRetrieved)}`,
|
|
244
|
+
'',
|
|
245
|
+
].join('\n'));
|
|
246
|
+
return EXIT.ok;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Law 3, from a shell.
|
|
250
|
+
*
|
|
251
|
+
* This is deliberately not "print the text and hope": it rebuilds the store from the
|
|
252
|
+
* envelope, checks every hash against the bytes it claims to key, and checks the
|
|
253
|
+
* reconstructed length against the `inputBytes` the result recorded at the time of the
|
|
254
|
+
* cut. A round trip that quietly returns almost-right text is the failure this whole
|
|
255
|
+
* repository is arranged against.
|
|
256
|
+
*/
|
|
257
|
+
function runReconstruct(text, io) {
|
|
258
|
+
const { result, elided } = parseEnvelope(text);
|
|
259
|
+
const store = new MemoryElisionStore();
|
|
260
|
+
for (const [hash, content] of Object.entries(elided)) {
|
|
261
|
+
const actual = store.put(content);
|
|
262
|
+
if (actual !== hash) {
|
|
263
|
+
throw new CliUsageError(`${CLI_NAME}: envelope is not self-consistent — it stores bytes under ` +
|
|
264
|
+
`"${hash}" that actually hash to "${actual}". Refusing to reconstruct from it.`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
for (const elision of result.elisions) {
|
|
268
|
+
if (!store.has(elision.hash)) {
|
|
269
|
+
throw new CliUsageError(`${CLI_NAME}: envelope is missing the bytes for "${elision.hash}". A result ` +
|
|
270
|
+
`without its elided bytes cannot be reconstructed; re-run with --json.`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const original = reconstruct(result, store);
|
|
274
|
+
const bytes = Buffer.byteLength(original, 'utf8');
|
|
275
|
+
if (bytes !== result.inputBytes) {
|
|
276
|
+
throw new SmeltError(`${CLI_NAME}: reconstruction produced ${String(bytes)} bytes but the result ` +
|
|
277
|
+
`recorded ${String(result.inputBytes)}. The round trip did not close.`);
|
|
278
|
+
}
|
|
279
|
+
io.stdout(original);
|
|
280
|
+
io.stderr(`${CLI_NAME} reconstructed ${String(result.inputBytes)} B from ` +
|
|
281
|
+
`${String(result.elisions.length)} elisions — byte for byte\n`);
|
|
282
|
+
return EXIT.ok;
|
|
283
|
+
}
|
|
284
|
+
/** `peek`, not `retrieve`: writing a file is not the model asking for anything back. */
|
|
285
|
+
function envelope(result, store) {
|
|
286
|
+
const elided = {};
|
|
287
|
+
for (const elision of result.elisions) {
|
|
288
|
+
const content = store.peek(elision.hash);
|
|
289
|
+
if (content === undefined) {
|
|
290
|
+
throw new SmeltError(`${CLI_NAME}: the store does not hold "${elision.hash}", which its own result ` +
|
|
291
|
+
`says it elided. Refusing to write an envelope that cannot round-trip.`);
|
|
292
|
+
}
|
|
293
|
+
elided[elision.hash] = content;
|
|
294
|
+
}
|
|
295
|
+
return { format: CLI_JSON_FORMAT, result, elided };
|
|
296
|
+
}
|
|
297
|
+
function parseEnvelope(text) {
|
|
298
|
+
let value;
|
|
299
|
+
try {
|
|
300
|
+
value = JSON.parse(text);
|
|
301
|
+
}
|
|
302
|
+
catch (cause) {
|
|
303
|
+
throw new CliUsageError(`${CLI_NAME}: --reconstruct expected a --json envelope, and this is not JSON: ` +
|
|
304
|
+
`${cause instanceof Error ? cause.message : String(cause)}`);
|
|
305
|
+
}
|
|
306
|
+
if (typeof value !== 'object' || value === null) {
|
|
307
|
+
throw new CliUsageError(`${CLI_NAME}: --reconstruct expected a JSON object.`);
|
|
308
|
+
}
|
|
309
|
+
const fields = value;
|
|
310
|
+
if (fields['format'] !== CLI_JSON_FORMAT) {
|
|
311
|
+
throw new CliUsageError(`${CLI_NAME}: this envelope says format "${String(fields['format'])}"; this build ` +
|
|
312
|
+
`reads "${CLI_JSON_FORMAT}". Formats are versioned so a mismatch is visible ` +
|
|
313
|
+
`instead of being half-understood.`);
|
|
314
|
+
}
|
|
315
|
+
const result = fields['result'];
|
|
316
|
+
if (typeof result !== 'object' || result === null) {
|
|
317
|
+
throw new CliUsageError(`${CLI_NAME}: envelope has no \`result\` object.`);
|
|
318
|
+
}
|
|
319
|
+
const resultFields = result;
|
|
320
|
+
if (typeof resultFields['text'] !== 'string' ||
|
|
321
|
+
typeof resultFields['inputBytes'] !== 'number' ||
|
|
322
|
+
!Array.isArray(resultFields['elisions'])) {
|
|
323
|
+
throw new CliUsageError(`${CLI_NAME}: envelope's \`result\` is missing text, inputBytes or elisions.`);
|
|
324
|
+
}
|
|
325
|
+
const elided = fields['elided'];
|
|
326
|
+
if (typeof elided !== 'object' || elided === null) {
|
|
327
|
+
throw new CliUsageError(`${CLI_NAME}: envelope has no \`elided\` map.`);
|
|
328
|
+
}
|
|
329
|
+
for (const [hash, content] of Object.entries(elided)) {
|
|
330
|
+
if (typeof content !== 'string') {
|
|
331
|
+
throw new CliUsageError(`${CLI_NAME}: envelope's \`elided["${hash}"]\` is not a string.`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
format: CLI_JSON_FORMAT,
|
|
336
|
+
result: result,
|
|
337
|
+
elided: elided,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function readInput(file, io) {
|
|
341
|
+
if (file === undefined)
|
|
342
|
+
return io.stdin();
|
|
343
|
+
try {
|
|
344
|
+
return readFileSync(file, 'utf8');
|
|
345
|
+
}
|
|
346
|
+
catch (cause) {
|
|
347
|
+
throw new CliUsageError(`${CLI_NAME}: cannot read "${file}": ` +
|
|
348
|
+
`${cause instanceof Error ? cause.message : String(cause)}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DetectedLanguage, LanguageId } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Every language smelt has a grammar mapping for — the registry's ids, in registry
|
|
4
|
+
* order. A derived view of `LANGUAGE_PROFILES`, kept as an export because tests and
|
|
5
|
+
* the CLI's rendered lists read it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SUPPORTED_LANGUAGES: readonly LanguageId[];
|
|
8
|
+
/**
|
|
9
|
+
* Detect the language of a path, from its extension. Returns `'unknown'` for anything
|
|
10
|
+
* unmapped — which is a normal outcome, not an error: it selects the lexical planner,
|
|
11
|
+
* which works on anything. The extension → profile mapping itself lives on each
|
|
12
|
+
* language's `LanguageProfile` (`src/lang/`); this is the id-shaped view of
|
|
13
|
+
* {@link profileForPath}.
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectLanguage(path: string | undefined): DetectedLanguage;
|
|
16
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,UAAU,EAEpD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAEzE"}
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LANGUAGE_PROFILES, profileForPath } from './lang/registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Every language smelt has a grammar mapping for — the registry's ids, in registry
|
|
4
|
+
* order. A derived view of `LANGUAGE_PROFILES`, kept as an export because tests and
|
|
5
|
+
* the CLI's rendered lists read it.
|
|
6
|
+
*/
|
|
7
|
+
export const SUPPORTED_LANGUAGES = Object.values(LANGUAGE_PROFILES).map((profile) => profile.id);
|
|
8
|
+
/**
|
|
9
|
+
* Detect the language of a path, from its extension. Returns `'unknown'` for anything
|
|
10
|
+
* unmapped — which is a normal outcome, not an error: it selects the lexical planner,
|
|
11
|
+
* which works on anything. The extension → profile mapping itself lives on each
|
|
12
|
+
* language's `LanguageProfile` (`src/lang/`); this is the id-shaped view of
|
|
13
|
+
* {@link profileForPath}.
|
|
14
|
+
*/
|
|
15
|
+
export function detectLanguage(path) {
|
|
16
|
+
return profileForPath(path)?.id ?? 'unknown';
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA0B,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAC5F,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CACxB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB;IACrD,OAAO,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,SAAS,CAAC;AAC/C,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every error smelt throws is one of these. Consumers can `instanceof SmeltError`
|
|
3
|
+
* to tell "the library said no" apart from "something else blew up".
|
|
4
|
+
*/
|
|
5
|
+
export declare class SmeltError extends Error {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A scaffold stub. It throws instead of returning a plausible-looking wrong answer,
|
|
10
|
+
* because a context optimizer that quietly returns bad output is indistinguishable
|
|
11
|
+
* from one that works. See CONTRIBUTING.md § "Silence is the enemy".
|
|
12
|
+
*/
|
|
13
|
+
export declare class NotImplementedError extends SmeltError {
|
|
14
|
+
readonly name = "NotImplementedError";
|
|
15
|
+
constructor(what: string, seeAlso: string);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when something in the elision path tried to reach a non-local resource.
|
|
19
|
+
* v1 has no network. See docs/HANDOFF.md § "Law 1 — zero network".
|
|
20
|
+
*/
|
|
21
|
+
export declare class NetworkPolicyError extends SmeltError {
|
|
22
|
+
readonly name = "NetworkPolicyError";
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
/** A plan asked to elide two overlapping ranges. Applying it would corrupt the output. */
|
|
26
|
+
export declare class OverlappingElisionError extends SmeltError {
|
|
27
|
+
readonly name = "OverlappingElisionError";
|
|
28
|
+
}
|
|
29
|
+
/** A plan referenced a byte range that is not inside the input. */
|
|
30
|
+
export declare class RangeOutOfBoundsError extends SmeltError {
|
|
31
|
+
readonly name = "RangeOutOfBoundsError";
|
|
32
|
+
}
|
|
33
|
+
/** A grammar was requested that is not installed or not registered. */
|
|
34
|
+
export declare class GrammarUnavailableError extends SmeltError {
|
|
35
|
+
readonly name = "GrammarUnavailableError";
|
|
36
|
+
}
|
|
37
|
+
/** `retrieve(hash)` was called with a hash the store does not hold. */
|
|
38
|
+
export declare class UnknownHashError extends SmeltError {
|
|
39
|
+
readonly name = "UnknownHashError";
|
|
40
|
+
constructor(hash: string);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A planner was handed a `PlanInput` without `pricing`. The type makes `pricing`
|
|
44
|
+
* required, so TypeScript callers cannot get here; a JS caller can, and the honest
|
|
45
|
+
* answer is this error rather than a guessed marker cost — a planner pricing markers
|
|
46
|
+
* itself is exactly the inversion the MarkerPricing seam removed.
|
|
47
|
+
*/
|
|
48
|
+
export declare class MissingMarkerPricingError extends SmeltError {
|
|
49
|
+
readonly name = "MissingMarkerPricingError";
|
|
50
|
+
constructor(plannerId: string);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The CLI was invoked wrongly — a missing `--budget`, an unknown flag, a budget that
|
|
54
|
+
* is not a number. Distinct from every other `SmeltError` so the CLI can exit with a
|
|
55
|
+
* usage code rather than pretending the library refused.
|
|
56
|
+
*/
|
|
57
|
+
export declare class CliUsageError extends SmeltError {
|
|
58
|
+
readonly name = "CliUsageError";
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Two different blobs hashed to the same key. Astronomically unlikely, and yet: the
|
|
62
|
+
* alternative to throwing is handing the model the wrong bytes and calling it a
|
|
63
|
+
* retrieval, which is precisely the silent failure this library exists to avoid.
|
|
64
|
+
*/
|
|
65
|
+
export declare class HashCollisionError extends SmeltError {
|
|
66
|
+
readonly name = "HashCollisionError";
|
|
67
|
+
constructor(hash: string);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A persistent store holds bytes under this hash, but they no longer hash to it — a
|
|
71
|
+
* torn write, a truncation, an edit behind the store's back. Deliberately distinct from
|
|
72
|
+
* {@link UnknownHashError}: "we hold damaged bytes" and "never existed" call for
|
|
73
|
+
* different responses, and returning the damaged bytes as a retrieval would be the
|
|
74
|
+
* silent wrong answer this library exists to refuse.
|
|
75
|
+
*/
|
|
76
|
+
export declare class StoreCorruptionError extends SmeltError {
|
|
77
|
+
readonly name = "StoreCorruptionError";
|
|
78
|
+
constructor(hash: string);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A directory offered as a persistent store carries a format marker this version of
|
|
82
|
+
* smelt does not understand — or no parseable marker at all. Refusing beats guessing:
|
|
83
|
+
* reinterpreting an unknown layout could hand back the wrong bytes with no error.
|
|
84
|
+
*/
|
|
85
|
+
export declare class StoreFormatError extends SmeltError {
|
|
86
|
+
readonly name = "StoreFormatError";
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,SAAkB,IAAI,EAAE,MAAM,CAAgB;CAC/C;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,SAAkB,IAAI,yBAAyB;IAE/C,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAKxC;CACF;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAkB,IAAI,wBAAwB;IAE9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,0FAA0F;AAC1F,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,SAAkB,IAAI,6BAA6B;CACpD;AAED,mEAAmE;AACnE,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,SAAkB,IAAI,2BAA2B;CAClD;AAED,uEAAuE;AACvE,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,SAAkB,IAAI,6BAA6B;CACpD;AAED,uEAAuE;AACvE,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAkB,IAAI,sBAAsB;IAE5C,YAAY,IAAI,EAAE,MAAM,EAIvB;CACF;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,UAAU;IACvD,SAAkB,IAAI,+BAA+B;IAErD,YAAY,SAAS,EAAE,MAAM,EAO5B;CACF;AAED;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,SAAkB,IAAI,mBAAmB;CAC1C;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAkB,IAAI,wBAAwB;IAE9C,YAAY,IAAI,EAAE,MAAM,EAKvB;CACF;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,SAAkB,IAAI,0BAA0B;IAEhD,YAAY,IAAI,EAAE,MAAM,EAMvB;CACF;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAkB,IAAI,sBAAsB;CAC7C"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every error smelt throws is one of these. Consumers can `instanceof SmeltError`
|
|
3
|
+
* to tell "the library said no" apart from "something else blew up".
|
|
4
|
+
*/
|
|
5
|
+
export class SmeltError extends Error {
|
|
6
|
+
name = 'SmeltError';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A scaffold stub. It throws instead of returning a plausible-looking wrong answer,
|
|
10
|
+
* because a context optimizer that quietly returns bad output is indistinguishable
|
|
11
|
+
* from one that works. See CONTRIBUTING.md § "Silence is the enemy".
|
|
12
|
+
*/
|
|
13
|
+
export class NotImplementedError extends SmeltError {
|
|
14
|
+
name = 'NotImplementedError';
|
|
15
|
+
constructor(what, seeAlso) {
|
|
16
|
+
super(`smelt: ${what} is not implemented yet. This is a scaffold stub — it throws rather ` +
|
|
17
|
+
`than returning a plausible wrong answer. See ${seeAlso}.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when something in the elision path tried to reach a non-local resource.
|
|
22
|
+
* v1 has no network. See docs/HANDOFF.md § "Law 1 — zero network".
|
|
23
|
+
*/
|
|
24
|
+
export class NetworkPolicyError extends SmeltError {
|
|
25
|
+
name = 'NetworkPolicyError';
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(`smelt: ${message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** A plan asked to elide two overlapping ranges. Applying it would corrupt the output. */
|
|
31
|
+
export class OverlappingElisionError extends SmeltError {
|
|
32
|
+
name = 'OverlappingElisionError';
|
|
33
|
+
}
|
|
34
|
+
/** A plan referenced a byte range that is not inside the input. */
|
|
35
|
+
export class RangeOutOfBoundsError extends SmeltError {
|
|
36
|
+
name = 'RangeOutOfBoundsError';
|
|
37
|
+
}
|
|
38
|
+
/** A grammar was requested that is not installed or not registered. */
|
|
39
|
+
export class GrammarUnavailableError extends SmeltError {
|
|
40
|
+
name = 'GrammarUnavailableError';
|
|
41
|
+
}
|
|
42
|
+
/** `retrieve(hash)` was called with a hash the store does not hold. */
|
|
43
|
+
export class UnknownHashError extends SmeltError {
|
|
44
|
+
name = 'UnknownHashError';
|
|
45
|
+
constructor(hash) {
|
|
46
|
+
super(`smelt: no stored content for hash "${hash}". It was never elided, or the store was reset.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A planner was handed a `PlanInput` without `pricing`. The type makes `pricing`
|
|
51
|
+
* required, so TypeScript callers cannot get here; a JS caller can, and the honest
|
|
52
|
+
* answer is this error rather than a guessed marker cost — a planner pricing markers
|
|
53
|
+
* itself is exactly the inversion the MarkerPricing seam removed.
|
|
54
|
+
*/
|
|
55
|
+
export class MissingMarkerPricingError extends SmeltError {
|
|
56
|
+
name = 'MissingMarkerPricingError';
|
|
57
|
+
constructor(plannerId) {
|
|
58
|
+
super(`smelt: ${plannerId} was handed a PlanInput without \`pricing\`. A planner never ` +
|
|
59
|
+
`guesses what a marker costs — the applier renders markers, so the applier ` +
|
|
60
|
+
`prices them. Build one with markerPricing(language, marker) from apply.ts and ` +
|
|
61
|
+
`put it on the input; createSmelter and the CLI construct it centrally.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The CLI was invoked wrongly — a missing `--budget`, an unknown flag, a budget that
|
|
66
|
+
* is not a number. Distinct from every other `SmeltError` so the CLI can exit with a
|
|
67
|
+
* usage code rather than pretending the library refused.
|
|
68
|
+
*/
|
|
69
|
+
export class CliUsageError extends SmeltError {
|
|
70
|
+
name = 'CliUsageError';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Two different blobs hashed to the same key. Astronomically unlikely, and yet: the
|
|
74
|
+
* alternative to throwing is handing the model the wrong bytes and calling it a
|
|
75
|
+
* retrieval, which is precisely the silent failure this library exists to avoid.
|
|
76
|
+
*/
|
|
77
|
+
export class HashCollisionError extends SmeltError {
|
|
78
|
+
name = 'HashCollisionError';
|
|
79
|
+
constructor(hash) {
|
|
80
|
+
super(`smelt: hash collision on "${hash}" — two different blobs share a key. Refusing to ` +
|
|
81
|
+
`store, because retrieving would return the wrong bytes. Please report this.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A persistent store holds bytes under this hash, but they no longer hash to it — a
|
|
86
|
+
* torn write, a truncation, an edit behind the store's back. Deliberately distinct from
|
|
87
|
+
* {@link UnknownHashError}: "we hold damaged bytes" and "never existed" call for
|
|
88
|
+
* different responses, and returning the damaged bytes as a retrieval would be the
|
|
89
|
+
* silent wrong answer this library exists to refuse.
|
|
90
|
+
*/
|
|
91
|
+
export class StoreCorruptionError extends SmeltError {
|
|
92
|
+
name = 'StoreCorruptionError';
|
|
93
|
+
constructor(hash) {
|
|
94
|
+
super(`smelt: the bytes stored under hash "${hash}" do not hash to "${hash}". Refusing ` +
|
|
95
|
+
`to return them — they are damaged, not merely unknown. The store directory was ` +
|
|
96
|
+
`truncated or edited outside smelt.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* A directory offered as a persistent store carries a format marker this version of
|
|
101
|
+
* smelt does not understand — or no parseable marker at all. Refusing beats guessing:
|
|
102
|
+
* reinterpreting an unknown layout could hand back the wrong bytes with no error.
|
|
103
|
+
*/
|
|
104
|
+
export class StoreFormatError extends SmeltError {
|
|
105
|
+
name = 'StoreFormatError';
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACjB,IAAI,GAAW,YAAY,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,UAAU;IAC/B,IAAI,GAAG,qBAAqB,CAAC;IAE/C,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CACH,UAAU,IAAI,sEAAsE;YAClF,gDAAgD,OAAO,GAAG,CAC7D,CAAC;IACJ,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAC9B,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YAAY,OAAe;QACzB,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,0FAA0F;AAC1F,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IACnC,IAAI,GAAG,yBAAyB,CAAC;CACpD;AAED,mEAAmE;AACnE,MAAM,OAAO,qBAAsB,SAAQ,UAAU;IACjC,IAAI,GAAG,uBAAuB,CAAC;CAClD;AAED,uEAAuE;AACvE,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IACnC,IAAI,GAAG,yBAAyB,CAAC;CACpD;AAED,uEAAuE;AACvE,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC5B,IAAI,GAAG,kBAAkB,CAAC;IAE5C,YAAY,IAAY;QACtB,KAAK,CACH,sCAAsC,IAAI,iDAAiD,CAC5F,CAAC;IACJ,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,yBAA0B,SAAQ,UAAU;IACrC,IAAI,GAAG,2BAA2B,CAAC;IAErD,YAAY,SAAiB;QAC3B,KAAK,CACH,UAAU,SAAS,+DAA+D;YAChF,4EAA4E;YAC5E,gFAAgF;YAChF,wEAAwE,CAC3E,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IACzB,IAAI,GAAG,eAAe,CAAC;CAC1C;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAC9B,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YAAY,IAAY;QACtB,KAAK,CACH,6BAA6B,IAAI,mDAAmD;YAClF,6EAA6E,CAChF,CAAC;IACJ,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,oBAAqB,SAAQ,UAAU;IAChC,IAAI,GAAG,sBAAsB,CAAC;IAEhD,YAAY,IAAY;QACtB,KAAK,CACH,uCAAuC,IAAI,qBAAqB,IAAI,cAAc;YAChF,iFAAiF;YACjF,oCAAoC,CACvC,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC5B,IAAI,GAAG,kBAAkB,CAAC;CAC7C"}
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Length of the hex digest smelt uses as a retrieval key.
|
|
3
|
+
*
|
|
4
|
+
* The hash goes into every marker, so the model pays for it in tokens on every
|
|
5
|
+
* elision. 16 hex characters is 64 bits — plenty for the number of elisions one
|
|
6
|
+
* session produces, and cheap enough to inline. The store still verifies content on
|
|
7
|
+
* collision rather than trusting the digest, so shortening it is a token decision,
|
|
8
|
+
* not a correctness one.
|
|
9
|
+
*/
|
|
10
|
+
export declare const HASH_LENGTH = 16;
|
|
11
|
+
/** Content hash of a string, over its UTF-8 bytes. Stable across runs and platforms. */
|
|
12
|
+
export declare function contentHash(content: string): string;
|
|
13
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Length of the hex digest smelt uses as a retrieval key.
|
|
4
|
+
*
|
|
5
|
+
* The hash goes into every marker, so the model pays for it in tokens on every
|
|
6
|
+
* elision. 16 hex characters is 64 bits — plenty for the number of elisions one
|
|
7
|
+
* session produces, and cheap enough to inline. The store still verifies content on
|
|
8
|
+
* collision rather than trusting the digest, so shortening it is a token decision,
|
|
9
|
+
* not a correctness one.
|
|
10
|
+
*/
|
|
11
|
+
export const HASH_LENGTH = 16;
|
|
12
|
+
/** Content hash of a string, over its UTF-8 bytes. Stable across runs and platforms. */
|
|
13
|
+
export function contentHash(content) {
|
|
14
|
+
return createHash('sha256').update(content, 'utf8').digest('hex').slice(0, HASH_LENGTH);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=hash.js.map
|
package/dist/hash.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;AAC1F,CAAC"}
|