pikelet-wasm 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +165 -29
- package/README.md +17 -1
- package/complete/builder.mjs +17 -4
- package/complete/format.mjs +6 -1
- package/complete/index.d.ts +9 -0
- package/complete/index.mjs +482 -58
- package/complete/layer-append.mjs +246 -0
- package/complete/layer-chain.mjs +286 -0
- package/complete/layer-compact.mjs +319 -0
- package/complete/layer-encoder.mjs +223 -0
- package/complete/layer-locator.mjs +228 -0
- package/complete/layer-manifest.mjs +271 -0
- package/complete/layer-reader.mjs +782 -0
- package/complete/layer-rebase.mjs +218 -0
- package/complete/lexical.mjs +78 -8
- package/complete/retrieval-abstention.mjs +15 -4
- package/complete/sources.mjs +27 -5
- package/complete/tombstones.mjs +281 -0
- package/dist/engine.scalar.wasm +0 -0
- package/dist/engine.wasm +0 -0
- package/package.json +33 -2
- package/src/artifact/pikelet-artifact-sketch.js +67 -5
package/CHANGELOG.md
CHANGED
|
@@ -8,43 +8,81 @@ Through 0.6.0 these were published as `pancake-wasm` and
|
|
|
8
8
|
packages are pre-1.0: a minor bump may carry breaking changes, and each entry lists them
|
|
9
9
|
first.
|
|
10
10
|
|
|
11
|
-
## 0.
|
|
11
|
+
## pikelet-wasm 0.9.1 / pikelet 0.9.1 — 2026-09-30
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
Fixes to the layered profile shipped in 0.9.0, headlined by a chain that
|
|
14
|
+
lost its abstention verdicts as soon as a layer deleted anything. Both
|
|
15
|
+
packages move to 0.9.1 together, and `pikelet` requires `pikelet-wasm`
|
|
16
|
+
^0.9.1 so that `pikelet mcp` serves chains with the reader fixes.
|
|
16
17
|
|
|
17
|
-
###
|
|
18
|
+
### Compatibility
|
|
18
19
|
|
|
19
|
-
- **
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
about 92 KB into a 1,536-byte buffer. `maxTokens` is now validated as an
|
|
28
|
-
integer in `[8, 512]` at parse, before anything is allocated, `maxSeq` is
|
|
29
|
-
clamped on both sides, and both call sites assert the window fits.
|
|
20
|
+
- **Layers from `append` and `rebase` now record a parent locator** when
|
|
21
|
+
the parent is a local file under the output's directory, so the same
|
|
22
|
+
inputs produce a layer with a different identity than under 0.9.0.
|
|
23
|
+
- **`--remove` and `--supersede` refuse ids they used to truncate**
|
|
24
|
+
(`1,2,3`, `12abc`, `1.5`, `1e3`); give one id per flag.
|
|
25
|
+
- **A chain-member location ending in `#` plus hex is read as a pin.** A
|
|
26
|
+
full 64-hex suffix is verified against the member's identity, and a
|
|
27
|
+
shorter one is refused; it is no longer treated as part of a filename.
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
for a non-deferred query-interp segment (and prefetches it otherwise), and
|
|
33
|
-
the declaration's own `testVectors` are verified after the buffers exist,
|
|
34
|
-
so a long test vector was itself a trigger. Every integrity check passed,
|
|
35
|
-
because the attacker authored the file and its digests bind their bytes.
|
|
36
|
-
|
|
37
|
-
Affects anyone who mounts a `.pikelet` they did not compile — the format's
|
|
38
|
-
intended use, since packs are distributed and range-read from URLs. The
|
|
39
|
-
write is confined to the encoder's WASM linear memory and does not reach
|
|
40
|
-
host JavaScript objects, which is why it is rated high rather than
|
|
41
|
-
critical. Upgrade to get the bound; there is no workaround short of not
|
|
42
|
-
mounting third-party packs.
|
|
29
|
+
### Fixed
|
|
43
30
|
|
|
44
|
-
|
|
31
|
+
- **`pikelet-wasm` now exports `./package.json`.** `require.resolve('pikelet-wasm/package.json')`
|
|
32
|
+
and JSON imports of it threw `ERR_PACKAGE_PATH_NOT_EXPORTED`, which
|
|
33
|
+
tooling that reads the installed version relies on.
|
|
34
|
+
- **`pikelet append` parses `--remove` and `--supersede` ids strictly.**
|
|
35
|
+
`--remove 1,2,3` used to tombstone record 1 alone, and `--remove 12abc`
|
|
36
|
+
record 12, without an error; each flag now takes exactly one decimal id.
|
|
37
|
+
- **`#<sha256>` pins on chain members are enforced.** Every layered
|
|
38
|
+
producer's usage line advertised `--parent <file|url>[#identity]`, but
|
|
39
|
+
nothing parsed the pin: a local path failed as a missing file, and a URL
|
|
40
|
+
dropped the fragment and built on whatever the server returned. `--parent`,
|
|
41
|
+
`--head`, `--onto`, `--old-parent` and `--layer` now verify each pinned
|
|
42
|
+
member's manifest identity before opening anything, and refuse a
|
|
43
|
+
truncated pin.
|
|
44
|
+
- **`append` and `rebase` record a parent locator by default.** They wrote
|
|
45
|
+
one only when given `--parent-locator`, so a rebased layer could not be
|
|
46
|
+
mounted from its head alone and its next rebase needed `--old-parent`.
|
|
47
|
+
When the parent is a local file under the output's directory, its relative
|
|
48
|
+
path is now recorded. An explicit `--parent-locator` is validated up front
|
|
49
|
+
instead of being written as-is and refused by every reader.
|
|
50
|
+
- **A URL chain mounts from its head alone, and locator redirects are
|
|
51
|
+
confined.** Walking up from a URL head failed at the first hop ("a locator
|
|
52
|
+
needs the child's own location") because a range source carried no URL,
|
|
53
|
+
and a parent's URL would then have been read as a file path. The walk now
|
|
54
|
+
resolves each parent over HTTP. A parent named by a publisher's locator is
|
|
55
|
+
confined to the child's origin and directory at every redirect hop, as
|
|
56
|
+
5.1.1 requires; lineage and host-resolver locations are the operator's
|
|
57
|
+
choice and are not confined. `httpRangeSource` gains `url` and a
|
|
58
|
+
`redirectGuard` option.
|
|
59
|
+
- **A tombstone-only layer no longer unscores its chain.** A layer that only
|
|
60
|
+
deletes records carries no vocabulary bloom, and the reader counted that
|
|
61
|
+
as a layer hiding its vocabulary, marking the whole chain
|
|
62
|
+
`drift-exceeded`: on the astro pack, deleting 3 of 3,255 records (drift
|
|
63
|
+
0.0009 against a 0.20 limit) turned every query's verdict to `unscored`.
|
|
64
|
+
The rule now applies only to layers with records, and LAYERED_PROFILE.md
|
|
65
|
+
4.5 and 5.5 say so. Tombstone-only layers already written with 0.9.0
|
|
66
|
+
score again with no rebuild.
|
|
67
|
+
- **`pikelet --help` documents `append`, `compact` and `rebase`**, including
|
|
68
|
+
`--supersede`, `--allow-drift`, `--max-depth` and pins.
|
|
69
|
+
|
|
70
|
+
## pikelet-wasm 0.9.0 / pikelet 0.9.0 — 2026-09-29
|
|
71
|
+
|
|
72
|
+
Everything merged to `main` since 0.8.0 that the 0.8.1 and 0.8.2 point
|
|
73
|
+
releases below did not carry, headlined by the layered profile. Both
|
|
74
|
+
packages move to 0.9.0 together. Pre-1.0, this minor carries breaking
|
|
75
|
+
changes; they are listed first.
|
|
45
76
|
|
|
46
77
|
### Breaking / compatibility
|
|
47
78
|
|
|
79
|
+
- **`pikelet` 0.9.0 requires `pikelet-wasm` ^0.9.0.** Its new `append`,
|
|
80
|
+
`compact` and `rebase` commands and its chain mounts import
|
|
81
|
+
`pikelet-wasm/complete/layer-*.mjs` and `complete/format.mjs`, which no
|
|
82
|
+
0.8.x exports; upgrade both together. A layer's profile string,
|
|
83
|
+
`pikelet-layer-v1`, is refused by every earlier reader by design: a layer
|
|
84
|
+
opened alone would serve a fraction of a corpus.
|
|
85
|
+
|
|
48
86
|
- **The `PANCAKE_ENCODER_WEIGHTS_URL`, `PANCAKE_SEARCH_EMBED_WORKERS`,
|
|
49
87
|
`PANCAKE_SEARCH_STUB_EMBEDDINGS`, and `PANCAKE_SEARCH_PYTHON`
|
|
50
88
|
environment variable fallbacks are removed.** 0.7.0 introduced
|
|
@@ -59,6 +97,42 @@ backlog below. `pikelet` is unchanged at 0.8.1 and picks this up through its
|
|
|
59
97
|
have; CI tests 18, 20 and 22. The `pikelet` CLI is unchanged at
|
|
60
98
|
`>=20`.
|
|
61
99
|
|
|
100
|
+
### Added
|
|
101
|
+
|
|
102
|
+
- **Layered search artifacts** (`LAYERED_PROFILE.md`, Draft 4). A pack can
|
|
103
|
+
be updated by appending a small `pikelet-layer-v1` file instead of
|
|
104
|
+
recompiling: a layer carries only its new records plus a cumulative
|
|
105
|
+
tombstone mask and supersession edges, and commits to its parent's
|
|
106
|
+
identity, so a chain's head identity pins every byte a query can touch.
|
|
107
|
+
`openPikeletChain` (`pikelet-wasm/complete/layer-reader.mjs`) mounts base
|
|
108
|
+
and layers, verifies every parent commitment and spec rule at mount, and
|
|
109
|
+
queries them as one corpus: masked candidate generation per tier,
|
|
110
|
+
chain-global BM25 statistics, one fusion, and the base's abstention fit
|
|
111
|
+
inherited until calibration drift passes the reader's limit (0.20 by
|
|
112
|
+
default), after which results ship `unscored`. Records carry the layer
|
|
113
|
+
that owns them; `record()` reports tombstoned records and their
|
|
114
|
+
successors; `citation()` resolves ids against any head on the mounted
|
|
115
|
+
history. A chain of local files mounts from its head alone through parent
|
|
116
|
+
locators; URL chains mount from a shelf `lineage`.
|
|
117
|
+
- **`pikelet append`** writes a layer over a base or chain: new documents
|
|
118
|
+
from `--source` (repeatable), deletions with `--remove`, and replacements
|
|
119
|
+
with `--supersede <id>=<path>`, reusing the base's own encoder, chunking
|
|
120
|
+
and vocabulary-bloom geometry. It refuses a layer that would push
|
|
121
|
+
calibration drift past the limit unless `--allow-drift` is given.
|
|
122
|
+
- **`pikelet compact`** collapses a chain into a fresh depth-0 base: live
|
|
123
|
+
records and quantized rows copied byte for byte, ids renumbered densely,
|
|
124
|
+
exact lexical statistics, and a refit abstention calibration (`--no-refit`
|
|
125
|
+
ships unscored). A lineage segment lets the compacted base translate
|
|
126
|
+
citations made against the chain.
|
|
127
|
+
- **`pikelet rebase`** moves a layer onto a different head (for instance a
|
|
128
|
+
fork sibling), replaying only what the layer itself did, with
|
|
129
|
+
`--on-conflict refuse|skip|keep-both` and `--on-foreign refuse|drop`.
|
|
130
|
+
- **MCP serves chains.** A shelf entry with a `lineage` mounts a chain,
|
|
131
|
+
pinned by its head identity; `list_packs` reports `chain`, `layers`,
|
|
132
|
+
`tombstones` and `calibrationStatus`, and counts live records; `get_record`
|
|
133
|
+
and search results carry `tombstoned`, `layer`, `supersededBy` and
|
|
134
|
+
`currentSuccessor`.
|
|
135
|
+
|
|
62
136
|
### Changed
|
|
63
137
|
|
|
64
138
|
- **The repository is an npm workspace with the two packages under
|
|
@@ -249,6 +323,68 @@ backlog below. `pikelet` is unchanged at 0.8.1 and picks this up through its
|
|
|
249
323
|
- Browser verification without a WebCrypto backend fails with a clear
|
|
250
324
|
error instead of an obscure one.
|
|
251
325
|
|
|
326
|
+
## 0.8.2 — `pikelet`
|
|
327
|
+
|
|
328
|
+
### Fixed
|
|
329
|
+
|
|
330
|
+
- **`pikelet mcp` answers `initialize` without waiting for its packs to
|
|
331
|
+
mount.** The server mounted every pack before replying to the MCP
|
|
332
|
+
handshake, and a URL mount fetches the pack's resident prefix first: about
|
|
333
|
+
5.6 s for the rust-book pack and 9.2 s for the 649 MiB Simple English
|
|
334
|
+
Wikipedia one. The published three-pack shelf took **10.2 s** to answer
|
|
335
|
+
`initialize`, past the point most MCP clients give up — so the documented
|
|
336
|
+
`mcp --shelf` command timed out for a new user.
|
|
337
|
+
|
|
338
|
+
Mounting now runs concurrently with the protocol loop. `initialize` returns
|
|
339
|
+
in about **0.6 s**; `tools/call` and `tools/list` await the mounts, so a
|
|
340
|
+
client gets a fast handshake and pays the mount cost on its first real call.
|
|
341
|
+
`tools/list` waits deliberately: it advertises the mounted pack names, and
|
|
342
|
+
answering early would name fewer packs than the server goes on to serve.
|
|
343
|
+
|
|
344
|
+
### Documentation
|
|
345
|
+
|
|
346
|
+
- The install instructions lead with `npm install -g pikelet --omit=optional`.
|
|
347
|
+
Measured on a clean project, that is 2 packages / 1.8 MB / "found 0
|
|
348
|
+
vulnerabilities" against 82 packages / 257 MB / "6 vulnerabilities (5 high,
|
|
349
|
+
1 critical)" — every advisory is inside `@xenova/transformers`, which only
|
|
350
|
+
the `create` scaffold path loads. The previous README put the flag in a
|
|
351
|
+
mid-paragraph aside and estimated its cost at "~140 MB".
|
|
352
|
+
- README carries an upgrade note for the `pikelet-wasm@0.8.1` heap-overflow
|
|
353
|
+
fix, which shipped without one.
|
|
354
|
+
|
|
355
|
+
## 0.8.1 — `pikelet-wasm`
|
|
356
|
+
|
|
357
|
+
Security release, cut from `main` ahead of the rest of the backlog, which
|
|
358
|
+
shipped in 0.9.0. `pikelet` is unchanged at 0.8.1 and picks this up through its
|
|
359
|
+
`^0.8.0` dependency range.
|
|
360
|
+
|
|
361
|
+
### Security
|
|
362
|
+
|
|
363
|
+
- **A `.pikelet` can no longer overflow the encoder's WASM heap through an
|
|
364
|
+
out-of-range `maxTokens`.** `complete/inline-transformer.mjs` sized the
|
|
365
|
+
kernel's ids and hidden-state buffers from the artifact's own encoder
|
|
366
|
+
declaration, clamped with `Math.min(declaration.maxTokens || 512, 512)` —
|
|
367
|
+
an upper bound only. A pack declaring `maxTokens: 1` made the windowing
|
|
368
|
+
arithmetic (`windowLen = maxSeq - 2`) negative, so `interior.slice(w *
|
|
369
|
+
windowLen, (w + 1) * windowLen)` returned nearly the whole token sequence
|
|
370
|
+
and the forward pass wrote past both allocations: a 60-token query wrote
|
|
371
|
+
about 92 KB into a 1,536-byte buffer. `maxTokens` is now validated as an
|
|
372
|
+
integer in `[8, 512]` at parse, before anything is allocated, `maxSeq` is
|
|
373
|
+
clamped on both sides, and both call sites assert the window fits.
|
|
374
|
+
|
|
375
|
+
This fired at **mount**, before any query: the reader awaits the encoder
|
|
376
|
+
for a non-deferred query-interp segment (and prefetches it otherwise), and
|
|
377
|
+
the declaration's own `testVectors` are verified after the buffers exist,
|
|
378
|
+
so a long test vector was itself a trigger. Every integrity check passed,
|
|
379
|
+
because the attacker authored the file and its digests bind their bytes.
|
|
380
|
+
|
|
381
|
+
Affects anyone who mounts a `.pikelet` they did not compile — the format's
|
|
382
|
+
intended use, since packs are distributed and range-read from URLs. The
|
|
383
|
+
write is confined to the encoder's WASM linear memory and does not reach
|
|
384
|
+
host JavaScript objects, which is why it is rated high rather than
|
|
385
|
+
critical. Upgrade to get the bound; there is no workaround short of not
|
|
386
|
+
mounting third-party packs.
|
|
387
|
+
|
|
252
388
|
## pikelet 0.8.1 — 2026-09-18
|
|
253
389
|
|
|
254
390
|
- `pikelet` records `mcpName: "io.github.mcn92/pikelet"` and ships
|
package/README.md
CHANGED
|
@@ -10,10 +10,26 @@ A `.pikelet` can carry the source text, semantic index, keyword index, query enc
|
|
|
10
10
|
|
|
11
11
|
The `pikelet` CLI requires Node 20+; the `pikelet-wasm` library runs on Node 18+ (CI tests 18, 20, 22), browsers, and Cloudflare Workers.
|
|
12
12
|
|
|
13
|
+
> **Upgrade to `pikelet-wasm@0.8.1` if you mount packs you did not compile.**
|
|
14
|
+
> 0.8.0 and earlier sized the inline encoder's WASM buffers from the
|
|
15
|
+
> artifact's own `maxTokens` with an upper bound only, so a pack declaring an
|
|
16
|
+
> out-of-range value could overflow them — at mount, before any query, and
|
|
17
|
+
> with every integrity check passing, because the pack's author is the one who
|
|
18
|
+
> signed it. 0.8.1 validates the bound. Packs the toolchain produces were
|
|
19
|
+
> never affected. `pikelet@0.8.1` picks the fix up through its dependency
|
|
20
|
+
> range; see the CHANGELOG for the detail.
|
|
21
|
+
|
|
13
22
|
```bash
|
|
14
23
|
npx pikelet compile --source ./docs --out docs.pikelet
|
|
15
24
|
```
|
|
16
25
|
|
|
26
|
+
Installing rather than using `npx`? Use `npm install -g pikelet
|
|
27
|
+
--omit=optional`. The optional dependency is `@xenova/transformers`, which
|
|
28
|
+
only the `create` scaffold path uses. Measured on a clean project, omitting it
|
|
29
|
+
takes the install from **82 packages / 257 MB / "6 vulnerabilities (5 high, 1
|
|
30
|
+
critical)"** to **2 packages / 1.8 MB / "found 0 vulnerabilities"** — the
|
|
31
|
+
advisories are all inside a dependency `compile` and `mcp` never load.
|
|
32
|
+
|
|
17
33
|
```text
|
|
18
34
|
Ingested 3 docs -> 3 chunks
|
|
19
35
|
Embedded 3/3 chunks with inline transformer
|
|
@@ -47,7 +63,7 @@ console.log(out.matchQuality, out.results[0]?.title);
|
|
|
47
63
|
// 'strong' 'Snapshot restore'
|
|
48
64
|
```
|
|
49
65
|
|
|
50
|
-
That's the whole loop. `compile` also takes a live URL (`--source https://docs.example.com`) instead of a directory. If you want a deployed search app — a Worker + UI, not a file — use `npx pikelet create` instead; see [`packages/pikelet/README.md`](packages/pikelet/README.md) for the full CLI reference and the tradeoffs between the two. `
|
|
66
|
+
That's the whole loop. `compile` also takes a live URL (`--source https://docs.example.com`) instead of a directory. If you want a deployed search app — a Worker + UI, not a file — use `npx pikelet create` instead; see [`packages/pikelet/README.md`](packages/pikelet/README.md) for the full CLI reference and the tradeoffs between the two. `create` is the one path that needs the optional `@xenova/transformers` dependency, which is why the install above omits it by default.
|
|
51
67
|
|
|
52
68
|
Under the hood, the file is one container for everything a reader needs:
|
|
53
69
|
|
package/complete/builder.mjs
CHANGED
|
@@ -456,16 +456,29 @@ function selfQueriesFromSnapshot(graph, maxQueries) {
|
|
|
456
456
|
// explicitly instead of misreading the corpus tables.
|
|
457
457
|
export const PROFILE_V1 = 'pikelet-complete-v1';
|
|
458
458
|
export const PROFILE_V2 = 'pikelet-complete-v2';
|
|
459
|
-
|
|
459
|
+
// A layer is a complete-profile container under formatVersion 2 with its own
|
|
460
|
+
// manifest profile string (LAYERED_PROFILE.md 4.1). A reader that predates the
|
|
461
|
+
// layered profile sees the string under formatVersion 2 and refuses with its
|
|
462
|
+
// existing "unsupported profile" error, which is the intended behavior: a layer
|
|
463
|
+
// opened alone would silently serve a fraction of a corpus.
|
|
464
|
+
export const PROFILE_LAYER = 'pikelet-layer-v1';
|
|
465
|
+
export const FORMAT_VERSIONS = { [PROFILE_V1]: 1, [PROFILE_V2]: 2, [PROFILE_LAYER]: 2 };
|
|
460
466
|
|
|
461
467
|
export function assemblePikeletFile(manifestFields, segments, outPath) {
|
|
462
468
|
const formatVersion = FORMAT_VERSIONS[manifestFields.profile];
|
|
463
469
|
if (!formatVersion) {
|
|
464
470
|
throw new Error(`manifest.profile must be ${PROFILE_V1} or ${PROFILE_V2}, got ${manifestFields.profile}`);
|
|
465
471
|
}
|
|
472
|
+
const isLayer = manifestFields.profile === PROFILE_LAYER;
|
|
466
473
|
const layout = manifestFields.corpus?.layout;
|
|
467
|
-
|
|
468
|
-
|
|
474
|
+
// A tombstone-only layer (4.1) carries a corpus object with records 0 and
|
|
475
|
+
// MUST omit the layout fields, because it has no corpus segment to describe.
|
|
476
|
+
const tombstoneOnlyLayer = isLayer && manifestFields.corpus?.records === 0;
|
|
477
|
+
if (formatVersion === 2 && !tombstoneOnlyLayer && layout !== CORPUS_LAYOUT_V2) {
|
|
478
|
+
throw new Error(`${manifestFields.profile} requires manifest.corpus from buildCorpusSegment() (layout ${CORPUS_LAYOUT_V2})`);
|
|
479
|
+
}
|
|
480
|
+
if (tombstoneOnlyLayer && layout !== undefined) {
|
|
481
|
+
throw new Error('a tombstone-only layer must omit corpus.layout');
|
|
469
482
|
}
|
|
470
483
|
if (formatVersion === 1 && layout !== undefined) {
|
|
471
484
|
throw new Error(`${PROFILE_V1} carries corpus layout v1 (buildCorpusSegmentFromBuffers); got layout ${layout}`);
|
|
@@ -479,7 +492,7 @@ export function assemblePikeletFile(manifestFields, segments, outPath) {
|
|
|
479
492
|
// sketch's own integrity chain to the complete identity without forcing a
|
|
480
493
|
// whole-segment read at open (the index segment stays lazy).
|
|
481
494
|
let indexCommitment;
|
|
482
|
-
if (formatVersion >= 2) {
|
|
495
|
+
if (formatVersion >= 2 && !tombstoneOnlyLayer) {
|
|
483
496
|
const index = segments.find((s) => s.kind === 'index');
|
|
484
497
|
if (!index || index.bytes.length < 256) {
|
|
485
498
|
throw new Error('index segment must carry a 256-byte sketch header');
|
package/complete/format.mjs
CHANGED
|
@@ -4,5 +4,10 @@
|
|
|
4
4
|
export const MAGIC = 0x31465350; // "PSF1"
|
|
5
5
|
export const HEADER_BYTES = 64;
|
|
6
6
|
export const TABLE_ENTRY_BYTES = 48;
|
|
7
|
-
|
|
7
|
+
// Kinds 6 and 7 belong to the layered profile (LAYERED_PROFILE.md 4.2): a
|
|
8
|
+
// layer carries exactly one tombstones segment, and a base compacted from a
|
|
9
|
+
// chain carries at most one lineage segment. They are registered here so the
|
|
10
|
+
// shared table has one definition; a complete-profile artifact never has
|
|
11
|
+
// either, and the required-segment tables stay per-profile.
|
|
12
|
+
export const KINDS = { index: 1, corpus: 2, 'query-interp': 3, evaluation: 4, lexical: 5, tombstones: 6, lineage: 7 };
|
|
8
13
|
export const KIND_NAMES = Object.fromEntries(Object.entries(KINDS).map(([name, kind]) => [kind, name]));
|
package/complete/index.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface HttpRangeSourceStats {
|
|
|
34
34
|
|
|
35
35
|
export interface HttpRangeSource extends CompleteRangeSource {
|
|
36
36
|
stats: HttpRangeSourceStats;
|
|
37
|
+
/** The URL as given, before any redirect; a layer's parent locator resolves against it. */
|
|
38
|
+
url: string;
|
|
37
39
|
init(): Promise<void>;
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -55,6 +57,13 @@ export declare function httpRangeSource(url: string, options?: {
|
|
|
55
57
|
* disables. CDNs rate-limit the parallel range bursts a query issues.
|
|
56
58
|
*/
|
|
57
59
|
maxRetries?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Called with each redirect target before it is followed; throw to refuse
|
|
62
|
+
* the hop. When set, range reads never auto-follow redirects. A chain walk
|
|
63
|
+
* sets it for a parent named by a layer's locator, confining every hop to
|
|
64
|
+
* the child's origin and directory (LAYERED_PROFILE.md 5.1.1).
|
|
65
|
+
*/
|
|
66
|
+
redirectGuard?: (target: string) => void;
|
|
58
67
|
}): HttpRangeSource;
|
|
59
68
|
|
|
60
69
|
export interface CompleteQueryResult {
|