@lanefoundry/gatelane-sdk 0.0.0-canary-b3f6165

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.
Files changed (47) hide show
  1. package/LICENSE +190 -0
  2. package/dist/candidate.d.ts +26 -0
  3. package/dist/candidate.d.ts.map +1 -0
  4. package/dist/candidate.js +41 -0
  5. package/dist/candidate.js.map +1 -0
  6. package/dist/capture.d.ts +105 -0
  7. package/dist/capture.d.ts.map +1 -0
  8. package/dist/capture.js +78 -0
  9. package/dist/capture.js.map +1 -0
  10. package/dist/dataset.d.ts +56 -0
  11. package/dist/dataset.d.ts.map +1 -0
  12. package/dist/dataset.js +35 -0
  13. package/dist/dataset.js.map +1 -0
  14. package/dist/gate.d.ts +81 -0
  15. package/dist/gate.d.ts.map +1 -0
  16. package/dist/gate.js +82 -0
  17. package/dist/gate.js.map +1 -0
  18. package/dist/index.d.ts +16 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +16 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/promotion.d.ts +82 -0
  23. package/dist/promotion.d.ts.map +1 -0
  24. package/dist/promotion.js +14 -0
  25. package/dist/promotion.js.map +1 -0
  26. package/dist/storage-fs.d.ts +27 -0
  27. package/dist/storage-fs.d.ts.map +1 -0
  28. package/dist/storage-fs.js +68 -0
  29. package/dist/storage-fs.js.map +1 -0
  30. package/dist/storage-http.d.ts +43 -0
  31. package/dist/storage-http.d.ts.map +1 -0
  32. package/dist/storage-http.js +112 -0
  33. package/dist/storage-http.js.map +1 -0
  34. package/dist/storage.d.ts +76 -0
  35. package/dist/storage.d.ts.map +1 -0
  36. package/dist/storage.js +52 -0
  37. package/dist/storage.js.map +1 -0
  38. package/package.json +69 -0
  39. package/src/candidate.ts +76 -0
  40. package/src/capture.ts +153 -0
  41. package/src/dataset.ts +85 -0
  42. package/src/gate.ts +162 -0
  43. package/src/index.ts +16 -0
  44. package/src/promotion.ts +98 -0
  45. package/src/storage-fs.ts +76 -0
  46. package/src/storage-http.ts +143 -0
  47. package/src/storage.ts +97 -0
package/dist/gate.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Gate runner registry — the SDK defines the shape, the engine plugs in the impl.
3
+ *
4
+ * The default export is a stub that produces a deterministic mock. Production
5
+ * installs the engine package and calls {@link setGateRunner} once at process
6
+ * startup; subsequent {@link runGate} calls delegate to the engine.
7
+ *
8
+ * Decoupling: SDK doesn't depend on engine (engine depends on SDK). Test code
9
+ * can install a fake runner for deterministic gate outputs.
10
+ *
11
+ * @see docs/prd.md §5.1 — The gate
12
+ */
13
+ import { shaOfCandidateRef } from './candidate.js';
14
+ import { DEFAULT_POLICY, } from './promotion.js';
15
+ let registeredRunner = null;
16
+ /** Install the real engine runner. Idempotent; subsequent calls overwrite. */
17
+ export function setGateRunner(runner) {
18
+ registeredRunner = runner;
19
+ }
20
+ /** Reset to the stub runner. Tests use this. */
21
+ export function resetGateRunner() {
22
+ registeredRunner = null;
23
+ }
24
+ /**
25
+ * Run the gate. If an engine runner is installed, delegates to it.
26
+ * Otherwise produces a deterministic mock report (W1 behavior).
27
+ */
28
+ export async function runGate(args) {
29
+ if (registeredRunner !== null) {
30
+ return registeredRunner(args);
31
+ }
32
+ return stubRunGate(args);
33
+ }
34
+ /** v0.0.0-dev stub — kept for SDK consumers that don't load the engine yet. */
35
+ export async function stubRunGate(args) {
36
+ const policy = args.policy ?? DEFAULT_POLICY;
37
+ const baseline = args.baseline ?? 'model:baseline';
38
+ const runId = crypto.randomUUID();
39
+ const reportId = crypto.randomUUID();
40
+ const candidate_shas = {};
41
+ for (const ref of args.candidates) {
42
+ candidate_shas[ref] = await shaOfCandidateRef(ref);
43
+ }
44
+ const judge_shas = {};
45
+ for (const judge of args.judges) {
46
+ judge_shas[judge] = await shaOfCandidateRef(judge);
47
+ }
48
+ const report = {
49
+ id: reportId,
50
+ gate_run_id: runId,
51
+ dataset_content_hash: args.dataset.content_hash,
52
+ dataset_version: args.dataset.version,
53
+ candidate_shas,
54
+ judge_shas,
55
+ scorer_code_sha: await shaOfCandidateRef('gatelane-sdk:scorer:v0.0.0-dev'),
56
+ baseline_metrics: { primary_score: 0.7 },
57
+ candidate_metrics: Object.fromEntries(args.candidates.map((ref) => [
58
+ ref,
59
+ { aggregate_delta: 0.05, cost_delta: 0.02, latency_delta: 0.03 },
60
+ ])),
61
+ judge_matrix: {
62
+ candidates: args.candidates,
63
+ judges: args.judges,
64
+ winners_by_judge: Object.fromEntries(args.candidates.map((ref) => [ref, args.judges.length])),
65
+ consensus_winners: args.candidates,
66
+ },
67
+ policy,
68
+ ...(args.approver !== undefined ? { approver: args.approver } : {}),
69
+ timestamp: new Date().toISOString(),
70
+ signature: 'stub:unsigned',
71
+ };
72
+ const winner = args.candidates[0] ?? baseline;
73
+ const decision = policy.approval_required
74
+ ? { action: 'hold_for_review', reason: 'approval_required' }
75
+ : {
76
+ action: 'pass',
77
+ winner,
78
+ reason: `stub: all candidates passed policy (min_delta=${policy.min_delta})`,
79
+ };
80
+ return { report, decision, replay_rows: [], verdicts: [], candidate_metrics: {} };
81
+ }
82
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @lanefoundry/gatelane-sdk — promotion gate SDK.
3
+ *
4
+ * Public API for v0.0.1-dev. Engine wires up across W1–W6 per docs/roadmap.md.
5
+ *
6
+ * @see docs/prd.md §5.6 — SDK & API surface
7
+ */
8
+ export * from './capture.js';
9
+ export * from './candidate.js';
10
+ export * from './dataset.js';
11
+ export * from './gate.js';
12
+ export * from './promotion.js';
13
+ export * from './storage.js';
14
+ export * from './storage-fs.js';
15
+ export * from './storage-http.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @lanefoundry/gatelane-sdk — promotion gate SDK.
3
+ *
4
+ * Public API for v0.0.1-dev. Engine wires up across W1–W6 per docs/roadmap.md.
5
+ *
6
+ * @see docs/prd.md §5.6 — SDK & API surface
7
+ */
8
+ export * from './capture.js';
9
+ export * from './candidate.js';
10
+ export * from './dataset.js';
11
+ export * from './gate.js';
12
+ export * from './promotion.js';
13
+ export * from './storage.js';
14
+ export * from './storage-fs.js';
15
+ export * from './storage-http.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Gate policy, report, and decision — the signed artifacts produced by a gate run.
3
+ *
4
+ * @see docs/prd.md §5.1 — The gate
5
+ */
6
+ export type GatePolicy = {
7
+ /** Minimum aggregate delta over baseline required to pass. */
8
+ readonly min_delta: number;
9
+ /** Minimum fraction of judges that must agree on the winning candidate (e.g., 0.67 = 2 of 3). */
10
+ readonly judge_stability_threshold: number;
11
+ /** Maximum cost increase over baseline permitted for a pass decision. */
12
+ readonly cost_ceiling: number;
13
+ /** Maximum latency increase over baseline permitted for a pass decision. */
14
+ readonly latency_ceiling: number;
15
+ /** Whether human approval is required before the gate decision is executed. */
16
+ readonly approval_required: boolean;
17
+ /** Trigger condition under which a deployed canary is automatically blocked. */
18
+ readonly auto_block_rule?: {
19
+ /** Metric drop threshold (e.g., 0.05 = 5% drop). */
20
+ readonly metric_drop: number;
21
+ /** Window to observe canary before passing to 100% (e.g., "24h"). */
22
+ readonly window: string;
23
+ };
24
+ };
25
+ /** @deprecated Use GatePolicy instead. */
26
+ export type PromotionPolicy = GatePolicy;
27
+ export type JudgeStabilityMatrix = {
28
+ readonly candidates: ReadonlyArray<string>;
29
+ readonly judges: ReadonlyArray<string>;
30
+ /** winners_by_judge[candidate] = count of judges that picked this candidate as winner. */
31
+ readonly winners_by_judge: Record<string, number>;
32
+ /** Candidates that won under at least judge_stability_threshold × judges.length judges. */
33
+ readonly consensus_winners: ReadonlyArray<string>;
34
+ };
35
+ export type GateReport = {
36
+ readonly id: string;
37
+ readonly gate_run_id: string;
38
+ readonly dataset_content_hash: string;
39
+ readonly dataset_version: string;
40
+ /** SHA per candidate, keyed by candidate ref. */
41
+ readonly candidate_shas: Record<string, string>;
42
+ /** SHA per judge, keyed by judge ref. */
43
+ readonly judge_shas: Record<string, string>;
44
+ /** SHA of the scorer / evaluator code. */
45
+ readonly scorer_code_sha: string;
46
+ /** Baseline metrics: per-metric. */
47
+ readonly baseline_metrics: Record<string, number>;
48
+ /** Per-candidate metrics: aggregate delta + cost + latency. */
49
+ readonly candidate_metrics: Record<string, {
50
+ aggregate_delta: number;
51
+ cost_delta: number;
52
+ latency_delta: number;
53
+ }>;
54
+ /** Which judge picked which candidate as winner. */
55
+ readonly judge_matrix: JudgeStabilityMatrix;
56
+ /** Policy that was applied to this gate run. */
57
+ readonly policy: GatePolicy;
58
+ /** Who approved the decision (when approval_required = true). */
59
+ readonly approver?: string;
60
+ /** ISO 8601 timestamp of report signing. */
61
+ readonly timestamp: string;
62
+ /** Signature. Stub in v0.0.0-dev. */
63
+ readonly signature: string;
64
+ };
65
+ /** @deprecated Use GateReport instead. */
66
+ export type PromotionReport = GateReport;
67
+ export type GateDecision = {
68
+ readonly action: 'pass';
69
+ readonly winner: string;
70
+ readonly reason: string;
71
+ } | {
72
+ readonly action: 'block';
73
+ readonly reason: string;
74
+ } | {
75
+ readonly action: 'hold_for_review';
76
+ readonly reason: string;
77
+ };
78
+ /** @deprecated Use GateDecision instead. */
79
+ export type PromotionDecision = GateDecision;
80
+ /** Default gate policy. */
81
+ export declare const DEFAULT_POLICY: GatePolicy;
82
+ //# sourceMappingURL=promotion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promotion.d.ts","sourceRoot":"","sources":["../src/promotion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Gate policy, report, and decision — the signed artifacts produced by a gate run.
3
+ *
4
+ * @see docs/prd.md §5.1 — The gate
5
+ */
6
+ /** Default gate policy. */
7
+ export const DEFAULT_POLICY = {
8
+ min_delta: 0.02,
9
+ judge_stability_threshold: 0.67,
10
+ cost_ceiling: 0.1,
11
+ latency_ceiling: 0.2,
12
+ approval_required: false,
13
+ };
14
+ //# sourceMappingURL=promotion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promotion.js","sourceRoot":"","sources":["../src/promotion.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsFH,2BAA2B;AAC3B,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,SAAS,EAAE,IAAI;IACf,yBAAyB,EAAE,IAAI;IAC/B,YAAY,EAAE,GAAG;IACjB,eAAe,EAAE,GAAG;IACpB,iBAAiB,EAAE,KAAK;CACzB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Filesystem storage adapter — writes capture records as JSONL files on disk.
3
+ *
4
+ * Use this in local dev when you don't want to spin up a Cloudflare Worker.
5
+ * Each record is one JSON line; the file name encodes the time so listing is cheap.
6
+ *
7
+ * Not for production: no concurrency control, no rotation, no replay-friendly
8
+ * content addressing. Use HttpStorage in production.
9
+ */
10
+ import type { CaptureRecord, StorageAdapter, WriteResult } from './storage.js';
11
+ export type FilesystemStorageOptions = {
12
+ /** Directory to write capture files. Created if missing. */
13
+ readonly dir: string;
14
+ };
15
+ export declare class FilesystemStorage implements StorageAdapter {
16
+ readonly name: string;
17
+ private readonly dir;
18
+ constructor(opts: FilesystemStorageOptions);
19
+ write(record: CaptureRecord): Promise<WriteResult>;
20
+ read(id: string): Promise<CaptureRecord | null>;
21
+ list(filter: {
22
+ source_kind?: string;
23
+ since?: string;
24
+ limit?: number;
25
+ }): Promise<ReadonlyArray<CaptureRecord>>;
26
+ }
27
+ //# sourceMappingURL=storage-fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-fs.d.ts","sourceRoot":"","sources":["../src/storage-fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE/E,MAAM,MAAM,wBAAwB,GAAG;IACrC,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,IAAI,EAAE,wBAAwB;IAKpC,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IAQlD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAe/C,IAAI,CAAC,MAAM,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;CAuBpH"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Filesystem storage adapter — writes capture records as JSONL files on disk.
3
+ *
4
+ * Use this in local dev when you don't want to spin up a Cloudflare Worker.
5
+ * Each record is one JSON line; the file name encodes the time so listing is cheap.
6
+ *
7
+ * Not for production: no concurrency control, no rotation, no replay-friendly
8
+ * content addressing. Use HttpStorage in production.
9
+ */
10
+ import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
11
+ import { join } from 'node:path';
12
+ export class FilesystemStorage {
13
+ name;
14
+ dir;
15
+ constructor(opts) {
16
+ this.dir = opts.dir;
17
+ this.name = `fs(${opts.dir})`;
18
+ }
19
+ async write(record) {
20
+ await mkdir(this.dir, { recursive: true });
21
+ const stored_at = new Date().toISOString();
22
+ const file = join(this.dir, `${record.started_at.replace(/[:.]/g, '-')}-${record.id}.jsonl`);
23
+ await writeFile(file, JSON.stringify(record), 'utf-8');
24
+ return { id: record.id, stored_at, backend: this.name };
25
+ }
26
+ async read(id) {
27
+ const files = await readdir(this.dir);
28
+ for (const f of files) {
29
+ if (!f.endsWith('.jsonl'))
30
+ continue;
31
+ const text = await readFile(join(this.dir, f), 'utf-8');
32
+ try {
33
+ const record = JSON.parse(text);
34
+ if (record.id === id)
35
+ return record;
36
+ }
37
+ catch {
38
+ // skip malformed
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ async list(filter) {
44
+ const files = (await readdir(this.dir)).filter((f) => f.endsWith('.jsonl')).sort();
45
+ let out = [];
46
+ for (const f of files) {
47
+ const text = await readFile(join(this.dir, f), 'utf-8');
48
+ try {
49
+ out.push(JSON.parse(text));
50
+ }
51
+ catch {
52
+ // skip
53
+ }
54
+ }
55
+ if (filter.source_kind !== undefined) {
56
+ out = out.filter((r) => r.input.metadata?.['source_kind'] === filter.source_kind);
57
+ }
58
+ if (filter.since !== undefined) {
59
+ const since = Date.parse(filter.since);
60
+ out = out.filter((r) => Date.parse(r.started_at) >= since);
61
+ }
62
+ if (filter.limit !== undefined) {
63
+ out = out.slice(0, filter.limit);
64
+ }
65
+ return out;
66
+ }
67
+ }
68
+ //# sourceMappingURL=storage-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-fs.js","sourceRoot":"","sources":["../src/storage-fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * HTTP storage adapter — sends capture records to a gatelane Worker endpoint.
3
+ *
4
+ * Production: `https://<your-worker>.workers.dev/v1/capture`
5
+ * Local dev: `http://localhost:8787/v1/capture`
6
+ *
7
+ * The Worker writes the record body to R2 (raw blob) and the metadata to D1.
8
+ * This adapter only handles transport; the storage / persistence happens server-side.
9
+ *
10
+ * Retries: 3 attempts with exponential backoff on 5xx / network errors.
11
+ *
12
+ * @see docs/prd.md §6.1 — Cloudflare-first deployment
13
+ */
14
+ import type { CaptureRecord, StorageAdapter, WriteResult } from './storage.js';
15
+ export type HttpStorageOptions = {
16
+ /** Base URL of the gatelane Worker, no trailing slash. */
17
+ readonly endpoint: string;
18
+ /** Bearer token for the `/v1/capture` route. */
19
+ readonly token: string;
20
+ /** Max retry attempts on transient failures. Defaults to 3. */
21
+ readonly maxRetries?: number;
22
+ /** Initial backoff in ms. Doubles each retry. Defaults to 200. */
23
+ readonly initialBackoffMs?: number;
24
+ /** Override fetch for testing. Defaults to global fetch. */
25
+ readonly fetchImpl?: typeof fetch;
26
+ };
27
+ export declare class HttpStorage implements StorageAdapter {
28
+ readonly name: string;
29
+ private readonly endpoint;
30
+ private readonly token;
31
+ private readonly maxRetries;
32
+ private readonly initialBackoffMs;
33
+ private readonly fetchImpl;
34
+ constructor(opts: HttpStorageOptions);
35
+ write(record: CaptureRecord): Promise<WriteResult>;
36
+ read(_id: string): Promise<CaptureRecord | null>;
37
+ list(filter?: {
38
+ source_kind?: string;
39
+ since?: string;
40
+ limit?: number;
41
+ }): Promise<ReadonlyArray<CaptureRecord>>;
42
+ }
43
+ //# sourceMappingURL=storage-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-http.d.ts","sourceRoot":"","sources":["../src/storage-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC,CAAC;AAEF,qBAAa,WAAY,YAAW,cAAc;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,IAAI,EAAE,kBAAkB;IAS9B,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IAyClD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAMhD,IAAI,CAAC,MAAM,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;CAkBzH"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * HTTP storage adapter — sends capture records to a gatelane Worker endpoint.
3
+ *
4
+ * Production: `https://<your-worker>.workers.dev/v1/capture`
5
+ * Local dev: `http://localhost:8787/v1/capture`
6
+ *
7
+ * The Worker writes the record body to R2 (raw blob) and the metadata to D1.
8
+ * This adapter only handles transport; the storage / persistence happens server-side.
9
+ *
10
+ * Retries: 3 attempts with exponential backoff on 5xx / network errors.
11
+ *
12
+ * @see docs/prd.md §6.1 — Cloudflare-first deployment
13
+ */
14
+ export class HttpStorage {
15
+ name;
16
+ endpoint;
17
+ token;
18
+ maxRetries;
19
+ initialBackoffMs;
20
+ fetchImpl;
21
+ constructor(opts) {
22
+ this.endpoint = opts.endpoint.replace(/\/$/, '');
23
+ this.token = opts.token;
24
+ this.maxRetries = opts.maxRetries ?? 3;
25
+ this.initialBackoffMs = opts.initialBackoffMs ?? 200;
26
+ this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
27
+ this.name = `http(${new URL(this.endpoint).host})`;
28
+ }
29
+ async write(record) {
30
+ const url = `${this.endpoint}/v1/capture`;
31
+ let lastErr = null;
32
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
33
+ try {
34
+ const response = await this.fetchImpl(url, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'content-type': 'application/json',
38
+ authorization: `Bearer ${this.token}`,
39
+ 'x-gatelane-span-kind': record.span_kind ?? 'gate.replay',
40
+ },
41
+ body: JSON.stringify(record),
42
+ });
43
+ if (response.ok) {
44
+ const body = (await response.json());
45
+ return { id: body.id, stored_at: body.stored_at, backend: this.name };
46
+ }
47
+ // 4xx is fatal — don't retry. 5xx is transient — retry.
48
+ if (response.status >= 400 && response.status < 500) {
49
+ throw new Error(`capture write rejected: ${response.status} ${response.statusText}`);
50
+ }
51
+ lastErr = new Error(`capture write failed: ${response.status} ${response.statusText}`);
52
+ }
53
+ catch (err) {
54
+ // Re-throw 4xx immediately so the caller sees the rejection; only network / 5xx retries.
55
+ if (err instanceof Error && err.message.startsWith('capture write rejected')) {
56
+ throw err;
57
+ }
58
+ lastErr = err;
59
+ }
60
+ // Backoff before next attempt (5xx / network path).
61
+ if (attempt < this.maxRetries - 1) {
62
+ const delay = this.initialBackoffMs * Math.pow(2, attempt);
63
+ const { promise, resolve } = Promise.withResolvers();
64
+ setTimeout(resolve, delay);
65
+ await promise;
66
+ }
67
+ }
68
+ throw new Error(`capture write failed after ${this.maxRetries} attempts: ${String(lastErr)}`);
69
+ }
70
+ async read(_id) {
71
+ // Worker exposes GET /v1/capture/:id in v0.2. Stub: return null.
72
+ // Returning null means "not in local cache; ask the Worker". Tests don't depend on read.
73
+ return null;
74
+ }
75
+ async list(filter = {}) {
76
+ const params = new URLSearchParams();
77
+ if (filter.since !== undefined)
78
+ params.set('since', filter.since);
79
+ if (filter.limit !== undefined)
80
+ params.set('limit', String(filter.limit));
81
+ const url = `${this.endpoint}/v1/captures${params.size > 0 ? '?' + params.toString() : ''}`;
82
+ const response = await this.fetchImpl(url, {
83
+ headers: { authorization: `Bearer ${this.token}` },
84
+ });
85
+ if (!response.ok) {
86
+ throw new Error(`capture list failed: ${response.status} ${response.statusText}`);
87
+ }
88
+ const body = (await response.json());
89
+ let out = body.captures.map(wireToLocal);
90
+ if (filter.source_kind !== undefined) {
91
+ out = out.filter((r) => r.input.metadata?.['source_kind'] === filter.source_kind);
92
+ }
93
+ return out;
94
+ }
95
+ }
96
+ /** Convert a wire capture row back into the SDK's CapturedCall shape. */
97
+ function wireToLocal(row) {
98
+ return {
99
+ id: row.id,
100
+ input: {
101
+ prompt: row.prompt,
102
+ model: row.model,
103
+ metadata: row.metadata,
104
+ },
105
+ output: row.response,
106
+ started_at: row.createdAt,
107
+ completed_at: row.createdAt,
108
+ cost_usd: row.costCents / 100,
109
+ latency_ms: row.latencyMs,
110
+ };
111
+ }
112
+ //# sourceMappingURL=storage-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-http.js","sourceRoot":"","sources":["../src/storage-http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Storage adapter — pluggable back-end for capture records.
3
+ *
4
+ * gatelane's capture SDK is host-agnostic: the same SDK can write to in-memory
5
+ * (tests), filesystem (local dev), or HTTP (production, talking to a Cloudflare
6
+ * Worker that writes to R2/D1).
7
+ *
8
+ * @see docs/prd.md §5.4 — Shared engine
9
+ */
10
+ export type D1DatabaseLike = {
11
+ prepare(query: string): D1PreparedStatementLike;
12
+ };
13
+ export type D1PreparedStatementLike = {
14
+ bind(...values: unknown[]): D1PreparedStatementLike;
15
+ first<T = unknown>(column?: string): Promise<T | null>;
16
+ run(): Promise<{
17
+ success: boolean;
18
+ }>;
19
+ all<T = unknown>(): Promise<{
20
+ results: T[];
21
+ }>;
22
+ };
23
+ export type R2BucketLike = {
24
+ put(key: string, value: ReadableStream | ArrayBuffer | string, options?: {
25
+ httpMetadata?: {
26
+ contentType?: string;
27
+ };
28
+ }): Promise<unknown>;
29
+ get(key: string): Promise<{
30
+ body: ReadableStream;
31
+ httpMetadata?: {
32
+ contentType?: string;
33
+ };
34
+ } | null>;
35
+ delete(key: string): Promise<void>;
36
+ };
37
+ import type { CapturedCall } from './capture.js';
38
+ export type CaptureRecord = CapturedCall;
39
+ /** Result of a successful write. */
40
+ export type WriteResult = {
41
+ readonly id: string;
42
+ readonly stored_at: string;
43
+ readonly backend: string;
44
+ };
45
+ /** Storage back-end interface. All implementations must be safe to call concurrently. */
46
+ export interface StorageAdapter {
47
+ /** Persist a capture record. Returns the canonical id + storage timestamp. */
48
+ write(record: CaptureRecord): Promise<WriteResult>;
49
+ /** Read back a record by id. Returns null if not found. */
50
+ read(id: string): Promise<CaptureRecord | null>;
51
+ /** List records matching a filter. Used by the freeze-slice tool. */
52
+ list(filter: {
53
+ source_kind?: string;
54
+ since?: string;
55
+ limit?: number;
56
+ }): Promise<ReadonlyArray<CaptureRecord>>;
57
+ /** Identity of the backend (for debugging + audit). */
58
+ readonly name: string;
59
+ }
60
+ /** In-memory adapter. Default for tests; never persisted across restarts. */
61
+ export declare class InMemoryStorage implements StorageAdapter {
62
+ readonly name = "memory";
63
+ private readonly records;
64
+ write(record: CaptureRecord): Promise<WriteResult>;
65
+ read(id: string): Promise<CaptureRecord | null>;
66
+ list(filter: {
67
+ source_kind?: string;
68
+ since?: string;
69
+ limit?: number;
70
+ }): Promise<ReadonlyArray<CaptureRecord>>;
71
+ }
72
+ /** Replace the active storage backend. Returns the previous backend for restoration in tests. */
73
+ export declare function setStorage(adapter: StorageAdapter): StorageAdapter;
74
+ /** Get the active storage backend. */
75
+ export declare function getStorage(): StorageAdapter;
76
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Storage adapter — pluggable back-end for capture records.
3
+ *
4
+ * gatelane's capture SDK is host-agnostic: the same SDK can write to in-memory
5
+ * (tests), filesystem (local dev), or HTTP (production, talking to a Cloudflare
6
+ * Worker that writes to R2/D1).
7
+ *
8
+ * @see docs/prd.md §5.4 — Shared engine
9
+ */
10
+ /** In-memory adapter. Default for tests; never persisted across restarts. */
11
+ export class InMemoryStorage {
12
+ name = 'memory';
13
+ records = new Map();
14
+ async write(record) {
15
+ this.records.set(record.id, record);
16
+ return {
17
+ id: record.id,
18
+ stored_at: new Date().toISOString(),
19
+ backend: this.name,
20
+ };
21
+ }
22
+ async read(id) {
23
+ return this.records.get(id) ?? null;
24
+ }
25
+ async list(filter) {
26
+ let out = Array.from(this.records.values());
27
+ if (filter.source_kind !== undefined) {
28
+ out = out.filter((r) => r.input.metadata?.['source_kind'] === filter.source_kind);
29
+ }
30
+ if (filter.since !== undefined) {
31
+ const since = Date.parse(filter.since);
32
+ out = out.filter((r) => Date.parse(r.started_at) >= since);
33
+ }
34
+ if (filter.limit !== undefined) {
35
+ out = out.slice(0, filter.limit);
36
+ }
37
+ return out;
38
+ }
39
+ }
40
+ /** Active storage. Module-level mutable singleton — initialized once. */
41
+ let activeStorage = new InMemoryStorage();
42
+ /** Replace the active storage backend. Returns the previous backend for restoration in tests. */
43
+ export function setStorage(adapter) {
44
+ const previous = activeStorage;
45
+ activeStorage = adapter;
46
+ return previous;
47
+ }
48
+ /** Get the active storage backend. */
49
+ export function getStorage() {
50
+ return activeStorage;
51
+ }
52
+ //# sourceMappingURL=storage.js.map