@telorun/kernel 0.62.0 → 0.64.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.
Files changed (116) hide show
  1. package/dist/boot-context-registry.js +1 -3
  2. package/dist/boot-context-registry.js.map +1 -1
  3. package/dist/bundle/module-artifact.js +15 -8
  4. package/dist/bundle/module-artifact.js.map +1 -1
  5. package/dist/controller-loader.d.ts +4 -0
  6. package/dist/controller-loader.d.ts.map +1 -1
  7. package/dist/controller-loader.js +5 -1
  8. package/dist/controller-loader.js.map +1 -1
  9. package/dist/controller-loaders/bundle-loader.d.ts +21 -0
  10. package/dist/controller-loaders/bundle-loader.d.ts.map +1 -1
  11. package/dist/controller-loaders/bundle-loader.js +98 -17
  12. package/dist/controller-loaders/bundle-loader.js.map +1 -1
  13. package/dist/controller-loaders/napi-loader.d.ts.map +1 -1
  14. package/dist/controller-loaders/napi-loader.js +24 -8
  15. package/dist/controller-loaders/napi-loader.js.map +1 -1
  16. package/dist/controller-loaders/npm-loader.js +37 -28
  17. package/dist/controller-loaders/npm-loader.js.map +1 -1
  18. package/dist/controller-loaders/source-bundle-builder.d.ts +30 -0
  19. package/dist/controller-loaders/source-bundle-builder.d.ts.map +1 -0
  20. package/dist/controller-loaders/source-bundle-builder.js +302 -0
  21. package/dist/controller-loaders/source-bundle-builder.js.map +1 -0
  22. package/dist/controller-registry.js +3 -5
  23. package/dist/controller-registry.js.map +1 -1
  24. package/dist/controllers/resource-definition/abstract-controller.js +2 -1
  25. package/dist/controllers/resource-definition/abstract-controller.js.map +1 -1
  26. package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
  27. package/dist/controllers/resource-definition/resource-definition-controller.js +11 -3
  28. package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
  29. package/dist/controllers/type/json-schema-controller.js +3 -0
  30. package/dist/controllers/type/json-schema-controller.js.map +1 -1
  31. package/dist/directory-lock.js +5 -5
  32. package/dist/directory-lock.js.map +1 -1
  33. package/dist/evaluation-context.js +60 -21
  34. package/dist/evaluation-context.js.map +1 -1
  35. package/dist/events.js +1 -3
  36. package/dist/events.js.map +1 -1
  37. package/dist/host-env.js +3 -2
  38. package/dist/host-env.js.map +1 -1
  39. package/dist/index.d.ts +1 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/kernel.d.ts +9 -0
  44. package/dist/kernel.d.ts.map +1 -1
  45. package/dist/kernel.js +64 -26
  46. package/dist/kernel.js.map +1 -1
  47. package/dist/logging/console-sink.js +18 -29
  48. package/dist/logging/console-sink.js.map +1 -1
  49. package/dist/logging/debug-wire-sink.js +11 -23
  50. package/dist/logging/debug-wire-sink.js.map +1 -1
  51. package/dist/logging/drop-accounting.js +28 -41
  52. package/dist/logging/drop-accounting.js.map +1 -1
  53. package/dist/logging/file-sink.js +29 -39
  54. package/dist/logging/file-sink.js.map +1 -1
  55. package/dist/logging/index.d.ts +1 -1
  56. package/dist/logging/index.d.ts.map +1 -1
  57. package/dist/logging/index.js +1 -1
  58. package/dist/logging/index.js.map +1 -1
  59. package/dist/logging/kernel-logging.js +30 -41
  60. package/dist/logging/kernel-logging.js.map +1 -1
  61. package/dist/logging/log-sink.d.ts +1 -1
  62. package/dist/logging/log-sink.d.ts.map +1 -1
  63. package/dist/logging/log-sink.js +1 -1
  64. package/dist/logging/log-sink.js.map +1 -1
  65. package/dist/logging/logging-pipeline.js +197 -197
  66. package/dist/logging/logging-pipeline.js.map +1 -1
  67. package/dist/logging/sampler.js +21 -34
  68. package/dist/logging/sampler.js.map +1 -1
  69. package/dist/manifest-sources/local-manifest-cache-source.js +2 -0
  70. package/dist/manifest-sources/local-manifest-cache-source.js.map +1 -1
  71. package/dist/manifest-sources/memory-source.js +1 -3
  72. package/dist/manifest-sources/memory-source.js.map +1 -1
  73. package/dist/module-context.js +62 -37
  74. package/dist/module-context.js.map +1 -1
  75. package/dist/registry.js +5 -7
  76. package/dist/registry.js.map +1 -1
  77. package/dist/resource-context.d.ts +11 -1
  78. package/dist/resource-context.d.ts.map +1 -1
  79. package/dist/resource-context.js +44 -23
  80. package/dist/resource-context.js.map +1 -1
  81. package/dist/resource-uri.js +4 -0
  82. package/dist/resource-uri.js.map +1 -1
  83. package/dist/runtime-seam.d.ts +19 -0
  84. package/dist/runtime-seam.d.ts.map +1 -0
  85. package/dist/runtime-seam.js +198 -0
  86. package/dist/runtime-seam.js.map +1 -0
  87. package/dist/schema-validator.js +20 -18
  88. package/dist/schema-validator.js.map +1 -1
  89. package/dist/tracing.js +4 -19
  90. package/dist/tracing.js.map +1 -1
  91. package/dist/transports/oci/oci-client.js +3 -1
  92. package/dist/transports/oci/oci-client.js.map +1 -1
  93. package/dist/transports/oci/oci-transport.js +17 -16
  94. package/dist/transports/oci/oci-transport.js.map +1 -1
  95. package/dist/transports/registry-transport.js +4 -0
  96. package/dist/transports/registry-transport.js.map +1 -1
  97. package/dist/transports/transport-registry.js +1 -0
  98. package/dist/transports/transport-registry.js.map +1 -1
  99. package/package.json +4 -4
  100. package/src/controller-loader.ts +5 -1
  101. package/src/controller-loaders/bundle-loader.ts +108 -23
  102. package/src/controller-loaders/napi-loader.ts +28 -9
  103. package/src/controller-loaders/source-bundle-builder.ts +337 -0
  104. package/src/controllers/resource-definition/resource-definition-controller.ts +23 -17
  105. package/src/index.ts +1 -0
  106. package/src/kernel.ts +13 -0
  107. package/src/logging/file-sink.ts +1 -1
  108. package/src/logging/index.ts +1 -1
  109. package/src/logging/log-sink.ts +1 -0
  110. package/src/resource-context.ts +23 -0
  111. package/src/runtime-seam.ts +230 -0
  112. package/dist/logging/record-buffer.d.ts +0 -24
  113. package/dist/logging/record-buffer.d.ts.map +0 -1
  114. package/dist/logging/record-buffer.js +0 -78
  115. package/dist/logging/record-buffer.js.map +0 -1
  116. package/src/logging/record-buffer.ts +0 -72
@@ -0,0 +1,230 @@
1
+ import {
2
+ Loader,
3
+ StaticAnalyzer,
4
+ flattenForAnalyzer,
5
+ type AnalysisDiagnostic,
6
+ type ManifestSource,
7
+ } from "@telorun/analyzer";
8
+ import {
9
+ Stream,
10
+ type RuntimeCheckOptions,
11
+ type RuntimeCheckResult,
12
+ type CheckDiagnostic,
13
+ type CheckDiagnosticSeverity,
14
+ type RuntimeRun,
15
+ type RuntimeRunOptions,
16
+ type RuntimeSeam,
17
+ } from "@telorun/sdk";
18
+ import { Writable } from "stream";
19
+ import { nodeCelHandlers } from "./cel-handlers.js";
20
+ import type { Kernel } from "./kernel.js";
21
+ import { defaultTransportRegistry } from "./transports/transport-registry.js";
22
+
23
+ /**
24
+ * How many unconsumed chunks a channel holds before it stops acknowledging the
25
+ * child's writes.
26
+ *
27
+ * This is a courtesy, **not a memory bound**, and the distinction matters enough
28
+ * to state: withholding a `Writable`'s `_write` callback only moves subsequent
29
+ * chunks into the stream's own internal buffer, which is unbounded, and a
30
+ * controller writing to `ctx.stdout` does not await `drain`. So a child whose
31
+ * output nobody reads still grows the host's memory. What actually bounds it is
32
+ * `RuntimeRun.cancel()` — stopping the child, rather than hoping it stops
33
+ * producing. What the high-water mark does buy is that a *slow* consumer sees
34
+ * chunks handed over in order without the channel racing ahead of it.
35
+ */
36
+ const CHANNEL_HIGH_WATER_MARK = 256;
37
+
38
+ interface PendingChunk {
39
+ text: string;
40
+ /** The child's `_write` callback, held back once the channel is over its
41
+ * high-water mark and released when a consumer takes the chunk. */
42
+ release?: () => void;
43
+ }
44
+
45
+ /**
46
+ * One direction of a child's output: a `Writable` the child kernel writes to,
47
+ * and the {@link Stream} the caller reads. Bridges Node's stream model to the
48
+ * SDK's without either side seeing the other's — `RuntimeSeam` has no
49
+ * `Writable` in it, and the child kernel has no `Stream` in it.
50
+ */
51
+ class OutputChannel {
52
+ #queue: PendingChunk[] = [];
53
+ #waiters: Array<(result: IteratorResult<string>) => void> = [];
54
+ #ended = false;
55
+
56
+ readonly writable: Writable = new Writable({
57
+ write: (chunk: Buffer | string, _encoding, callback) => {
58
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
59
+ this.#push(text, callback);
60
+ },
61
+ });
62
+
63
+ readonly stream: Stream<string> = new Stream<string>({
64
+ [Symbol.asyncIterator]: () => ({
65
+ next: () => this.#next(),
66
+ }),
67
+ });
68
+
69
+ /** No more output is coming. Wakes every waiter with a terminal result, so a
70
+ * `for await` over the stream completes rather than hanging on a child that
71
+ * has already exited. */
72
+ end(): void {
73
+ this.#ended = true;
74
+ while (this.#waiters.length > 0) {
75
+ this.#waiters.shift()!({ value: undefined, done: true });
76
+ }
77
+ }
78
+
79
+ #push(text: string, callback: () => void): void {
80
+ const waiter = this.#waiters.shift();
81
+ if (waiter) {
82
+ waiter({ value: text, done: false });
83
+ callback();
84
+ return;
85
+ }
86
+ if (this.#queue.length >= CHANNEL_HIGH_WATER_MARK) {
87
+ this.#queue.push({ text, release: callback });
88
+ return;
89
+ }
90
+ this.#queue.push({ text });
91
+ callback();
92
+ }
93
+
94
+ async #next(): Promise<IteratorResult<string>> {
95
+ const pending = this.#queue.shift();
96
+ if (pending) {
97
+ pending.release?.();
98
+ return { value: pending.text, done: false };
99
+ }
100
+ if (this.#ended) return { value: undefined, done: true };
101
+ return new Promise<IteratorResult<string>>((resolve) => this.#waiters.push(resolve));
102
+ }
103
+ }
104
+
105
+ const SEVERITY_NAMES: Record<number, CheckDiagnosticSeverity> = {
106
+ 1: "error",
107
+ 2: "warning",
108
+ 3: "info",
109
+ 4: "hint",
110
+ };
111
+
112
+ /** Flatten an analyzer finding to the seam's plain-data shape. Severity becomes
113
+ * a name because the integer is an LSP protocol detail, and this contract is
114
+ * read by kernels that speak no LSP. An unlabelled severity is an error: the
115
+ * analyzer's own default, and the safe reading for a caller gating on it. */
116
+ function toCheckDiagnostic(diagnostic: AnalysisDiagnostic): CheckDiagnostic {
117
+ return {
118
+ code: String(diagnostic.code ?? ""),
119
+ message: diagnostic.message,
120
+ severity: SEVERITY_NAMES[diagnostic.severity ?? 1] ?? "error",
121
+ source: diagnostic.source,
122
+ line: diagnostic.range?.start?.line,
123
+ column: diagnostic.range?.start?.character,
124
+ };
125
+ }
126
+
127
+ /**
128
+ * The kernel's implementation of the SDK's {@link RuntimeSeam} — run a manifest,
129
+ * analyze a manifest — built over the `Kernel`, `Loader` and `StaticAnalyzer`
130
+ * this kernel already owns.
131
+ *
132
+ * Isolation is this class's choice, not the caller's: today a child `Kernel` in
133
+ * the same process, later a subprocess. That is what keeps process-global host
134
+ * state (the `process.env` guardrail is additive across in-process kernels) an
135
+ * implementation detail rather than something a module author has to know.
136
+ */
137
+ export class KernelRuntimeSeam implements RuntimeSeam {
138
+ constructor(private readonly kernel: Kernel) {}
139
+
140
+ async run(source: string, options?: RuntimeRunOptions): Promise<RuntimeRun> {
141
+ // Import here rather than at module scope: `resource-context.ts` imports this
142
+ // file to construct the seam and `kernel.ts` imports *that*, so a top-level
143
+ // import of `Kernel` here would close the cycle. The dynamic import resolves
144
+ // after both modules are evaluated.
145
+ const { Kernel: ChildKernel } = await import("./kernel.js");
146
+ const stdout = new OutputChannel();
147
+ const stderr = new OutputChannel();
148
+
149
+ const child = new ChildKernel({
150
+ env: options?.env ?? this.kernel.env,
151
+ stdout: stdout.writable,
152
+ stderr: stderr.writable,
153
+ sources: [...this.kernel.injectedSources],
154
+ registryUrl: this.kernel.registryUrl,
155
+ });
156
+
157
+ // A child that fails to load is not an exception on this side: the caller
158
+ // asked to run a manifest and the answer is "it exited non-zero, here is
159
+ // why", which keeps both failure modes on one path. `start()` tears the
160
+ // child down in its own `finally`, so this only has to close the channels.
161
+ const exitCode = (async () => {
162
+ try {
163
+ await child.load(source);
164
+ await child.start();
165
+ return child.exitCode;
166
+ } catch (err) {
167
+ stderr.writable.write(`${err instanceof Error ? err.message : String(err)}\n`);
168
+ return child.exitCode === 0 ? 1 : child.exitCode;
169
+ } finally {
170
+ stdout.end();
171
+ stderr.end();
172
+ }
173
+ })();
174
+
175
+ let cancelled: Promise<void> | undefined;
176
+ const cancel = (reason = "cancelled"): Promise<void> => {
177
+ // Idempotent: repeated calls await the first teardown rather than starting
178
+ // another. `teardown()` is itself idempotent, but a second `forceIdle()`
179
+ // against a kernel already unwinding buys nothing.
180
+ cancelled ??= (async () => {
181
+ // Cancel the boot run first so an in-flight target observes it, then
182
+ // release `waitForIdle` so `start()`'s own `finally` performs the
183
+ // teardown — the same path a SIGINT takes. Awaiting `exitCode` is what
184
+ // makes "resolves when teardown has completed" true.
185
+ child.cancel(reason);
186
+ child.forceIdle();
187
+ await exitCode;
188
+ })();
189
+ return cancelled;
190
+ };
191
+
192
+ return { stdout: stdout.stream, stderr: stderr.stream, exitCode, cancel };
193
+ }
194
+
195
+ async check(source: string, options?: RuntimeCheckOptions): Promise<RuntimeCheckResult> {
196
+ // A fresh loader over this kernel's own resolution chain — the transports it
197
+ // was configured with plus whatever sources were injected — so `check()`
198
+ // resolves an import exactly as a run of the same manifest would. Fresh
199
+ // rather than the kernel's own loader because a checked manifest is often
200
+ // deliberately broken and has no business entering the running kernel's
201
+ // parse cache.
202
+ const loader = new Loader(defaultTransportRegistry(this.kernel.registryUrl).sources(), {
203
+ celHandlers: nodeCelHandlers,
204
+ });
205
+ for (const injected of this.kernel.injectedSources) {
206
+ loader.register(injected as ManifestSource);
207
+ }
208
+
209
+ let manifests;
210
+ try {
211
+ const graph = await loader.loadGraph(source, {
212
+ desugarImports: options?.desugarImports ?? true,
213
+ });
214
+ if (graph.errors.length > 0) throw graph.errors[0].error;
215
+ manifests = flattenForAnalyzer(graph);
216
+ } catch (err) {
217
+ // A graph that would not load is an answer, not a failure of the call —
218
+ // "this manifest does not load, and here is the reason" is precisely what
219
+ // a caller asked for. Throwing would force every caller to re-express the
220
+ // distinction in a catch.
221
+ return {
222
+ diagnostics: [],
223
+ loadError: err instanceof Error ? err.message : String(err),
224
+ };
225
+ }
226
+
227
+ const diagnostics = new StaticAnalyzer({ celHandlers: nodeCelHandlers }).analyze(manifests);
228
+ return { diagnostics: diagnostics.map(toCheckDiagnostic) };
229
+ }
230
+ }
@@ -1,24 +0,0 @@
1
- import type { LogRecord } from "@telorun/sdk";
2
- import type { SinkBufferPolicy } from "./log-sink.js";
3
- /**
4
- * The bounded buffer every asynchronous sink composes — `kernel/specs/logging.md`
5
- * §10.3.
6
- *
7
- * The ecosystems disagree by default: Rust's `tracing-appender` drops, zap
8
- * buffers 256 kB / 30 s, and pino has no bound at all. Telo therefore makes the
9
- * policy explicit and required, and the buffer is never unbounded.
10
- *
11
- * `drop_old` has no precedent among the surveyed libraries, so it is implemented
12
- * as specified — true ring-buffer semantics, evicting the oldest record — rather
13
- * than by analogy to something else.
14
- */
15
- export declare class RecordBuffer {
16
- #private;
17
- constructor(policy: SinkBufferPolicy, onDrop: () => void);
18
- get size(): number;
19
- get isFull(): boolean;
20
- push(record: LogRecord): void;
21
- /** Take everything buffered, leaving the buffer empty. */
22
- drain(): LogRecord[];
23
- }
24
- //# sourceMappingURL=record-buffer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"record-buffer.d.ts","sourceRoot":"","sources":["../../src/logging/record-buffer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,qBAAa,YAAY;;gBASX,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,IAAI;IAMxD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAiB7B,0DAA0D;IAC1D,KAAK,IAAI,SAAS,EAAE;CAerB"}
@@ -1,78 +0,0 @@
1
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
- if (kind === "m") throw new TypeError("Private method is not writable");
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
- };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- var _RecordBuffer_instances, _RecordBuffer_capacity, _RecordBuffer_policy, _RecordBuffer_onDrop, _RecordBuffer_items, _RecordBuffer_head, _RecordBuffer_compact;
13
- /**
14
- * The bounded buffer every asynchronous sink composes — `kernel/specs/logging.md`
15
- * §10.3.
16
- *
17
- * The ecosystems disagree by default: Rust's `tracing-appender` drops, zap
18
- * buffers 256 kB / 30 s, and pino has no bound at all. Telo therefore makes the
19
- * policy explicit and required, and the buffer is never unbounded.
20
- *
21
- * `drop_old` has no precedent among the surveyed libraries, so it is implemented
22
- * as specified — true ring-buffer semantics, evicting the oldest record — rather
23
- * than by analogy to something else.
24
- */
25
- export class RecordBuffer {
26
- constructor(policy, onDrop) {
27
- _RecordBuffer_instances.add(this);
28
- _RecordBuffer_capacity.set(this, void 0);
29
- _RecordBuffer_policy.set(this, void 0);
30
- _RecordBuffer_onDrop.set(this, void 0);
31
- _RecordBuffer_items.set(this, []);
32
- /** Index of the oldest record, so `drop_old` evicts in O(1) instead of
33
- * shifting the whole array on every overflow. */
34
- _RecordBuffer_head.set(this, 0);
35
- __classPrivateFieldSet(this, _RecordBuffer_capacity, Math.max(1, policy.buffer), "f");
36
- __classPrivateFieldSet(this, _RecordBuffer_policy, policy, "f");
37
- __classPrivateFieldSet(this, _RecordBuffer_onDrop, onDrop, "f");
38
- }
39
- get size() {
40
- return __classPrivateFieldGet(this, _RecordBuffer_items, "f").length - __classPrivateFieldGet(this, _RecordBuffer_head, "f");
41
- }
42
- get isFull() {
43
- return this.size >= __classPrivateFieldGet(this, _RecordBuffer_capacity, "f");
44
- }
45
- push(record) {
46
- if (this.isFull) {
47
- if (__classPrivateFieldGet(this, _RecordBuffer_policy, "f").onFull === "drop_old") {
48
- __classPrivateFieldSet(this, _RecordBuffer_head, __classPrivateFieldGet(this, _RecordBuffer_head, "f") + 1, "f");
49
- __classPrivateFieldGet(this, _RecordBuffer_instances, "m", _RecordBuffer_compact).call(this);
50
- }
51
- else {
52
- // `drop_new` — and `block`, which never reaches here because a runtime
53
- // that cannot honour it rejects the manifest at load rather than
54
- // silently degrading to a dropping policy.
55
- __classPrivateFieldGet(this, _RecordBuffer_onDrop, "f").call(this);
56
- return;
57
- }
58
- __classPrivateFieldGet(this, _RecordBuffer_onDrop, "f").call(this);
59
- }
60
- __classPrivateFieldGet(this, _RecordBuffer_items, "f").push(record);
61
- }
62
- /** Take everything buffered, leaving the buffer empty. */
63
- drain() {
64
- const drained = __classPrivateFieldGet(this, _RecordBuffer_head, "f") === 0 ? __classPrivateFieldGet(this, _RecordBuffer_items, "f") : __classPrivateFieldGet(this, _RecordBuffer_items, "f").slice(__classPrivateFieldGet(this, _RecordBuffer_head, "f"));
65
- __classPrivateFieldSet(this, _RecordBuffer_items, [], "f");
66
- __classPrivateFieldSet(this, _RecordBuffer_head, 0, "f");
67
- return drained;
68
- }
69
- }
70
- _RecordBuffer_capacity = new WeakMap(), _RecordBuffer_policy = new WeakMap(), _RecordBuffer_onDrop = new WeakMap(), _RecordBuffer_items = new WeakMap(), _RecordBuffer_head = new WeakMap(), _RecordBuffer_instances = new WeakSet(), _RecordBuffer_compact = function _RecordBuffer_compact() {
71
- // Reclaim the consumed prefix once it dominates the array, so a long-lived
72
- // ring does not grow its backing store without bound.
73
- if (__classPrivateFieldGet(this, _RecordBuffer_head, "f") > 32 && __classPrivateFieldGet(this, _RecordBuffer_head, "f") * 2 >= __classPrivateFieldGet(this, _RecordBuffer_items, "f").length) {
74
- __classPrivateFieldSet(this, _RecordBuffer_items, __classPrivateFieldGet(this, _RecordBuffer_items, "f").slice(__classPrivateFieldGet(this, _RecordBuffer_head, "f")), "f");
75
- __classPrivateFieldSet(this, _RecordBuffer_head, 0, "f");
76
- }
77
- };
78
- //# sourceMappingURL=record-buffer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"record-buffer.js","sourceRoot":"","sources":["../../src/logging/record-buffer.ts"],"names":[],"mappings":";;;;;;;;;;;;AAGA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,YAAY;IASvB,YAAY,MAAwB,EAAE,MAAkB;;QAR/C,yCAAkB;QAClB,uCAA0B;QAC1B,uCAAoB;QAC7B,8BAAsB,EAAE,EAAC;QACzB;0DACkD;QAClD,6BAAQ,CAAC,EAAC;QAGR,uBAAA,IAAI,0BAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,MAAA,CAAC;QAC5C,uBAAA,IAAI,wBAAW,MAAM,MAAA,CAAC;QACtB,uBAAA,IAAI,wBAAW,MAAM,MAAA,CAAC;IACxB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,2BAAO,CAAC,MAAM,GAAG,uBAAA,IAAI,0BAAM,CAAC;IACzC,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,IAAI,IAAI,uBAAA,IAAI,8BAAU,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,MAAiB;QACpB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,uBAAA,IAAI,4BAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBACvC,yGAAc,CAAC,MAAA,CAAC;gBAChB,uBAAA,IAAI,sDAAS,MAAb,IAAI,CAAW,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,uEAAuE;gBACvE,iEAAiE;gBACjE,2CAA2C;gBAC3C,uBAAA,IAAI,4BAAQ,MAAZ,IAAI,CAAU,CAAC;gBACf,OAAO;YACT,CAAC;YACD,uBAAA,IAAI,4BAAQ,MAAZ,IAAI,CAAU,CAAC;QACjB,CAAC;QACD,uBAAA,IAAI,2BAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,0DAA0D;IAC1D,KAAK;QACH,MAAM,OAAO,GAAG,uBAAA,IAAI,0BAAM,KAAK,CAAC,CAAC,CAAC,CAAC,uBAAA,IAAI,2BAAO,CAAC,CAAC,CAAC,uBAAA,IAAI,2BAAO,CAAC,KAAK,CAAC,uBAAA,IAAI,0BAAM,CAAC,CAAC;QAC/E,uBAAA,IAAI,uBAAU,EAAE,MAAA,CAAC;QACjB,uBAAA,IAAI,sBAAS,CAAC,MAAA,CAAC;QACf,OAAO,OAAO,CAAC;IACjB,CAAC;CAUF;;IAPG,2EAA2E;IAC3E,sDAAsD;IACtD,IAAI,uBAAA,IAAI,0BAAM,GAAG,EAAE,IAAI,uBAAA,IAAI,0BAAM,GAAG,CAAC,IAAI,uBAAA,IAAI,2BAAO,CAAC,MAAM,EAAE,CAAC;QAC5D,uBAAA,IAAI,uBAAU,uBAAA,IAAI,2BAAO,CAAC,KAAK,CAAC,uBAAA,IAAI,0BAAM,CAAC,MAAA,CAAC;QAC5C,uBAAA,IAAI,sBAAS,CAAC,MAAA,CAAC;IACjB,CAAC;AACH,CAAC"}
@@ -1,72 +0,0 @@
1
- import type { LogRecord } from "@telorun/sdk";
2
- import type { SinkBufferPolicy } from "./log-sink.js";
3
-
4
- /**
5
- * The bounded buffer every asynchronous sink composes — `kernel/specs/logging.md`
6
- * §10.3.
7
- *
8
- * The ecosystems disagree by default: Rust's `tracing-appender` drops, zap
9
- * buffers 256 kB / 30 s, and pino has no bound at all. Telo therefore makes the
10
- * policy explicit and required, and the buffer is never unbounded.
11
- *
12
- * `drop_old` has no precedent among the surveyed libraries, so it is implemented
13
- * as specified — true ring-buffer semantics, evicting the oldest record — rather
14
- * than by analogy to something else.
15
- */
16
- export class RecordBuffer {
17
- readonly #capacity: number;
18
- readonly #policy: SinkBufferPolicy;
19
- readonly #onDrop: () => void;
20
- #items: LogRecord[] = [];
21
- /** Index of the oldest record, so `drop_old` evicts in O(1) instead of
22
- * shifting the whole array on every overflow. */
23
- #head = 0;
24
-
25
- constructor(policy: SinkBufferPolicy, onDrop: () => void) {
26
- this.#capacity = Math.max(1, policy.buffer);
27
- this.#policy = policy;
28
- this.#onDrop = onDrop;
29
- }
30
-
31
- get size(): number {
32
- return this.#items.length - this.#head;
33
- }
34
-
35
- get isFull(): boolean {
36
- return this.size >= this.#capacity;
37
- }
38
-
39
- push(record: LogRecord): void {
40
- if (this.isFull) {
41
- if (this.#policy.onFull === "drop_old") {
42
- this.#head += 1;
43
- this.#compact();
44
- } else {
45
- // `drop_new` — and `block`, which never reaches here because a runtime
46
- // that cannot honour it rejects the manifest at load rather than
47
- // silently degrading to a dropping policy.
48
- this.#onDrop();
49
- return;
50
- }
51
- this.#onDrop();
52
- }
53
- this.#items.push(record);
54
- }
55
-
56
- /** Take everything buffered, leaving the buffer empty. */
57
- drain(): LogRecord[] {
58
- const drained = this.#head === 0 ? this.#items : this.#items.slice(this.#head);
59
- this.#items = [];
60
- this.#head = 0;
61
- return drained;
62
- }
63
-
64
- #compact(): void {
65
- // Reclaim the consumed prefix once it dominates the array, so a long-lived
66
- // ring does not grow its backing store without bound.
67
- if (this.#head > 32 && this.#head * 2 >= this.#items.length) {
68
- this.#items = this.#items.slice(this.#head);
69
- this.#head = 0;
70
- }
71
- }
72
- }