@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
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human words for the tree-sitter node types the structural planner expects at the
|
|
3
|
+
* top level of a TypeScript or TSX file. What the map does not name, the planner
|
|
4
|
+
* still labels honestly — see {@link LanguageStructure.kindLabels}.
|
|
5
|
+
*/
|
|
6
|
+
export const TS_KIND_LABELS = {
|
|
7
|
+
function_declaration: 'function',
|
|
8
|
+
generator_function_declaration: 'function',
|
|
9
|
+
function_signature: 'function',
|
|
10
|
+
class_declaration: 'class',
|
|
11
|
+
abstract_class_declaration: 'class',
|
|
12
|
+
interface_declaration: 'interface',
|
|
13
|
+
type_alias_declaration: 'type alias',
|
|
14
|
+
enum_declaration: 'enum',
|
|
15
|
+
lexical_declaration: 'variable',
|
|
16
|
+
variable_declaration: 'variable',
|
|
17
|
+
import_statement: 'import statement',
|
|
18
|
+
internal_module: 'namespace',
|
|
19
|
+
module: 'namespace',
|
|
20
|
+
ambient_declaration: 'ambient declaration',
|
|
21
|
+
expression_statement: 'statement',
|
|
22
|
+
hash_bang_line: 'shebang',
|
|
23
|
+
comment: 'comment',
|
|
24
|
+
};
|
|
25
|
+
/** One grammar family, two ids: the tsx profile shares this structure verbatim. */
|
|
26
|
+
export const TS_STRUCTURE = {
|
|
27
|
+
commentTypes: new Set(['comment']),
|
|
28
|
+
attributeTypes: new Set(),
|
|
29
|
+
// `#!/usr/bin/env -S npx tsx` parses as a hash_bang_line here too — same node, same
|
|
30
|
+
// law as javascript's: collapsing it changes which interpreter runs the file.
|
|
31
|
+
pinnedTypes: new Set(['hash_bang_line']),
|
|
32
|
+
wrapperTypes: { export_statement: 'export' },
|
|
33
|
+
kindLabels: TS_KIND_LABELS,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Repo-map definition kinds for the typescript grammar family — shared by the
|
|
37
|
+
* typescript, tsx and javascript profiles, exactly as the extraction treats them.
|
|
38
|
+
*/
|
|
39
|
+
export const TS_DEF_KINDS = {
|
|
40
|
+
function_declaration: 'function',
|
|
41
|
+
generator_function_declaration: 'function',
|
|
42
|
+
function_signature: 'function',
|
|
43
|
+
class_declaration: 'class',
|
|
44
|
+
abstract_class_declaration: 'class',
|
|
45
|
+
interface_declaration: 'interface',
|
|
46
|
+
type_alias_declaration: 'type alias',
|
|
47
|
+
enum_declaration: 'enum',
|
|
48
|
+
method_definition: 'method',
|
|
49
|
+
variable_declarator: 'variable',
|
|
50
|
+
};
|
|
51
|
+
/** Repo-map reference node types for the typescript grammar family. */
|
|
52
|
+
export const TS_REF_TYPES = ['identifier', 'type_identifier'];
|
|
53
|
+
export const typescript = {
|
|
54
|
+
id: 'typescript',
|
|
55
|
+
extensions: ['ts', 'mts', 'cts'],
|
|
56
|
+
wasm: 'tree-sitter-typescript.wasm',
|
|
57
|
+
markerLeader: '// ',
|
|
58
|
+
structure: TS_STRUCTURE,
|
|
59
|
+
repomap: { defKinds: TS_DEF_KINDS, refTypes: TS_REF_TYPES },
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=typescript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../src/lang/typescript.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAqC;IAC9D,oBAAoB,EAAE,UAAU;IAChC,8BAA8B,EAAE,UAAU;IAC1C,kBAAkB,EAAE,UAAU;IAC9B,iBAAiB,EAAE,OAAO;IAC1B,0BAA0B,EAAE,OAAO;IACnC,qBAAqB,EAAE,WAAW;IAClC,sBAAsB,EAAE,YAAY;IACpC,gBAAgB,EAAE,MAAM;IACxB,mBAAmB,EAAE,UAAU;IAC/B,oBAAoB,EAAE,UAAU;IAChC,gBAAgB,EAAE,kBAAkB;IACpC,eAAe,EAAE,WAAW;IAC5B,MAAM,EAAE,WAAW;IACnB,mBAAmB,EAAE,qBAAqB;IAC1C,oBAAoB,EAAE,WAAW;IACjC,cAAc,EAAE,SAAS;IACzB,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,YAAY,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAClC,cAAc,EAAE,IAAI,GAAG,EAAE;IACzB,oFAAoF;IACpF,8EAA8E;IAC9E,WAAW,EAAE,IAAI,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC;IACxC,YAAY,EAAE,EAAE,gBAAgB,EAAE,QAAQ,EAAE;IAC5C,UAAU,EAAE,cAAc;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAqC;IAC5D,oBAAoB,EAAE,UAAU;IAChC,8BAA8B,EAAE,UAAU;IAC1C,kBAAkB,EAAE,UAAU;IAC9B,iBAAiB,EAAE,OAAO;IAC1B,0BAA0B,EAAE,OAAO;IACnC,qBAAqB,EAAE,WAAW;IAClC,sBAAsB,EAAE,YAAY;IACpC,gBAAgB,EAAE,MAAM;IACxB,iBAAiB,EAAE,QAAQ;IAC3B,mBAAmB,EAAE,UAAU;CAChC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,YAAY,GAAsB,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;AAEjF,MAAM,CAAC,MAAM,UAAU,GAAoB;IACzC,EAAE,EAAE,YAAY;IAChB,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;IAChC,IAAI,EAAE,6BAA6B;IACnC,YAAY,EAAE,KAAK;IACnB,SAAS,EAAE,YAAY;IACvB,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE;CAC5D,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Law 1 — zero network in v1 — written down once, in one place, so that the guard
|
|
3
|
+
* test and the runtime check cannot drift apart. `test/guards/no-network.test.ts`
|
|
4
|
+
* imports these lists and fails if any module reachable from the public entrypoint
|
|
5
|
+
* imports one of them.
|
|
6
|
+
*
|
|
7
|
+
* This is a *partition of a discovered set*, not an allowlist over an assumed one:
|
|
8
|
+
* the guard walks the real import graph and classifies every edge it finds. A new
|
|
9
|
+
* transport nobody thought of still gets classified — as an unknown bare import,
|
|
10
|
+
* which the guard reports rather than ignores.
|
|
11
|
+
*/
|
|
12
|
+
export declare const FORBIDDEN_NODE_MODULES: readonly string[];
|
|
13
|
+
/** Third-party transports. A dependency that pulls one in is a Law 1 violation too. */
|
|
14
|
+
export declare const FORBIDDEN_PACKAGES: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* Globals that reach the network without an import. Grepped for by the guard — both
|
|
17
|
+
* bare (`fetch(…)`) and qualified through a global object (`globalThis.fetch(…)`),
|
|
18
|
+
* which is why `globalThis` and `global` are themselves on the list: an access
|
|
19
|
+
* routed through the global object is the escape hatch around a bare-name grep.
|
|
20
|
+
*/
|
|
21
|
+
export declare const FORBIDDEN_GLOBALS: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* The other half of the partition.
|
|
24
|
+
*
|
|
25
|
+
* A forbidden-list alone is an allowlist over an assumed set: it can only catch
|
|
26
|
+
* transports somebody already thought of. So the guard classifies *every* import edge
|
|
27
|
+
* it discovers, and an edge that matches neither the forbidden lists nor these
|
|
28
|
+
* permitted ones **fails the build**. Adding a dependency therefore requires adding it
|
|
29
|
+
* here, in a diff, next to the reason — which is the point.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ALLOWED_NODE_BUILTINS: readonly string[];
|
|
32
|
+
/** Third-party packages any smelt module may import. Keep this list boring and short. */
|
|
33
|
+
export declare const ALLOWED_PACKAGES: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* The only URL schemes any smelt code path may resolve.
|
|
36
|
+
*
|
|
37
|
+
* This is not paranoia about our own code. `web-tree-sitter`'s `Language.load()`
|
|
38
|
+
* accepts a `string | URL`, and an `https:` URL there would fetch a grammar over the
|
|
39
|
+
* wire — a real network call inside the elision path, from a dependency's happy path.
|
|
40
|
+
* Every resource smelt hands to a loader goes through {@link assertLocalResource} first.
|
|
41
|
+
*/
|
|
42
|
+
export declare const ALLOWED_URL_SCHEMES: readonly string[];
|
|
43
|
+
/**
|
|
44
|
+
* Reject anything that is not a local file, and return the resolved `file:` URL.
|
|
45
|
+
*
|
|
46
|
+
* @throws {NetworkPolicyError} if the input names a remote scheme.
|
|
47
|
+
*/
|
|
48
|
+
export declare function assertLocalResource(input: string | URL): URL;
|
|
49
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/net/policy.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAenD,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAU/C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAQ9C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAYlD,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAG7C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAc,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,GAAG,CAS5D"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { NetworkPolicyError } from '../errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Law 1 — zero network in v1 — written down once, in one place, so that the guard
|
|
4
|
+
* test and the runtime check cannot drift apart. `test/guards/no-network.test.ts`
|
|
5
|
+
* imports these lists and fails if any module reachable from the public entrypoint
|
|
6
|
+
* imports one of them.
|
|
7
|
+
*
|
|
8
|
+
* This is a *partition of a discovered set*, not an allowlist over an assumed one:
|
|
9
|
+
* the guard walks the real import graph and classifies every edge it finds. A new
|
|
10
|
+
* transport nobody thought of still gets classified — as an unknown bare import,
|
|
11
|
+
* which the guard reports rather than ignores.
|
|
12
|
+
*/
|
|
13
|
+
export const FORBIDDEN_NODE_MODULES = [
|
|
14
|
+
'http',
|
|
15
|
+
'https',
|
|
16
|
+
'http2',
|
|
17
|
+
'net',
|
|
18
|
+
'tls',
|
|
19
|
+
'dgram',
|
|
20
|
+
'dns',
|
|
21
|
+
'node:http',
|
|
22
|
+
'node:https',
|
|
23
|
+
'node:http2',
|
|
24
|
+
'node:net',
|
|
25
|
+
'node:tls',
|
|
26
|
+
'node:dgram',
|
|
27
|
+
'node:dns',
|
|
28
|
+
];
|
|
29
|
+
/** Third-party transports. A dependency that pulls one in is a Law 1 violation too. */
|
|
30
|
+
export const FORBIDDEN_PACKAGES = [
|
|
31
|
+
'undici',
|
|
32
|
+
'axios',
|
|
33
|
+
'node-fetch',
|
|
34
|
+
'got',
|
|
35
|
+
'ky',
|
|
36
|
+
'superagent',
|
|
37
|
+
'ws',
|
|
38
|
+
'socket.io-client',
|
|
39
|
+
'eventsource',
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Globals that reach the network without an import. Grepped for by the guard — both
|
|
43
|
+
* bare (`fetch(…)`) and qualified through a global object (`globalThis.fetch(…)`),
|
|
44
|
+
* which is why `globalThis` and `global` are themselves on the list: an access
|
|
45
|
+
* routed through the global object is the escape hatch around a bare-name grep.
|
|
46
|
+
*/
|
|
47
|
+
export const FORBIDDEN_GLOBALS = [
|
|
48
|
+
'fetch',
|
|
49
|
+
'XMLHttpRequest',
|
|
50
|
+
'WebSocket',
|
|
51
|
+
'EventSource',
|
|
52
|
+
'navigator',
|
|
53
|
+
'globalThis',
|
|
54
|
+
'global',
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* The other half of the partition.
|
|
58
|
+
*
|
|
59
|
+
* A forbidden-list alone is an allowlist over an assumed set: it can only catch
|
|
60
|
+
* transports somebody already thought of. So the guard classifies *every* import edge
|
|
61
|
+
* it discovers, and an edge that matches neither the forbidden lists nor these
|
|
62
|
+
* permitted ones **fails the build**. Adding a dependency therefore requires adding it
|
|
63
|
+
* here, in a diff, next to the reason — which is the point.
|
|
64
|
+
*/
|
|
65
|
+
export const ALLOWED_NODE_BUILTINS = [
|
|
66
|
+
'node:crypto', // content hashing for the elision store
|
|
67
|
+
'node:fs', // reading grammar .wasm files off disk
|
|
68
|
+
'node:fs/promises',
|
|
69
|
+
'node:path',
|
|
70
|
+
'node:url',
|
|
71
|
+
'node:buffer',
|
|
72
|
+
'node:module', // web-tree-sitter's own loader shim needs createRequire
|
|
73
|
+
'node:util', // parseArgs, for the CLI. Argument parsing with zero dependencies.
|
|
74
|
+
'node:process', // argv, stdin/stdout/stderr and the exit code, for the CLI
|
|
75
|
+
'node:readline/promises', // line-by-line answers for `smelt init` — reads streams it is handed, opens nothing
|
|
76
|
+
'node:tty', // isatty(0) for the CLI's TTY check — a plain syscall, no stream, no socket
|
|
77
|
+
];
|
|
78
|
+
/** Third-party packages any smelt module may import. Keep this list boring and short. */
|
|
79
|
+
export const ALLOWED_PACKAGES = [
|
|
80
|
+
'web-tree-sitter', // WASM parsers. No native build step, no download at runtime.
|
|
81
|
+
'tree-sitter-wasms', // prebuilt grammar .wasm blobs, resolved to local file paths
|
|
82
|
+
];
|
|
83
|
+
/**
|
|
84
|
+
* The only URL schemes any smelt code path may resolve.
|
|
85
|
+
*
|
|
86
|
+
* This is not paranoia about our own code. `web-tree-sitter`'s `Language.load()`
|
|
87
|
+
* accepts a `string | URL`, and an `https:` URL there would fetch a grammar over the
|
|
88
|
+
* wire — a real network call inside the elision path, from a dependency's happy path.
|
|
89
|
+
* Every resource smelt hands to a loader goes through {@link assertLocalResource} first.
|
|
90
|
+
*/
|
|
91
|
+
export const ALLOWED_URL_SCHEMES = ['file:'];
|
|
92
|
+
/**
|
|
93
|
+
* Reject anything that is not a local file, and return the resolved `file:` URL.
|
|
94
|
+
*
|
|
95
|
+
* @throws {NetworkPolicyError} if the input names a remote scheme.
|
|
96
|
+
*/
|
|
97
|
+
export function assertLocalResource(input) {
|
|
98
|
+
const url = toUrl(input);
|
|
99
|
+
if (!ALLOWED_URL_SCHEMES.includes(url.protocol)) {
|
|
100
|
+
throw new NetworkPolicyError(`refusing to load "${String(input)}": scheme "${url.protocol}" is not local. ` +
|
|
101
|
+
`v1 makes zero network calls; only ${ALLOWED_URL_SCHEMES.join(', ')} is allowed.`);
|
|
102
|
+
}
|
|
103
|
+
return url;
|
|
104
|
+
}
|
|
105
|
+
function toUrl(input) {
|
|
106
|
+
if (input instanceof URL)
|
|
107
|
+
return input;
|
|
108
|
+
// A bare path is a local path. Anything with a scheme is parsed as-is so that
|
|
109
|
+
// "https://evil/grammar.wasm" is caught rather than treated as a relative path.
|
|
110
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(input)) {
|
|
111
|
+
try {
|
|
112
|
+
return new URL(input);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
throw new NetworkPolicyError(`refusing to load "${input}": not a parseable URL or path.`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return new URL(`file://${input.startsWith('/') ? '' : '/'}${input}`);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/net/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,MAAM;IACN,OAAO;IACP,OAAO;IACP,KAAK;IACL,KAAK;IACL,OAAO;IACP,KAAK;IACL,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,UAAU;IACV,YAAY;IACZ,UAAU;CACX,CAAC;AAEF,uFAAuF;AACvF,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,QAAQ;IACR,OAAO;IACP,YAAY;IACZ,KAAK;IACL,IAAI;IACJ,YAAY;IACZ,IAAI;IACJ,kBAAkB;IAClB,aAAa;CACd,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,OAAO;IACP,gBAAgB;IAChB,WAAW;IACX,aAAa;IACb,WAAW;IACX,YAAY;IACZ,QAAQ;CACT,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,aAAa,EAAE,wCAAwC;IACvD,SAAS,EAAE,uCAAuC;IAClD,kBAAkB;IAClB,WAAW;IACX,UAAU;IACV,aAAa;IACb,aAAa,EAAE,wDAAwD;IACvE,WAAW,EAAE,mEAAmE;IAChF,cAAc,EAAE,2DAA2D;IAC3E,wBAAwB,EAAE,oFAAoF;IAC9G,UAAU,EAAE,4EAA4E;CACzF,CAAC;AAEF,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD,iBAAiB,EAAE,8DAA8D;IACjF,mBAAmB,EAAE,6DAA6D;CACnF,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsB,CAAC,OAAO,CAAC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAmB;IACrD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,kBAAkB,CAC1B,qBAAqB,MAAM,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,QAAQ,kBAAkB;YAC5E,qCAAqC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,KAAK,CAAC,KAAmB;IAChC,IAAI,KAAK,YAAY,GAAG;QAAE,OAAO,KAAK,CAAC;IACvC,8EAA8E;IAC9E,gFAAgF;IAChF,IAAI,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,kBAAkB,CAAC,qBAAqB,KAAK,iCAAiC,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Language } from 'web-tree-sitter';
|
|
2
|
+
import type { LanguageId } from '../types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Grammar file for each language smelt claims to parse — the registry's `wasm`
|
|
5
|
+
* facts, as a map. Derived from `LANGUAGE_PROFILES` (which is
|
|
6
|
+
* `Record<LanguageId, LanguageProfile>`, so adding a `LanguageId` without a profile —
|
|
7
|
+
* and its grammar — is a type error and the two cannot drift). It stays exported
|
|
8
|
+
* because `scripts/bundle-grammars.mjs` and the attribution generator both read it —
|
|
9
|
+
* a hand-written second list of grammar filenames would be exactly the drift the
|
|
10
|
+
* registry exists to prevent.
|
|
11
|
+
*/
|
|
12
|
+
export declare const WASM_BY_LANGUAGE: Readonly<Record<LanguageId, string>>;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a grammar to a path on this machine.
|
|
15
|
+
*
|
|
16
|
+
* The copy bundled in this package wins; `tree-sitter-wasms` is the fallback, for a
|
|
17
|
+
* source checkout that has not run `pnpm build` yet. Note what this function does *not*
|
|
18
|
+
* do: it never constructs a URL from a version string, a CDN base, or anything else.
|
|
19
|
+
* Grammars come off disk — either the ones shipped here or the ones a package manager
|
|
20
|
+
* already installed. A "fetch the grammar on first use" cache is the most natural way
|
|
21
|
+
* to break Law 1 without noticing, because it works perfectly on the machine that
|
|
22
|
+
* wrote it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function grammarPath(language: LanguageId): string;
|
|
25
|
+
/**
|
|
26
|
+
* Load a grammar, from disk, once.
|
|
27
|
+
*
|
|
28
|
+
* The bytes are read here and handed to tree-sitter as a `Uint8Array` rather than
|
|
29
|
+
* passing it a path. `Language.load()` accepts `string | URL`, and a `URL` with an
|
|
30
|
+
* `https:` scheme would make it fetch — inside the elision path, from a dependency's
|
|
31
|
+
* ordinary happy path. Reading the file ourselves removes that capability instead of
|
|
32
|
+
* documenting it, and {@link assertLocalResource} rejects a remote path before we get
|
|
33
|
+
* that far.
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadGrammar(language: LanguageId): Promise<Language>;
|
|
36
|
+
/** Reset the grammar cache. Tests use it; production has no reason to. */
|
|
37
|
+
export declare function clearGrammarCache(): void;
|
|
38
|
+
//# sourceMappingURL=grammar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../src/plan/grammar.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAU,MAAM,iBAAiB,CAAC;AAKnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAEnC,CAAC;AAkBhC;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAexD;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAYzE;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { Language, Parser } from 'web-tree-sitter';
|
|
6
|
+
import { GrammarUnavailableError } from '../errors.js';
|
|
7
|
+
import { LANGUAGE_PROFILES } from '../lang/registry.js';
|
|
8
|
+
import { assertLocalResource } from '../net/policy.js';
|
|
9
|
+
/**
|
|
10
|
+
* Grammar file for each language smelt claims to parse — the registry's `wasm`
|
|
11
|
+
* facts, as a map. Derived from `LANGUAGE_PROFILES` (which is
|
|
12
|
+
* `Record<LanguageId, LanguageProfile>`, so adding a `LanguageId` without a profile —
|
|
13
|
+
* and its grammar — is a type error and the two cannot drift). It stays exported
|
|
14
|
+
* because `scripts/bundle-grammars.mjs` and the attribution generator both read it —
|
|
15
|
+
* a hand-written second list of grammar filenames would be exactly the drift the
|
|
16
|
+
* registry exists to prevent.
|
|
17
|
+
*/
|
|
18
|
+
export const WASM_BY_LANGUAGE = Object.fromEntries(Object.values(LANGUAGE_PROFILES).map((profile) => [profile.id, profile.wasm]));
|
|
19
|
+
/**
|
|
20
|
+
* Where the bundled grammars live, relative to this module.
|
|
21
|
+
*
|
|
22
|
+
* From `dist/plan/grammar.js` and from `src/plan/grammar.ts` alike, `../../grammars/`
|
|
23
|
+
* is this package's own `grammars/` directory — filled by `pnpm build` and shipped
|
|
24
|
+
* inside the npm tarball. That is what makes "zero native compilation, works offline"
|
|
25
|
+
* true rather than aspirational: whoever installs the package has the parsers, with no
|
|
26
|
+
* post-install download and no optional peer dependency to remember. It is also
|
|
27
|
+
* redistribution, which is why `THIRD-PARTY.md` exists and is generated.
|
|
28
|
+
*/
|
|
29
|
+
const BUNDLED_GRAMMAR_DIR = new URL('../../grammars/', import.meta.url);
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const cache = new Map();
|
|
32
|
+
let runtimeReady;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a grammar to a path on this machine.
|
|
35
|
+
*
|
|
36
|
+
* The copy bundled in this package wins; `tree-sitter-wasms` is the fallback, for a
|
|
37
|
+
* source checkout that has not run `pnpm build` yet. Note what this function does *not*
|
|
38
|
+
* do: it never constructs a URL from a version string, a CDN base, or anything else.
|
|
39
|
+
* Grammars come off disk — either the ones shipped here or the ones a package manager
|
|
40
|
+
* already installed. A "fetch the grammar on first use" cache is the most natural way
|
|
41
|
+
* to break Law 1 without noticing, because it works perfectly on the machine that
|
|
42
|
+
* wrote it.
|
|
43
|
+
*/
|
|
44
|
+
export function grammarPath(language) {
|
|
45
|
+
const file = WASM_BY_LANGUAGE[language];
|
|
46
|
+
const bundled = fileURLToPath(new URL(file, BUNDLED_GRAMMAR_DIR));
|
|
47
|
+
if (existsSync(bundled))
|
|
48
|
+
return bundled;
|
|
49
|
+
try {
|
|
50
|
+
return require.resolve(`tree-sitter-wasms/out/${file}`);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new GrammarUnavailableError(`smelt: no grammar for "${language}". The bundled copy is missing (run ` +
|
|
54
|
+
`\`pnpm build\` in a source checkout) and \`tree-sitter-wasms\` is not installed ` +
|
|
55
|
+
`either. Pass \`language: 'unknown'\` to use the lexical planner.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load a grammar, from disk, once.
|
|
60
|
+
*
|
|
61
|
+
* The bytes are read here and handed to tree-sitter as a `Uint8Array` rather than
|
|
62
|
+
* passing it a path. `Language.load()` accepts `string | URL`, and a `URL` with an
|
|
63
|
+
* `https:` scheme would make it fetch — inside the elision path, from a dependency's
|
|
64
|
+
* ordinary happy path. Reading the file ourselves removes that capability instead of
|
|
65
|
+
* documenting it, and {@link assertLocalResource} rejects a remote path before we get
|
|
66
|
+
* that far.
|
|
67
|
+
*/
|
|
68
|
+
export async function loadGrammar(language) {
|
|
69
|
+
const cached = cache.get(language);
|
|
70
|
+
if (cached !== undefined)
|
|
71
|
+
return cached;
|
|
72
|
+
runtimeReady ??= Parser.init();
|
|
73
|
+
await runtimeReady;
|
|
74
|
+
const resolved = assertLocalResource(grammarPath(language));
|
|
75
|
+
const bytes = await readFile(fileURLToPath(resolved));
|
|
76
|
+
const grammar = await Language.load(new Uint8Array(bytes));
|
|
77
|
+
cache.set(language, grammar);
|
|
78
|
+
return grammar;
|
|
79
|
+
}
|
|
80
|
+
/** Reset the grammar cache. Tests use it; production has no reason to. */
|
|
81
|
+
export function clearGrammarCache() {
|
|
82
|
+
cache.clear();
|
|
83
|
+
runtimeReady = undefined;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=grammar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grammar.js","sourceRoot":"","sources":["../../src/plan/grammar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAyC,MAAM,CAAC,WAAW,CACtF,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAChD,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAExE,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;AAC9C,IAAI,YAAuC,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,QAAoB;IAC9C,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAExC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAClE,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAExC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,OAAO,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,uBAAuB,CAC/B,0BAA0B,QAAQ,sCAAsC;YACtE,kFAAkF;YAClF,kEAAkE,CACrE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAoB;IACpD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAExC,YAAY,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,YAAY,CAAC;IAEnB,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3D,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB;IAC/B,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,YAAY,GAAG,SAAS,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ElisionPlan, PlanInput, Planner } from '../types.ts';
|
|
2
|
+
export declare const LEXICAL_PLANNER_ID = "lexical/v1";
|
|
3
|
+
export interface LexicalPlannerOptions {
|
|
4
|
+
/** Lines of context kept either side of a focus match. Shrinks under budget pressure. */
|
|
5
|
+
readonly contextLines?: number;
|
|
6
|
+
/** Never collapse a run shorter than this, however tempting. */
|
|
7
|
+
readonly minRunLines?: number;
|
|
8
|
+
/** With no focus terms: lines kept at the top. */
|
|
9
|
+
readonly headLines?: number;
|
|
10
|
+
/** With no focus terms: lines kept at the bottom. */
|
|
11
|
+
readonly tailLines?: number;
|
|
12
|
+
/** Focus matching is substring, case-insensitive by default. */
|
|
13
|
+
readonly caseSensitive?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The fallback planner, and the reference implementation of the whole pipeline.
|
|
17
|
+
*
|
|
18
|
+
* It knows nothing about syntax: it keeps the lines you were looking for plus a window
|
|
19
|
+
* of context, and collapses the runs in between. That is a weak strategy on code and a
|
|
20
|
+
* perfectly good one on log files, stack traces, JSON dumps and every other thing a
|
|
21
|
+
* coding agent shovels into a prompt — which is why it is the fallback rather than an
|
|
22
|
+
* embarrassment. It also runs on languages smelt has no grammar for, so
|
|
23
|
+
* `language: 'unknown'` is never a dead end.
|
|
24
|
+
*
|
|
25
|
+
* Everything here is deterministic and rule-named. Same input, same plan, and every
|
|
26
|
+
* elision can say which rule produced it.
|
|
27
|
+
*/
|
|
28
|
+
export declare class LexicalPlanner implements Planner {
|
|
29
|
+
#private;
|
|
30
|
+
readonly id = "lexical/v1";
|
|
31
|
+
constructor(options?: LexicalPlannerOptions);
|
|
32
|
+
plan(input: PlanInput): Promise<ElisionPlan>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The synchronous core, exported because it is worth testing and reusing directly.
|
|
36
|
+
*
|
|
37
|
+
* `budgetBytes` is a target, not a guarantee. If the smallest context window still
|
|
38
|
+
* exceeds it, the plan comes back over budget rather than eliding the matches the
|
|
39
|
+
* caller asked to keep — an optimizer that silently drops the thing you searched for is
|
|
40
|
+
* the exact failure this design is built to prevent. Callers who need a hard ceiling
|
|
41
|
+
* check `outputBytes` and decide; smelt will not decide for them.
|
|
42
|
+
*
|
|
43
|
+
* Profitability and budget prediction are **measured, not estimated**: every candidate
|
|
44
|
+
* elision is priced through the input's {@link MarkerPricing} — the seam `apply.ts`
|
|
45
|
+
* builds from the exact builder `applyPlan` will use, comment leader and custom
|
|
46
|
+
* builder included. A guessed constant here once under-counted real ~105-byte markers
|
|
47
|
+
* as 64, so a plan could be "chosen as fitting" and then come back over budget after
|
|
48
|
+
* the markers landed.
|
|
49
|
+
*/
|
|
50
|
+
export declare function planLexical(input: PlanInput, options?: LexicalPlannerOptions): ElisionPlan;
|
|
51
|
+
//# sourceMappingURL=lexical.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lexical.d.ts","sourceRoot":"","sources":["../../src/plan/lexical.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAiB,SAAS,EAAkB,OAAO,EAAE,MAAM,aAAa,CAAC;AAElG,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAK/C,MAAM,WAAW,qBAAqB;IACpC,yFAAyF;IACzF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAUD;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,YAAW,OAAO;;IAC5C,QAAQ,CAAC,EAAE,gBAAsB;IAGjC,YAAY,OAAO,GAAE,qBAA0B,EAE9C;IAED,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAE3C;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,qBAA0B,GAAG,WAAW,CAsC9F"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { MissingMarkerPricingError } from '../errors.js';
|
|
2
|
+
export const LEXICAL_PLANNER_ID = 'lexical/v1';
|
|
3
|
+
/** How hard the head/tail strategy squeezes, in order, when the budget is not met. */
|
|
4
|
+
const HEAD_TAIL_LADDER = [1, 0.5, 0.25, 0.1, 0.05];
|
|
5
|
+
/**
|
|
6
|
+
* The fallback planner, and the reference implementation of the whole pipeline.
|
|
7
|
+
*
|
|
8
|
+
* It knows nothing about syntax: it keeps the lines you were looking for plus a window
|
|
9
|
+
* of context, and collapses the runs in between. That is a weak strategy on code and a
|
|
10
|
+
* perfectly good one on log files, stack traces, JSON dumps and every other thing a
|
|
11
|
+
* coding agent shovels into a prompt — which is why it is the fallback rather than an
|
|
12
|
+
* embarrassment. It also runs on languages smelt has no grammar for, so
|
|
13
|
+
* `language: 'unknown'` is never a dead end.
|
|
14
|
+
*
|
|
15
|
+
* Everything here is deterministic and rule-named. Same input, same plan, and every
|
|
16
|
+
* elision can say which rule produced it.
|
|
17
|
+
*/
|
|
18
|
+
export class LexicalPlanner {
|
|
19
|
+
id = LEXICAL_PLANNER_ID;
|
|
20
|
+
#options;
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.#options = options;
|
|
23
|
+
}
|
|
24
|
+
plan(input) {
|
|
25
|
+
return Promise.resolve(planLexical(input, this.#options));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The synchronous core, exported because it is worth testing and reusing directly.
|
|
30
|
+
*
|
|
31
|
+
* `budgetBytes` is a target, not a guarantee. If the smallest context window still
|
|
32
|
+
* exceeds it, the plan comes back over budget rather than eliding the matches the
|
|
33
|
+
* caller asked to keep — an optimizer that silently drops the thing you searched for is
|
|
34
|
+
* the exact failure this design is built to prevent. Callers who need a hard ceiling
|
|
35
|
+
* check `outputBytes` and decide; smelt will not decide for them.
|
|
36
|
+
*
|
|
37
|
+
* Profitability and budget prediction are **measured, not estimated**: every candidate
|
|
38
|
+
* elision is priced through the input's {@link MarkerPricing} — the seam `apply.ts`
|
|
39
|
+
* builds from the exact builder `applyPlan` will use, comment leader and custom
|
|
40
|
+
* builder included. A guessed constant here once under-counted real ~105-byte markers
|
|
41
|
+
* as 64, so a plan could be "chosen as fitting" and then come back over budget after
|
|
42
|
+
* the markers landed.
|
|
43
|
+
*/
|
|
44
|
+
export function planLexical(input, options = {}) {
|
|
45
|
+
const pricing = requirePricing(input);
|
|
46
|
+
const lines = splitLines(input.text);
|
|
47
|
+
const focus = (input.focus ?? []).filter((term) => term.length > 0);
|
|
48
|
+
const minRunLines = options.minRunLines ?? 3;
|
|
49
|
+
const attempts = focus.length > 0
|
|
50
|
+
? ladder(options.contextLines ?? 4).map((context) => collapse(lines, keepByFocus(lines, focus, context, options.caseSensitive ?? false), {
|
|
51
|
+
minRunLines,
|
|
52
|
+
rule: 'focus-window',
|
|
53
|
+
pricing,
|
|
54
|
+
}))
|
|
55
|
+
: HEAD_TAIL_LADDER.map((shrink) => collapse(lines, keepByHeadTail(lines, Math.max(3, Math.round((options.headLines ?? 40) * shrink)), Math.max(3, Math.round((options.tailLines ?? 20) * shrink))), { minRunLines, rule: 'head-tail', pricing }));
|
|
56
|
+
const inputBytes = Buffer.byteLength(input.text, 'utf8');
|
|
57
|
+
const chosen = attempts.find((elisions) => predictOutputBytes(inputBytes, elisions, pricing) <= input.budgetBytes) ?? attempts[attempts.length - 1];
|
|
58
|
+
return {
|
|
59
|
+
planner: LEXICAL_PLANNER_ID,
|
|
60
|
+
language: input.language,
|
|
61
|
+
elisions: chosen,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Context-window sizes to try, largest first. */
|
|
65
|
+
function ladder(start) {
|
|
66
|
+
const sizes = [];
|
|
67
|
+
for (let n = start; n >= 0; n -= 1)
|
|
68
|
+
sizes.push(n);
|
|
69
|
+
if (sizes.length === 0)
|
|
70
|
+
sizes.push(0);
|
|
71
|
+
return sizes;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The runtime backstop for JS callers: TypeScript makes `pricing` required, but a JS
|
|
75
|
+
* caller can omit it, and the honest answer is a named refusal rather than a planner
|
|
76
|
+
* quietly pricing markers itself — the inversion the seam removed.
|
|
77
|
+
*/
|
|
78
|
+
function requirePricing(input) {
|
|
79
|
+
const pricing = input.pricing;
|
|
80
|
+
if (pricing === undefined)
|
|
81
|
+
throw new MissingMarkerPricingError(LEXICAL_PLANNER_ID);
|
|
82
|
+
return pricing;
|
|
83
|
+
}
|
|
84
|
+
/** The exact UTF-8 cost of the marker this elision would earn. Asked, not guessed. */
|
|
85
|
+
function markerBytes(elision, pricing) {
|
|
86
|
+
return pricing.costBytes(elision.reason, elision.range.end - elision.range.start);
|
|
87
|
+
}
|
|
88
|
+
function predictOutputBytes(inputBytes, elisions, pricing) {
|
|
89
|
+
return elisions.reduce((bytes, elision) => bytes - (elision.range.end - elision.range.start) + markerBytes(elision, pricing), inputBytes);
|
|
90
|
+
}
|
|
91
|
+
function splitLines(text) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
let start = 0;
|
|
94
|
+
let byte = 0;
|
|
95
|
+
const raw = text.split('\n');
|
|
96
|
+
for (let i = 0; i < raw.length; i += 1) {
|
|
97
|
+
const content = raw[i];
|
|
98
|
+
const contentBytes = Buffer.byteLength(content, 'utf8');
|
|
99
|
+
byte = start + contentBytes;
|
|
100
|
+
lines.push({ start, end: byte, text: content });
|
|
101
|
+
// +1 for the '\n' we split on; the final fragment has none.
|
|
102
|
+
start = byte + 1;
|
|
103
|
+
}
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
|
106
|
+
function keepByFocus(lines, focus, contextLines, caseSensitive) {
|
|
107
|
+
const needles = caseSensitive ? focus : focus.map((t) => t.toLowerCase());
|
|
108
|
+
const keep = Array.from({ length: lines.length }, () => false);
|
|
109
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
110
|
+
const haystack = caseSensitive ? lines[i].text : lines[i].text.toLowerCase();
|
|
111
|
+
if (!needles.some((needle) => haystack.includes(needle)))
|
|
112
|
+
continue;
|
|
113
|
+
const from = Math.max(0, i - contextLines);
|
|
114
|
+
const to = Math.min(lines.length - 1, i + contextLines);
|
|
115
|
+
for (let j = from; j <= to; j += 1)
|
|
116
|
+
keep[j] = true;
|
|
117
|
+
}
|
|
118
|
+
return keep;
|
|
119
|
+
}
|
|
120
|
+
function keepByHeadTail(lines, head, tail) {
|
|
121
|
+
const keep = Array.from({ length: lines.length }, () => false);
|
|
122
|
+
for (let i = 0; i < Math.min(head, lines.length); i += 1)
|
|
123
|
+
keep[i] = true;
|
|
124
|
+
for (let i = Math.max(0, lines.length - tail); i < lines.length; i += 1)
|
|
125
|
+
keep[i] = true;
|
|
126
|
+
return keep;
|
|
127
|
+
}
|
|
128
|
+
function collapse(lines, keep, config) {
|
|
129
|
+
const elisions = [];
|
|
130
|
+
let runStart = -1;
|
|
131
|
+
const flush = (endExclusive) => {
|
|
132
|
+
if (runStart < 0)
|
|
133
|
+
return;
|
|
134
|
+
const count = endExclusive - runStart;
|
|
135
|
+
const range = { start: lines[runStart].start, end: lines[endExclusive - 1].end };
|
|
136
|
+
runStart = -1;
|
|
137
|
+
if (count < config.minRunLines)
|
|
138
|
+
return;
|
|
139
|
+
const candidate = {
|
|
140
|
+
range,
|
|
141
|
+
reason: {
|
|
142
|
+
rule: config.rule,
|
|
143
|
+
explanation: `collapsed ${String(count)} ${count === 1 ? 'line' : 'lines'}${describe(config.rule)}`,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
// Profitability, measured rather than estimated: a marker that costs at least as
|
|
147
|
+
// many bytes as it removes grows the output — same rule, same seam, as the
|
|
148
|
+
// structural planner.
|
|
149
|
+
if (range.end - range.start <= markerBytes(candidate, config.pricing))
|
|
150
|
+
return;
|
|
151
|
+
elisions.push(candidate);
|
|
152
|
+
};
|
|
153
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
154
|
+
if (keep[i] === true) {
|
|
155
|
+
flush(i);
|
|
156
|
+
}
|
|
157
|
+
else if (runStart < 0) {
|
|
158
|
+
runStart = i;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
flush(lines.length);
|
|
162
|
+
return elisions;
|
|
163
|
+
}
|
|
164
|
+
function describe(rule) {
|
|
165
|
+
return rule === 'focus-window' ? ' with no match for the focus terms' : ' from the middle';
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=lexical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lexical.js","sourceRoot":"","sources":["../../src/plan/lexical.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Planner } from '../types.ts';
|
|
2
|
+
import type { LexicalPlannerOptions } from './lexical.ts';
|
|
3
|
+
import type { StructuralPlannerOptions } from './structural.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The option bags a strategy factory may draw from — the same fields
|
|
6
|
+
* `SmelterConfig` carries, so the config object itself can be handed to a factory.
|
|
7
|
+
*/
|
|
8
|
+
export interface PlannerFactoryOptions {
|
|
9
|
+
readonly lexical?: LexicalPlannerOptions;
|
|
10
|
+
readonly structural?: StructuralPlannerOptions;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The one registry of planner strategies — string in, constructed {@link Planner} out.
|
|
14
|
+
*
|
|
15
|
+
* This object is the single place the strategy names live. `createSmelter` builds from
|
|
16
|
+
* it, `--strategy` and `smelt.config.json` validation accept exactly its keys, and the
|
|
17
|
+
* `--help` text renders its keys — so a strategy cannot exist in one of those faces and
|
|
18
|
+
* be missing from another. Before this registry the pair was restated in three places,
|
|
19
|
+
* which is how help text rots.
|
|
20
|
+
*
|
|
21
|
+
* `'structural'` parses every language named in {@link STRUCTURAL_LANGUAGES} with a
|
|
22
|
+
* bundled grammar and throws {@link GrammarUnavailableError} for anything else — never
|
|
23
|
+
* a silent lexical fallback. See {@link StructuralPlanner}.
|
|
24
|
+
*/
|
|
25
|
+
export declare const PLANNERS: {
|
|
26
|
+
readonly lexical: (options: PlannerFactoryOptions) => Planner;
|
|
27
|
+
readonly structural: (options: PlannerFactoryOptions) => Planner;
|
|
28
|
+
};
|
|
29
|
+
/** Which planner a smelter uses, named by string. Exactly the keys of {@link PLANNERS}. */
|
|
30
|
+
export type Strategy = keyof typeof PLANNERS;
|
|
31
|
+
/** The registry's keys, in declaration order, for help text and error messages. */
|
|
32
|
+
export declare const STRATEGIES: readonly Strategy[];
|
|
33
|
+
/** The one membership test `--strategy` and config validation both use. */
|
|
34
|
+
export declare function isStrategy(value: string): value is Strategy;
|
|
35
|
+
//# sourceMappingURL=planners.d.ts.map
|