@graphrefly/graphrefly 0.20.0 → 0.22.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/README.md +27 -8
- package/dist/chunk-44HD4BTA.js +47 -0
- package/dist/chunk-44HD4BTA.js.map +1 -0
- package/dist/chunk-7TAQJHQV.js +103 -0
- package/dist/chunk-7TAQJHQV.js.map +1 -0
- package/dist/chunk-BLD3IFYF.js +6827 -0
- package/dist/chunk-BLD3IFYF.js.map +1 -0
- package/dist/{chunk-IAPLC4NR.js → chunk-EQUZ5NLD.js} +34 -45
- package/dist/chunk-EQUZ5NLD.js.map +1 -0
- package/dist/{chunk-OOA2UTXF.js → chunk-IR3KMOLX.js} +358 -128
- package/dist/chunk-IR3KMOLX.js.map +1 -0
- package/dist/{chunk-5PSVTDNZ.js → chunk-MQBQOFDS.js} +20 -11
- package/dist/chunk-MQBQOFDS.js.map +1 -0
- package/dist/chunk-NXC35KC5.js +2417 -0
- package/dist/chunk-NXC35KC5.js.map +1 -0
- package/dist/chunk-QA3RP5NH.js +2234 -0
- package/dist/chunk-QA3RP5NH.js.map +1 -0
- package/dist/chunk-RHI3GHZW.js +115 -0
- package/dist/chunk-RHI3GHZW.js.map +1 -0
- package/dist/{chunk-2L5J6RPM.js → chunk-TH6COGOP.js} +15 -26
- package/dist/chunk-TH6COGOP.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +3366 -2259
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +6 -4
- package/dist/compat/nestjs/index.d.ts +6 -4
- package/dist/compat/nestjs/index.js +8 -8
- package/dist/core/index.cjs +1611 -1218
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3 -2
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +37 -34
- package/dist/extra/index.cjs +7726 -6470
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +4 -4
- package/dist/extra/index.d.ts +4 -4
- package/dist/extra/index.js +57 -30
- package/dist/graph/index.cjs +3107 -2216
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +5 -3
- package/dist/graph/index.d.ts +5 -3
- package/dist/graph/index.js +24 -11
- package/dist/graph-DFr0diXB.d.ts +1128 -0
- package/dist/graph-ab1yPwIB.d.cts +1128 -0
- package/dist/{index-8a605sg9.d.ts → index-BHm3Ba5q.d.ts} +2 -2
- package/dist/{index-SFzE_KTa.d.cts → index-BbYZma8G.d.ts} +1697 -586
- package/dist/{index-DuN3bhtm.d.ts → index-BvWfZCTt.d.cts} +1697 -586
- package/dist/index-C9z6rU9P.d.cts +388 -0
- package/dist/{index-BjtlNirP.d.cts → index-D36MAQ3f.d.ts} +4 -4
- package/dist/{index-VHA43cGP.d.cts → index-DLE1Sp-L.d.cts} +2 -2
- package/dist/{index-CgSiUouz.d.ts → index-DrJq9B1T.d.cts} +4 -4
- package/dist/index-DsGxLfwL.d.ts +315 -0
- package/dist/index-Dy04P4W3.d.cts +315 -0
- package/dist/index-HdJx_BjO.d.ts +388 -0
- package/dist/index.cjs +9919 -7900
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +415 -42
- package/dist/index.d.ts +415 -42
- package/dist/index.js +1064 -639
- package/dist/index.js.map +1 -1
- package/dist/meta--fr9sxRM.d.cts +41 -0
- package/dist/meta-n3FoVWML.d.ts +41 -0
- package/dist/node-C5UD5MGq.d.cts +1146 -0
- package/dist/node-C5UD5MGq.d.ts +1146 -0
- package/dist/{observable-DcBwQY7t.d.ts → observable-CQRBtEbq.d.ts} +1 -1
- package/dist/{observable-C8Kx_O6k.d.cts → observable-DWydVy5b.d.cts} +1 -1
- package/dist/patterns/reactive-layout/index.cjs +3102 -2132
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +5 -3
- package/dist/patterns/reactive-layout/index.d.ts +5 -3
- package/dist/patterns/reactive-layout/index.js +5 -4
- package/dist/storage-Bew05Xy6.d.cts +182 -0
- package/dist/storage-C9fZfMfM.d.ts +182 -0
- package/package.json +2 -1
- package/dist/chunk-2L5J6RPM.js.map +0 -1
- package/dist/chunk-3N2Y6PCR.js +0 -2117
- package/dist/chunk-3N2Y6PCR.js.map +0 -1
- package/dist/chunk-5PSVTDNZ.js.map +0 -1
- package/dist/chunk-BJAOEU4D.js +0 -6269
- package/dist/chunk-BJAOEU4D.js.map +0 -1
- package/dist/chunk-IAPLC4NR.js.map +0 -1
- package/dist/chunk-OOA2UTXF.js.map +0 -1
- package/dist/chunk-PGEU5MEH.js +0 -162
- package/dist/chunk-PGEU5MEH.js.map +0 -1
- package/dist/chunk-R2LPZIY2.js +0 -111
- package/dist/chunk-R2LPZIY2.js.map +0 -1
- package/dist/chunk-WZ2Z2CRV.js +0 -32
- package/dist/chunk-WZ2Z2CRV.js.map +0 -1
- package/dist/chunk-XYL3GLB3.js +0 -1631
- package/dist/chunk-XYL3GLB3.js.map +0 -1
- package/dist/graph-KsTe57nI.d.cts +0 -750
- package/dist/graph-mILUUqW8.d.ts +0 -750
- package/dist/index-B2SvPEbc.d.ts +0 -257
- package/dist/index-BHfg_Ez3.d.ts +0 -629
- package/dist/index-Bc_diYYJ.d.cts +0 -629
- package/dist/index-UudxGnzc.d.cts +0 -257
- package/dist/meta-BnG7XAaE.d.cts +0 -778
- package/dist/meta-BnG7XAaE.d.ts +0 -778
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# GraphReFly
|
|
2
2
|
|
|
3
|
-
**Describe
|
|
3
|
+
**The reactive harness layer for agent workflows.** Describe in plain language, review visually, run persistently, trace every decision.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
GraphReFly makes long-running human + LLM co-operation reactive, resumable, and causally explainable. State pushes downstream on change (no re-reading), nodes have lifecycles (not infinite append), and every decision has a traceable causal chain — the substrate underneath tools, agents, and personal automations.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@graphrefly/graphrefly)
|
|
8
8
|
[](./LICENSE)
|
|
9
9
|
|
|
10
|
-
[Docs](https://graphrefly.dev) | [Spec](https://graphrefly.dev/spec/) | [Python API](https://graphrefly.dev/py/api/) | [API Reference](https://graphrefly.dev/api/node/)
|
|
10
|
+
[Docs](https://graphrefly.dev) | [Spec](https://graphrefly.dev/spec/) | [Python API](https://graphrefly.dev/py/api/) | [TS API Reference](https://graphrefly.dev/api/node/)
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -46,6 +46,23 @@ count.set(3);
|
|
|
46
46
|
|
|
47
47
|
You describe what you need — an LLM composes a reactive graph (like SQL for data flows). The graph runs persistently, checkpoints its state, and traces every decision through a causal chain. Ask "why?" at any point and get a human-readable explanation from source to conclusion.
|
|
48
48
|
|
|
49
|
+
## Harness engineering coverage
|
|
50
|
+
|
|
51
|
+
The eight requirements of a production agent harness cluster into a handful of composed blocks that sit on top of the reactive graph primitives:
|
|
52
|
+
|
|
53
|
+
| Need | GraphReFly |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Context & state | `persistentState()` — `autoCheckpoint` + `snapshot` / `restore` + incremental diff |
|
|
56
|
+
| Agent memory | `agentMemory()` — `distill` + vectors + knowledge graph + tiers, OpenViking decay |
|
|
57
|
+
| Control flow & resilience | `resilientPipeline()` — `rateLimiter → breaker → retry → timeout → fallback`, correct ordering built in |
|
|
58
|
+
| Execution & policy | `guardedExecution()` — Actor / Guard ABAC + `policy()` + `budgetGate` + scoped describe |
|
|
59
|
+
| Observability & causality | `graphLens()` — reactive topology, health, flow, and `why(node)` causal chains as structured data |
|
|
60
|
+
| Human governance | `gate` — reactive `pending` / `isOpen` with `approve` / `reject` / `modify(fn, n)` |
|
|
61
|
+
| Verification | Multi-model eval harness with regression gates |
|
|
62
|
+
| Continuous improvement | Strategy model: `rootCause × intervention → successRate` |
|
|
63
|
+
|
|
64
|
+
The library computes structured facts reactively; LLMs and UIs render them. Natural language is never the library's job — which keeps the whole stack model-agnostic and testable.
|
|
65
|
+
|
|
49
66
|
## Why GraphReFly?
|
|
50
67
|
|
|
51
68
|
| | Zustand / Jotai | RxJS | XState | LangGraph | TC39 Signals | **GraphReFly** |
|
|
@@ -183,10 +200,10 @@ import { node, map, Graph } from "@graphrefly/graphrefly";
|
|
|
183
200
|
|
|
184
201
|
## Resilience & checkpoints
|
|
185
202
|
|
|
186
|
-
Built-in retry, circuit breakers, rate limiters, and persistent
|
|
203
|
+
Built-in retry, circuit breakers, rate limiters, and persistent storage:
|
|
187
204
|
|
|
188
205
|
```ts
|
|
189
|
-
import { retry, circuitBreaker,
|
|
206
|
+
import { retry, circuitBreaker, fileStorage, memoryStorage } from "@graphrefly/graphrefly";
|
|
190
207
|
|
|
191
208
|
// Retry with exponential backoff
|
|
192
209
|
const resilient = pipe(source, retry({ strategy: "exponential" }));
|
|
@@ -194,9 +211,11 @@ const resilient = pipe(source, retry({ strategy: "exponential" }));
|
|
|
194
211
|
// Circuit breaker
|
|
195
212
|
const breaker = circuitBreaker({ threshold: 5, resetTimeout: 30_000 });
|
|
196
213
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
214
|
+
// Multi-tier storage with auto-restore: hot in-memory + warm on disk.
|
|
215
|
+
graph.attachStorage(
|
|
216
|
+
[memoryStorage(), fileStorage("./checkpoints")],
|
|
217
|
+
{ autoRestore: true },
|
|
218
|
+
);
|
|
200
219
|
```
|
|
201
220
|
|
|
202
221
|
## Project layout
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GRAPH_META_SEGMENT,
|
|
3
|
+
Graph,
|
|
4
|
+
OVERHEAD,
|
|
5
|
+
SIZEOF_SYMBOL,
|
|
6
|
+
diffForWAL,
|
|
7
|
+
graphProfile,
|
|
8
|
+
reachable,
|
|
9
|
+
sizeof
|
|
10
|
+
} from "./chunk-NXC35KC5.js";
|
|
11
|
+
import {
|
|
12
|
+
ENVELOPE_VERSION,
|
|
13
|
+
JsonCodec,
|
|
14
|
+
__export,
|
|
15
|
+
createDagCborCodec,
|
|
16
|
+
createDagCborZstdCodec,
|
|
17
|
+
decodeEnvelope,
|
|
18
|
+
encodeEnvelope,
|
|
19
|
+
registerBuiltinCodecs,
|
|
20
|
+
replayWAL
|
|
21
|
+
} from "./chunk-QA3RP5NH.js";
|
|
22
|
+
|
|
23
|
+
// src/graph/index.ts
|
|
24
|
+
var graph_exports = {};
|
|
25
|
+
__export(graph_exports, {
|
|
26
|
+
ENVELOPE_VERSION: () => ENVELOPE_VERSION,
|
|
27
|
+
GRAPH_META_SEGMENT: () => GRAPH_META_SEGMENT,
|
|
28
|
+
Graph: () => Graph,
|
|
29
|
+
JsonCodec: () => JsonCodec,
|
|
30
|
+
SIZEOF_OVERHEAD: () => OVERHEAD,
|
|
31
|
+
SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
|
|
32
|
+
createDagCborCodec: () => createDagCborCodec,
|
|
33
|
+
createDagCborZstdCodec: () => createDagCborZstdCodec,
|
|
34
|
+
decodeEnvelope: () => decodeEnvelope,
|
|
35
|
+
diffForWAL: () => diffForWAL,
|
|
36
|
+
encodeEnvelope: () => encodeEnvelope,
|
|
37
|
+
graphProfile: () => graphProfile,
|
|
38
|
+
reachable: () => reachable,
|
|
39
|
+
registerBuiltinCodecs: () => registerBuiltinCodecs,
|
|
40
|
+
replayWAL: () => replayWAL,
|
|
41
|
+
sizeof: () => sizeof
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
graph_exports
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=chunk-44HD4BTA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graph/index.ts"],"sourcesContent":["/**\n * Graph container: registry, wiring, introspection (Phase 1).\n */\n\nexport { OVERHEAD as SIZEOF_OVERHEAD, SIZEOF_SYMBOL, sizeof } from \"../extra/utils/sizeof.js\";\nexport {\n\tcreateDagCborCodec,\n\tcreateDagCborZstdCodec,\n\tdecodeEnvelope,\n\tENVELOPE_VERSION,\n\ttype EvictedSubgraphInfo,\n\ttype EvictionPolicy,\n\tencodeEnvelope,\n\ttype GraphCodec,\n\tJsonCodec,\n\ttype LazyGraphCodec,\n\tregisterBuiltinCodecs,\n\treplayWAL,\n\ttype WALEntry,\n} from \"./codec.js\";\nexport {\n\ttype DescribeFilter,\n\tdiffForWAL,\n\tGRAPH_META_SEGMENT,\n\tGraph,\n\ttype GraphActorOptions,\n\ttype GraphAttachStorageOptions,\n\ttype GraphCheckpointRecord,\n\ttype GraphDescribeOptions,\n\ttype GraphDescribeOutput,\n\ttype GraphDiagramDirection,\n\ttype GraphDiagramOptions,\n\ttype GraphDiffChange,\n\ttype GraphDiffResult,\n\ttype GraphFactoryContext,\n\ttype GraphNodeFactory,\n\ttype GraphObserveAll,\n\ttype GraphObserveOne,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype GraphVersionChange,\n\ttype GraphWALDiff,\n\ttype ObserveDetail,\n\ttype ObserveEvent,\n\ttype ObserveOptions,\n\ttype ObserveResult,\n\ttype ObserveTheme,\n\ttype ObserveThemeName,\n\ttype ReachableDirection,\n\ttype ReachableOptions,\n\treachable,\n\ttype TraceEntry,\n} from \"./graph.js\";\nexport {\n\ttype GraphProfileOptions,\n\ttype GraphProfileResult,\n\tgraphProfile,\n\ttype NodeProfile,\n} from \"./profile.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// src/extra/timer.ts
|
|
2
|
+
var ResettableTimer = class {
|
|
3
|
+
_timer;
|
|
4
|
+
_gen = 0;
|
|
5
|
+
/** Schedule callback after delayMs. Cancels any pending timer. */
|
|
6
|
+
start(delayMs, callback) {
|
|
7
|
+
this.cancel();
|
|
8
|
+
this._gen += 1;
|
|
9
|
+
const gen = this._gen;
|
|
10
|
+
this._timer = setTimeout(() => {
|
|
11
|
+
this._timer = void 0;
|
|
12
|
+
if (gen !== this._gen) return;
|
|
13
|
+
callback();
|
|
14
|
+
}, delayMs);
|
|
15
|
+
}
|
|
16
|
+
/** Cancel the pending timer (if any). */
|
|
17
|
+
cancel() {
|
|
18
|
+
if (this._timer !== void 0) {
|
|
19
|
+
clearTimeout(this._timer);
|
|
20
|
+
this._timer = void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Whether a timer is currently pending. */
|
|
24
|
+
get pending() {
|
|
25
|
+
return this._timer !== void 0;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/extra/utils/ring-buffer.ts
|
|
30
|
+
var RingBuffer = class {
|
|
31
|
+
constructor(capacity) {
|
|
32
|
+
this.capacity = capacity;
|
|
33
|
+
if (!Number.isInteger(capacity) || capacity <= 0) {
|
|
34
|
+
throw new Error(`RingBuffer capacity must be a positive integer (got ${capacity})`);
|
|
35
|
+
}
|
|
36
|
+
this.buf = new Array(capacity);
|
|
37
|
+
}
|
|
38
|
+
buf;
|
|
39
|
+
head = 0;
|
|
40
|
+
_size = 0;
|
|
41
|
+
/** Current number of stored entries. */
|
|
42
|
+
get size() {
|
|
43
|
+
return this._size;
|
|
44
|
+
}
|
|
45
|
+
/** Configured maximum before drop-oldest eviction fires. */
|
|
46
|
+
get maxSize() {
|
|
47
|
+
return this.capacity;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Append an item. If size equals capacity, drops the oldest entry and
|
|
51
|
+
* advances the head pointer.
|
|
52
|
+
*/
|
|
53
|
+
push(item) {
|
|
54
|
+
const idx = (this.head + this._size) % this.capacity;
|
|
55
|
+
this.buf[idx] = item;
|
|
56
|
+
if (this._size < this.capacity) this._size++;
|
|
57
|
+
else this.head = (this.head + 1) % this.capacity;
|
|
58
|
+
}
|
|
59
|
+
/** Remove and return the oldest entry; `undefined` when empty. */
|
|
60
|
+
shift() {
|
|
61
|
+
if (this._size === 0) return void 0;
|
|
62
|
+
const item = this.buf[this.head];
|
|
63
|
+
this.buf[this.head] = void 0;
|
|
64
|
+
this.head = (this.head + 1) % this.capacity;
|
|
65
|
+
this._size--;
|
|
66
|
+
return item;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* O(1) index lookup. Negative indices count from the tail (Python-style).
|
|
70
|
+
* Returns `undefined` for out-of-range.
|
|
71
|
+
*/
|
|
72
|
+
at(i) {
|
|
73
|
+
if (this._size === 0) return void 0;
|
|
74
|
+
const n = i < 0 ? this._size + i : i;
|
|
75
|
+
if (n < 0 || n >= this._size) return void 0;
|
|
76
|
+
return this.buf[(this.head + n) % this.capacity];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Materialize the contents in insertion order (oldest → newest).
|
|
80
|
+
* Returns a new array each call.
|
|
81
|
+
*/
|
|
82
|
+
toArray() {
|
|
83
|
+
const result = new Array(this._size);
|
|
84
|
+
for (let i = 0; i < this._size; i++) {
|
|
85
|
+
result[i] = this.buf[(this.head + i) % this.capacity];
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/** Reset to empty. Storage slots are released so held refs can GC. */
|
|
90
|
+
clear() {
|
|
91
|
+
for (let i = 0; i < this._size; i++) {
|
|
92
|
+
this.buf[(this.head + i) % this.capacity] = void 0;
|
|
93
|
+
}
|
|
94
|
+
this.head = 0;
|
|
95
|
+
this._size = 0;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
ResettableTimer,
|
|
101
|
+
RingBuffer
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=chunk-7TAQJHQV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/extra/timer.ts","../src/extra/utils/ring-buffer.ts"],"sourcesContent":["/**\n * Creates a resettable deadline timer for internal timeout, retry, and rate-limiting use.\n *\n * @remarks **Centralised primitive:** wraps `setTimeout`/`clearTimeout` with a generation guard\n * so that stale callbacks never fire after `cancel()` or a new `start()`.\n *\n * @remarks **Spec §5.10 exception:** resilience operators (`timeout`, `retry`, `rateLimiter`)\n * need raw timers — `fromTimer` creates a new Node per reset, which is too heavy here.\n * Lives in `src/extra/` (not `src/core/`) because it is a documented escape hatch from\n * the protocol-pure core layer.\n *\n * @example\n * ```ts\n * import { ResettableTimer } from \"@graphrefly/graphrefly-ts\";\n *\n * const timer = new ResettableTimer();\n * timer.start(1000, () => console.log(\"fired\"));\n * timer.cancel(); // cancels before firing\n * timer.start(500, () => console.log(\"new deadline\"));\n * console.log(timer.pending); // true\n * ```\n *\n * @category extra\n */\nexport class ResettableTimer {\n\tprivate _timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate _gen = 0;\n\n\t/** Schedule callback after delayMs. Cancels any pending timer. */\n\tstart(delayMs: number, callback: () => void): void {\n\t\tthis.cancel();\n\t\tthis._gen += 1;\n\t\tconst gen = this._gen;\n\t\tthis._timer = setTimeout(() => {\n\t\t\tthis._timer = undefined;\n\t\t\tif (gen !== this._gen) return;\n\t\t\tcallback();\n\t\t}, delayMs);\n\t}\n\n\t/** Cancel the pending timer (if any). */\n\tcancel(): void {\n\t\tif (this._timer !== undefined) {\n\t\t\tclearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/** Whether a timer is currently pending. */\n\tget pending(): boolean {\n\t\treturn this._timer !== undefined;\n\t}\n}\n","/**\n * Fixed-capacity ring buffer — O(1) push and drop-oldest eviction.\n *\n * Used by `Graph._traceRing` (reasoning trace), `reactiveLog` (append-only\n * log backend), and `reactiveSink` (drop-oldest backpressure buffer). One\n * implementation, three use sites.\n *\n * @module\n * @internal\n */\n\n/**\n * Fixed-capacity ring buffer. Once `capacity` entries are stored, subsequent\n * `push` calls evict the oldest entry (drop-oldest / FIFO eviction).\n *\n * Operations:\n * - `push(item)` — O(1).\n * - `shift()` — O(1) remove oldest (returns `undefined` when empty).\n * - `at(i)` — O(1) index lookup, with Python-style negative indexing.\n * - `toArray()` — O(n) materialize in insertion order.\n * - `clear()` — O(1) reset to empty.\n *\n * Not thread-safe; JS semantics assumed (single-threaded within a sync call).\n */\nexport class RingBuffer<T> {\n\tprivate buf: (T | undefined)[];\n\tprivate head = 0;\n\tprivate _size = 0;\n\n\tconstructor(private capacity: number) {\n\t\tif (!Number.isInteger(capacity) || capacity <= 0) {\n\t\t\tthrow new Error(`RingBuffer capacity must be a positive integer (got ${capacity})`);\n\t\t}\n\t\tthis.buf = new Array(capacity);\n\t}\n\n\t/** Current number of stored entries. */\n\tget size(): number {\n\t\treturn this._size;\n\t}\n\n\t/** Configured maximum before drop-oldest eviction fires. */\n\tget maxSize(): number {\n\t\treturn this.capacity;\n\t}\n\n\t/**\n\t * Append an item. If size equals capacity, drops the oldest entry and\n\t * advances the head pointer.\n\t */\n\tpush(item: T): void {\n\t\tconst idx = (this.head + this._size) % this.capacity;\n\t\tthis.buf[idx] = item;\n\t\tif (this._size < this.capacity) this._size++;\n\t\telse this.head = (this.head + 1) % this.capacity;\n\t}\n\n\t/** Remove and return the oldest entry; `undefined` when empty. */\n\tshift(): T | undefined {\n\t\tif (this._size === 0) return undefined;\n\t\tconst item = this.buf[this.head];\n\t\tthis.buf[this.head] = undefined;\n\t\tthis.head = (this.head + 1) % this.capacity;\n\t\tthis._size--;\n\t\treturn item;\n\t}\n\n\t/**\n\t * O(1) index lookup. Negative indices count from the tail (Python-style).\n\t * Returns `undefined` for out-of-range.\n\t */\n\tat(i: number): T | undefined {\n\t\tif (this._size === 0) return undefined;\n\t\tconst n = i < 0 ? this._size + i : i;\n\t\tif (n < 0 || n >= this._size) return undefined;\n\t\treturn this.buf[(this.head + n) % this.capacity];\n\t}\n\n\t/**\n\t * Materialize the contents in insertion order (oldest → newest).\n\t * Returns a new array each call.\n\t */\n\ttoArray(): T[] {\n\t\tconst result: T[] = new Array(this._size);\n\t\tfor (let i = 0; i < this._size; i++) {\n\t\t\tresult[i] = this.buf[(this.head + i) % this.capacity]!;\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Reset to empty. Storage slots are released so held refs can GC. */\n\tclear(): void {\n\t\tfor (let i = 0; i < this._size; i++) {\n\t\t\tthis.buf[(this.head + i) % this.capacity] = undefined;\n\t\t}\n\t\tthis.head = 0;\n\t\tthis._size = 0;\n\t}\n}\n"],"mappings":";AAwBO,IAAM,kBAAN,MAAsB;AAAA,EACpB;AAAA,EACA,OAAO;AAAA;AAAA,EAGf,MAAM,SAAiB,UAA4B;AAClD,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,UAAM,MAAM,KAAK;AACjB,SAAK,SAAS,WAAW,MAAM;AAC9B,WAAK,SAAS;AACd,UAAI,QAAQ,KAAK,KAAM;AACvB,eAAS;AAAA,IACV,GAAG,OAAO;AAAA,EACX;AAAA;AAAA,EAGA,SAAe;AACd,QAAI,KAAK,WAAW,QAAW;AAC9B,mBAAa,KAAK,MAAM;AACxB,WAAK,SAAS;AAAA,IACf;AAAA,EACD;AAAA;AAAA,EAGA,IAAI,UAAmB;AACtB,WAAO,KAAK,WAAW;AAAA,EACxB;AACD;;;AC5BO,IAAM,aAAN,MAAoB;AAAA,EAK1B,YAAoB,UAAkB;AAAlB;AACnB,QAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,YAAY,GAAG;AACjD,YAAM,IAAI,MAAM,uDAAuD,QAAQ,GAAG;AAAA,IACnF;AACA,SAAK,MAAM,IAAI,MAAM,QAAQ;AAAA,EAC9B;AAAA,EATQ;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA;AAAA,EAUhB,IAAI,OAAe;AAClB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA,EAGA,IAAI,UAAkB;AACrB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,MAAe;AACnB,UAAM,OAAO,KAAK,OAAO,KAAK,SAAS,KAAK;AAC5C,SAAK,IAAI,GAAG,IAAI;AAChB,QAAI,KAAK,QAAQ,KAAK,SAAU,MAAK;AAAA,QAChC,MAAK,QAAQ,KAAK,OAAO,KAAK,KAAK;AAAA,EACzC;AAAA;AAAA,EAGA,QAAuB;AACtB,QAAI,KAAK,UAAU,EAAG,QAAO;AAC7B,UAAM,OAAO,KAAK,IAAI,KAAK,IAAI;AAC/B,SAAK,IAAI,KAAK,IAAI,IAAI;AACtB,SAAK,QAAQ,KAAK,OAAO,KAAK,KAAK;AACnC,SAAK;AACL,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,GAAG,GAA0B;AAC5B,QAAI,KAAK,UAAU,EAAG,QAAO;AAC7B,UAAM,IAAI,IAAI,IAAI,KAAK,QAAQ,IAAI;AACnC,QAAI,IAAI,KAAK,KAAK,KAAK,MAAO,QAAO;AACrC,WAAO,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAe;AACd,UAAM,SAAc,IAAI,MAAM,KAAK,KAAK;AACxC,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,KAAK;AACpC,aAAO,CAAC,IAAI,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ;AAAA,IACrD;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAGA,QAAc;AACb,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,KAAK;AACpC,WAAK,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,IAAI;AAAA,IAC7C;AACA,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACd;AACD;","names":[]}
|