@relayburn/sdk 2.2.0 → 2.4.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/CHANGELOG.md +27 -5
- package/README.md +16 -23
- package/package.json +5 -5
- package/src/binding.d.ts +1 -0
- package/src/index.cjs +40 -5
- package/src/index.d.ts +56 -20
- package/src/index.js +53 -24
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.4.0] - 2026-05-08
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Removed the `onLog` option from `summary`, `sessionCost`, `overhead`, `overheadTrim`, `hotspots`, and `compare` option types. The 2.x stack is SQLite-native and has no archive-fallback path to surface, so the callback was already a no-op at the napi boundary. (#374)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Exported `writePendingStamp()` so Node launchers can write generic
|
|
14
|
+
enrichment tags before spawning Claude, Codex, or OpenCode directly.
|
|
15
|
+
- `summary()` options now accept `tags` and `groupByTag` for generic
|
|
16
|
+
enrichment filtering and cost/token grouping.
|
|
17
|
+
- Exported `computeCompareExcluded()` from the Node facade for callers that
|
|
18
|
+
need the same fidelity-exclusion breakdown used by `compare()`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Replaced the TypeScript 1.x deep-conformance test with native 2.x smoke
|
|
23
|
+
coverage against the committed fixture ledger.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- `search()` now accepts a numeric `limit` in the Node facade and normalizes it
|
|
28
|
+
before calling the napi-rs binding.
|
|
29
|
+
|
|
5
30
|
## [2.1.0] - 2026-05-07
|
|
6
31
|
|
|
7
32
|
### Added
|
|
@@ -30,8 +55,5 @@
|
|
|
30
55
|
outside that range stay `BigInt` to avoid silent precision loss.
|
|
31
56
|
- Conformance suite is now wired into CI: `napi build` writes its outputs
|
|
32
57
|
(`.node`, `binding.cjs`, `binding.d.ts`) into `src/` so the generated
|
|
33
|
-
loader's local-file branch resolves; the suite
|
|
34
|
-
|
|
35
|
-
flips `RELAYBURN_SDK_NAPI_BUILT=1` to enable the `deepStrictEqual` gate
|
|
36
|
-
against TS `@relayburn/sdk@1.x`. (#247 part d)
|
|
37
|
-
|
|
58
|
+
loader's local-file branch resolves; the suite was originally wired as a
|
|
59
|
+
deep-equality gate against TS `@relayburn/sdk@1.x`. (#247 part d)
|
package/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
# @relayburn/sdk
|
|
1
|
+
# @relayburn/sdk
|
|
2
2
|
|
|
3
|
-
Embeddable Relayburn SDK
|
|
4
|
-
|
|
5
|
-
`packages/sdk/`.
|
|
3
|
+
Embeddable Relayburn SDK for Node.js. The package is a napi-rs facade over the
|
|
4
|
+
Rust `relayburn-sdk` crate.
|
|
6
5
|
|
|
7
|
-
The
|
|
6
|
+
The package resolves the native binding for your platform via
|
|
8
7
|
`optionalDependencies`:
|
|
9
8
|
|
|
10
9
|
| Platform | Package |
|
|
@@ -16,21 +15,15 @@ The 2.x umbrella resolves the right native binary for your platform via
|
|
|
16
15
|
|
|
17
16
|
Windows (`win32-x64-msvc`) is not yet shipped — see #247 follow-up.
|
|
18
17
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Status
|
|
33
|
-
|
|
34
|
-
This is a `2.0.0-pre` build published to npm under the `next` tag while
|
|
35
|
-
the rest of the Rust port lands. Until the lockstep 2.0 cutover ships, the
|
|
36
|
-
1.x TS SDK at `packages/sdk/` is still the source of truth.
|
|
18
|
+
## API Notes
|
|
19
|
+
|
|
20
|
+
- Large u64 token counts may be `bigint`. napi-rs maps Rust `u64` to
|
|
21
|
+
JavaScript `BigInt`; the facade downcasts safe-range integers to `number`
|
|
22
|
+
and leaves larger values as `bigint`. The declarations widen these fields
|
|
23
|
+
to `number | bigint`.
|
|
24
|
+
- The SDK exposes read verbs such as `summary()`, `sessionCost()`,
|
|
25
|
+
`hotspots()`, `compare()`, `search()`, `exportLedger()`, and
|
|
26
|
+
`exportStamps()`.
|
|
27
|
+
- Launchers can call `writePendingStamp({ harness, cwd, enrichment })`
|
|
28
|
+
before spawning Claude, Codex, or OpenCode, then run `ingest()` to fold
|
|
29
|
+
those generic enrichment tags onto the discovered turns.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayburn/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Embeddable Relayburn SDK — napi-rs bindings over the Rust relayburn-sdk crate (2.x). Drop-in replacement for the TS @relayburn/sdk@1.x.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"optionalDependencies": {
|
|
46
|
-
"@relayburn/sdk-darwin-arm64": "2.
|
|
47
|
-
"@relayburn/sdk-darwin-x64": "2.
|
|
48
|
-
"@relayburn/sdk-linux-arm64-gnu": "2.
|
|
49
|
-
"@relayburn/sdk-linux-x64-gnu": "2.
|
|
46
|
+
"@relayburn/sdk-darwin-arm64": "2.4.0",
|
|
47
|
+
"@relayburn/sdk-darwin-x64": "2.4.0",
|
|
48
|
+
"@relayburn/sdk-linux-arm64-gnu": "2.4.0",
|
|
49
|
+
"@relayburn/sdk-linux-x64-gnu": "2.4.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@napi-rs/cli": "^2.18.4",
|
package/src/binding.d.ts
CHANGED
|
@@ -19,3 +19,4 @@ export declare function overhead(opts?: unknown): Promise<unknown>;
|
|
|
19
19
|
export declare function overheadTrim(opts?: unknown): Promise<unknown>;
|
|
20
20
|
export declare function hotspots(opts?: unknown): Promise<unknown>;
|
|
21
21
|
export declare function compare(opts: unknown): Promise<unknown>;
|
|
22
|
+
export declare function writePendingStamp(opts: unknown): unknown;
|
package/src/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// package is `"type": "commonjs"` and the `exports.require` map is honored.
|
|
5
5
|
//
|
|
6
6
|
// Mirrors the ESM facade verb-for-verb. The sync binding verbs are wrapped
|
|
7
|
-
// in `async` so callers see `Promise<T>` (matching the
|
|
7
|
+
// in `async` so callers see `Promise<T>` (matching the Node facade contract);
|
|
8
8
|
// see `src/index.js` for the rationale.
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
const binding = require('./binding.cjs');
|
|
13
13
|
|
|
14
14
|
// See `src/index.js` for the rationale: napi-rs serializes Rust `u64` /
|
|
15
|
-
// `i64` as JS `BigInt
|
|
16
|
-
//
|
|
17
|
-
// passing in conformance and to match user expectations from 1.x.
|
|
15
|
+
// `i64` as JS `BigInt`. We downcast safe-range BigInts to match common
|
|
16
|
+
// JavaScript caller expectations while keeping larger values precise.
|
|
18
17
|
const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
|
|
19
18
|
const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
|
|
20
19
|
|
|
@@ -40,6 +39,16 @@ function coerceBigInts(value) {
|
|
|
40
39
|
return value;
|
|
41
40
|
}
|
|
42
41
|
|
|
42
|
+
function normalizeSearchOptions(opts) {
|
|
43
|
+
if (!opts || typeof opts !== 'object' || typeof opts.limit !== 'number') {
|
|
44
|
+
return opts;
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isSafeInteger(opts.limit) || opts.limit < 0) {
|
|
47
|
+
throw new RangeError('search limit must be a non-negative safe integer');
|
|
48
|
+
}
|
|
49
|
+
return { ...opts, limit: BigInt(opts.limit) };
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
class Ledger {
|
|
44
53
|
constructor(home) {
|
|
45
54
|
this.home = home;
|
|
@@ -50,6 +59,30 @@ class Ledger {
|
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
function computeCompareExcluded(summary, minimum) {
|
|
63
|
+
const out = { total: 0, aggregateOnly: 0, costOnly: 0, partial: 0, usageOnly: 0 };
|
|
64
|
+
if (minimum === 'partial') return out;
|
|
65
|
+
const order = ['cost-only', 'aggregate-only', 'partial', 'usage-only', 'full'];
|
|
66
|
+
const need = order.indexOf(minimum);
|
|
67
|
+
if (need < 0) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`invalid minimum fidelity: ${minimum} (expected one of ${order.join(', ')})`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const byClass = summary?.byClass ?? {};
|
|
73
|
+
for (const cls of order) {
|
|
74
|
+
if (order.indexOf(cls) >= need) continue;
|
|
75
|
+
const n = Number(byClass[cls] ?? 0);
|
|
76
|
+
if (!n) continue;
|
|
77
|
+
out.total += n;
|
|
78
|
+
if (cls === 'aggregate-only') out.aggregateOnly += n;
|
|
79
|
+
else if (cls === 'cost-only') out.costOnly += n;
|
|
80
|
+
else if (cls === 'partial') out.partial += n;
|
|
81
|
+
else if (cls === 'usage-only') out.usageOnly += n;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
53
86
|
module.exports = {
|
|
54
87
|
Ledger,
|
|
55
88
|
ingest: async (opts) => coerceBigInts(await binding.ingest(opts)),
|
|
@@ -59,7 +92,9 @@ module.exports = {
|
|
|
59
92
|
overheadTrim: async (opts) => coerceBigInts(await binding.overheadTrim(opts)),
|
|
60
93
|
hotspots: async (opts) => coerceBigInts(await binding.hotspots(opts)),
|
|
61
94
|
compare: async (opts) => coerceBigInts(await binding.compare(opts)),
|
|
62
|
-
|
|
95
|
+
writePendingStamp: async (opts) => coerceBigInts(await binding.writePendingStamp(opts)),
|
|
96
|
+
computeCompareExcluded,
|
|
97
|
+
search: async (opts) => coerceBigInts(await binding.search(normalizeSearchOptions(opts))),
|
|
63
98
|
exportLedger: async (opts) => coerceBigInts(await binding.exportLedger(opts)),
|
|
64
99
|
exportStamps: async (opts) => coerceBigInts(await binding.exportStamps(opts)),
|
|
65
100
|
BurnErrorCode: binding.BurnErrorCode,
|
package/src/index.d.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
// Type surface for `@relayburn/sdk@2.x`.
|
|
2
2
|
//
|
|
3
|
-
// Mirrors
|
|
3
|
+
// Mirrors the Rust SDK verb surface through the napi-rs facade, with
|
|
4
|
+
// compatibility affordances for callers migrating from the 1.x JS SDK:
|
|
4
5
|
// - `bigint` is allowed alongside `number` for u64-typed token counts (the
|
|
5
|
-
// napi-rs binding emits `BigInt` for `u64`; the
|
|
6
|
-
//
|
|
7
|
-
// bound to the Rust impl).
|
|
6
|
+
// napi-rs binding emits `BigInt` for `u64`; the facade downcasts safe-range
|
|
7
|
+
// values at runtime).
|
|
8
8
|
// - Async fns return `Promise<T>` — the napi-rs binding uses `async fn`
|
|
9
9
|
// where the Rust SDK does, which is everywhere except the `Ledger.open`
|
|
10
10
|
// constructor.
|
|
11
|
-
//
|
|
12
|
-
// Source-of-truth comment: track `packages/sdk/index.d.ts`. Whenever a verb
|
|
13
|
-
// shape changes in TS, mirror it here AND in the Rust napi-rs binding (#247-a).
|
|
14
11
|
|
|
15
12
|
export interface LedgerOpenOptions { home?: string; contentHome?: string }
|
|
16
13
|
/**
|
|
17
|
-
* Stateful ledger handle. The
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* Stateful ledger handle. The Node facade exposes the static `open()`
|
|
15
|
+
* constructor; instances carry the resolved home for callers that want
|
|
16
|
+
* to confirm which ledger they attached to. Verb methods are reserved
|
|
17
|
+
* for a future facade expansion.
|
|
21
18
|
*/
|
|
22
19
|
export declare class Ledger {
|
|
23
20
|
readonly home: string;
|
|
@@ -29,17 +26,48 @@ export interface IngestReport {
|
|
|
29
26
|
scannedSessions: number | bigint;
|
|
30
27
|
ingestedSessions: number | bigint;
|
|
31
28
|
appendedTurns: number | bigint;
|
|
29
|
+
appliedPendingStamps: number | bigint;
|
|
32
30
|
}
|
|
33
31
|
export declare function ingest(opts?: IngestOptions): Promise<IngestReport>
|
|
34
32
|
|
|
33
|
+
export type PendingStampHarness = 'claude' | 'codex' | 'opencode';
|
|
34
|
+
export interface WritePendingStampOptions {
|
|
35
|
+
harness: PendingStampHarness;
|
|
36
|
+
cwd: string;
|
|
37
|
+
enrichment: Record<string, string>;
|
|
38
|
+
sessionDirHint?: string;
|
|
39
|
+
/** ISO timestamp, e.g. `2026-04-23T00:00:00.000Z`. Defaults to now. */
|
|
40
|
+
spawnStartTs?: string;
|
|
41
|
+
spawnerPid?: number;
|
|
42
|
+
ledgerHome?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface PendingStamp {
|
|
45
|
+
v: number;
|
|
46
|
+
harness: PendingStampHarness;
|
|
47
|
+
spawnerPid: number;
|
|
48
|
+
spawnStartTs: string;
|
|
49
|
+
cwd: string;
|
|
50
|
+
enrichment: Record<string, string>;
|
|
51
|
+
sessionDirHint?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface PendingStampWriteResult {
|
|
54
|
+
file: string;
|
|
55
|
+
stamp: PendingStamp;
|
|
56
|
+
}
|
|
57
|
+
export declare function writePendingStamp(
|
|
58
|
+
opts: WritePendingStampOptions,
|
|
59
|
+
): Promise<PendingStampWriteResult>
|
|
60
|
+
|
|
35
61
|
export interface SummaryOptions {
|
|
36
62
|
session?: string;
|
|
37
63
|
project?: string;
|
|
38
64
|
/** ISO timestamp (e.g. `2026-04-01T00:00:00Z`) or relative range (`24h`, `7d`, `4w`, `2m`). */
|
|
39
65
|
since?: string;
|
|
66
|
+
/** Folded enrichment tag filters; every key/value pair must match. */
|
|
67
|
+
tags?: Record<string, string>;
|
|
68
|
+
/** Group summary costs/tokens by this folded enrichment tag key. */
|
|
69
|
+
groupByTag?: string;
|
|
40
70
|
ledgerHome?: string;
|
|
41
|
-
/** Optional logger invoked when the SQLite archive read fails and the SDK falls back to a full ledger walk. */
|
|
42
|
-
onLog?: (msg: string) => void;
|
|
43
71
|
}
|
|
44
72
|
export declare function summary(opts?: SummaryOptions): Promise<{
|
|
45
73
|
totalTokens: number | bigint;
|
|
@@ -47,6 +75,13 @@ export declare function summary(opts?: SummaryOptions): Promise<{
|
|
|
47
75
|
turnCount: number;
|
|
48
76
|
byTool: Array<{ tool: string; tokens: number | bigint; cost: number; count: number }>;
|
|
49
77
|
byModel: Array<{ model: string; tokens: number | bigint; cost: number }>;
|
|
78
|
+
byTag?: Array<{
|
|
79
|
+
tag: string;
|
|
80
|
+
value?: string;
|
|
81
|
+
tokens: number | bigint;
|
|
82
|
+
cost: number;
|
|
83
|
+
turnCount: number | bigint;
|
|
84
|
+
}>;
|
|
50
85
|
replacementSavings?: {
|
|
51
86
|
calls: number | bigint;
|
|
52
87
|
collapsedCalls: number | bigint;
|
|
@@ -64,7 +99,6 @@ export interface SessionCostOptions {
|
|
|
64
99
|
/** Session id to total. Omit for `{ note: 'no session id provided' }`. */
|
|
65
100
|
session?: string;
|
|
66
101
|
ledgerHome?: string;
|
|
67
|
-
onLog?: (msg: string) => void;
|
|
68
102
|
}
|
|
69
103
|
export interface SessionCostResult {
|
|
70
104
|
sessionId: string | null;
|
|
@@ -85,7 +119,6 @@ export interface OverheadOptions {
|
|
|
85
119
|
since?: string;
|
|
86
120
|
kind?: OverheadFileKind;
|
|
87
121
|
ledgerHome?: string;
|
|
88
|
-
onLog?: (msg: string) => void;
|
|
89
122
|
}
|
|
90
123
|
|
|
91
124
|
export interface OverheadSection {
|
|
@@ -183,7 +216,6 @@ export interface HotspotsOptions {
|
|
|
183
216
|
groupBy?: HotspotsGroupBy;
|
|
184
217
|
patterns?: string[];
|
|
185
218
|
ledgerHome?: string;
|
|
186
|
-
onLog?: (msg: string) => void;
|
|
187
219
|
}
|
|
188
220
|
|
|
189
221
|
export interface HotspotsFileRow {
|
|
@@ -333,7 +365,6 @@ export interface CompareOptions {
|
|
|
333
365
|
minSample?: number;
|
|
334
366
|
minFidelity?: FidelityClass;
|
|
335
367
|
ledgerHome?: string;
|
|
336
|
-
onLog?: (msg: string) => void;
|
|
337
368
|
}
|
|
338
369
|
|
|
339
370
|
export interface CompareResult {
|
|
@@ -352,13 +383,18 @@ export interface CompareResult {
|
|
|
352
383
|
|
|
353
384
|
/** Per-(model, activity) comparison shape. Powers `burn compare`. */
|
|
354
385
|
export declare function compare(opts: CompareOptions): Promise<CompareResult>
|
|
386
|
+
export declare function computeCompareExcluded(
|
|
387
|
+
summary: FidelitySummaryShape,
|
|
388
|
+
minimum: FidelityClass
|
|
389
|
+
): CompareExcludedBreakdown
|
|
355
390
|
|
|
356
391
|
// ---------------------------------------------------------------------------
|
|
357
392
|
// 2.x extensions — surfaces present in `relayburn-sdk` (the Rust crate)
|
|
358
393
|
// but not in the TS 1.x `packages/sdk/index.d.ts`. Pre-1.0 widening per
|
|
359
|
-
// the SDK shape rule; embedders that pinned to 1.x won't see these names
|
|
360
|
-
//
|
|
361
|
-
//
|
|
394
|
+
// the SDK shape rule; embedders that pinned to 1.x won't see these names.
|
|
395
|
+
// The 1.x `onLog` callback is intentionally omitted: it surfaced the
|
|
396
|
+
// archive-fallback path that no longer exists in the SQLite-native 2.x
|
|
397
|
+
// stack (see issue #374), so there is nothing to log.
|
|
362
398
|
// ---------------------------------------------------------------------------
|
|
363
399
|
|
|
364
400
|
export interface SearchQueryOptions {
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Thin ESM facade over the napi-rs binding. The verbs here re-export the
|
|
2
2
|
// matching `#[napi]` exports from the platform package resolved by
|
|
3
|
-
// `./binding.cjs`, with two adjustments
|
|
4
|
-
//
|
|
3
|
+
// `./binding.cjs`, with two compatibility adjustments carried forward from
|
|
4
|
+
// the 1.x SDK contract:
|
|
5
5
|
//
|
|
6
6
|
// 1. The sync `#[napi]` verbs (`summary`, `sessionCost`, `overhead`,
|
|
7
7
|
// `overheadTrim`, `hotspots`, `compare`) are re-exported as `async`
|
|
@@ -23,18 +23,13 @@ import { createRequire } from 'node:module';
|
|
|
23
23
|
const require = createRequire(import.meta.url);
|
|
24
24
|
const binding = require('./binding.cjs');
|
|
25
25
|
|
|
26
|
-
// napi-rs serializes Rust `u64` / `i64` as JS `BigInt`,
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// `
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// outside that range are left as `BigInt`: realistic burn ledgers won't
|
|
34
|
-
// hit 2^53 tokens, but if one ever does, leaking a `BigInt` that crashes
|
|
35
|
-
// a `===` check is strictly safer than silently rounding to the nearest
|
|
36
|
-
// 1024. The TS shape declares `number | bigint` everywhere this matters
|
|
37
|
-
// so the type stays sound either way.
|
|
26
|
+
// napi-rs serializes Rust `u64` / `i64` as JS `BigInt`, while 1.x emitted
|
|
27
|
+
// plain `Number` for the same fields. To keep common caller expectations
|
|
28
|
+
// intact (e.g. `result.turnCount === 0`, not `=== 0n`), downcast every
|
|
29
|
+
// `BigInt` in a verb's return value to `Number` when it fits in
|
|
30
|
+
// `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]`. Values outside that
|
|
31
|
+
// range are left as `BigInt`; leaking a `BigInt` is safer than silently
|
|
32
|
+
// rounding a very large ledger.
|
|
38
33
|
const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
|
|
39
34
|
const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
|
|
40
35
|
|
|
@@ -63,12 +58,20 @@ function coerceBigInts(value) {
|
|
|
63
58
|
return value;
|
|
64
59
|
}
|
|
65
60
|
|
|
61
|
+
function normalizeSearchOptions(opts) {
|
|
62
|
+
if (!opts || typeof opts !== 'object' || typeof opts.limit !== 'number') {
|
|
63
|
+
return opts;
|
|
64
|
+
}
|
|
65
|
+
if (!Number.isSafeInteger(opts.limit) || opts.limit < 0) {
|
|
66
|
+
throw new RangeError('search limit must be a non-negative safe integer');
|
|
67
|
+
}
|
|
68
|
+
return { ...opts, limit: BigInt(opts.limit) };
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
/**
|
|
67
|
-
* Stateful ledger handle.
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* PR — so we replicate that surface and stash the resolved home for
|
|
71
|
-
* introspection.
|
|
72
|
+
* Stateful ledger handle. The 1.x SDK only exposed the static `open(opts)`
|
|
73
|
+
* constructor; instance methods are reserved for a future PR. Keep that shape
|
|
74
|
+
* and stash the resolved home for introspection.
|
|
72
75
|
*/
|
|
73
76
|
export class Ledger {
|
|
74
77
|
constructor(home) {
|
|
@@ -117,13 +120,39 @@ export async function compare(opts) {
|
|
|
117
120
|
return coerceBigInts(await binding.compare(opts));
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
export async function writePendingStamp(opts) {
|
|
124
|
+
return coerceBigInts(await binding.writePendingStamp(opts));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function computeCompareExcluded(summary, minimum) {
|
|
128
|
+
const out = { total: 0, aggregateOnly: 0, costOnly: 0, partial: 0, usageOnly: 0 };
|
|
129
|
+
if (minimum === 'partial') return out;
|
|
130
|
+
const order = ['cost-only', 'aggregate-only', 'partial', 'usage-only', 'full'];
|
|
131
|
+
const need = order.indexOf(minimum);
|
|
132
|
+
if (need < 0) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`invalid minimum fidelity: ${minimum} (expected one of ${order.join(', ')})`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const byClass = summary?.byClass ?? {};
|
|
138
|
+
for (const cls of order) {
|
|
139
|
+
if (order.indexOf(cls) >= need) continue;
|
|
140
|
+
const n = Number(byClass[cls] ?? 0);
|
|
141
|
+
if (!n) continue;
|
|
142
|
+
out.total += n;
|
|
143
|
+
if (cls === 'aggregate-only') out.aggregateOnly += n;
|
|
144
|
+
else if (cls === 'cost-only') out.costOnly += n;
|
|
145
|
+
else if (cls === 'partial') out.partial += n;
|
|
146
|
+
else if (cls === 'usage-only') out.usageOnly += n;
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
120
151
|
// 2.x extensions — exposed by the Rust SDK but not declared in
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
// reach the FTS5 search index and the JSONL export iterators without
|
|
124
|
-
// dropping into the binding directly.
|
|
152
|
+
// the 1.x SDK surface. These let embedders reach the FTS5 search index and
|
|
153
|
+
// JSONL export iterators without dropping into the binding directly.
|
|
125
154
|
export async function search(opts) {
|
|
126
|
-
return coerceBigInts(await binding.search(opts));
|
|
155
|
+
return coerceBigInts(await binding.search(normalizeSearchOptions(opts)));
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
export async function exportLedger(opts) {
|