better-effect 0.11.0 → 0.12.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 (67) hide show
  1. package/README.md +220 -6
  2. package/dist/adapters/iti.d.mts +2 -1
  3. package/dist/adapters/iti.d.mts.map +1 -1
  4. package/dist/adapters/iti.mjs +11 -10
  5. package/dist/adapters/iti.mjs.map +1 -1
  6. package/dist/{effect-2ZcGZI8A.mjs → effect-CK4YZaX0.mjs} +56 -10
  7. package/dist/effect-CK4YZaX0.mjs.map +1 -0
  8. package/dist/hono.d.mts +3 -3
  9. package/dist/hono.mjs +3 -3
  10. package/dist/{index-DStz0JMN.d.mts → index-B1nH4luq.d.mts} +73 -6
  11. package/dist/index-B1nH4luq.d.mts.map +1 -0
  12. package/dist/index-BMdU_Z72.d.mts +18 -0
  13. package/dist/index-BMdU_Z72.d.mts.map +1 -0
  14. package/dist/{index-C7Qild0_.d.mts → index-CX6iAoCB.d.mts} +15 -151
  15. package/dist/index-CX6iAoCB.d.mts.map +1 -0
  16. package/dist/index-GYJ4qm9K.d.mts +179 -0
  17. package/dist/index-GYJ4qm9K.d.mts.map +1 -0
  18. package/dist/index-heuaRmXR.d.mts.map +1 -1
  19. package/dist/index.d.mts +5 -3
  20. package/dist/index.mjs +6 -5
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{internal-identity-DmUpBeeL.mjs → internal-identity-CbXoZIfw.mjs} +3 -3
  23. package/dist/internal-identity-CbXoZIfw.mjs.map +1 -0
  24. package/dist/{signal-B97cs85Z.mjs → layer-D56fRdLt.mjs} +30 -111
  25. package/dist/layer-D56fRdLt.mjs.map +1 -0
  26. package/dist/node-context-DWsCUBRa.d.mts +12 -0
  27. package/dist/node-context-DWsCUBRa.d.mts.map +1 -0
  28. package/dist/node-context-lggRF4bR.mjs +25 -0
  29. package/dist/node-context-lggRF4bR.mjs.map +1 -0
  30. package/dist/node.d.mts +51 -0
  31. package/dist/node.d.mts.map +1 -0
  32. package/dist/node.mjs +233 -0
  33. package/dist/node.mjs.map +1 -0
  34. package/dist/{index-rQhZk3Nt.d.mts → outcome-CB43bnFW.d.mts} +29 -19
  35. package/dist/outcome-CB43bnFW.d.mts.map +1 -0
  36. package/dist/{scope-GGnmTQck.mjs → program-metadata-DhQCCirO.mjs} +18 -3
  37. package/dist/program-metadata-DhQCCirO.mjs.map +1 -0
  38. package/dist/registration-drnZk0SY.mjs +182 -0
  39. package/dist/registration-drnZk0SY.mjs.map +1 -0
  40. package/dist/runtime/node.d.mts +2 -11
  41. package/dist/runtime/node.mjs +2 -24
  42. package/dist/{runtime-DnMn0X0X.mjs → runtime-CBMrX8Wl.mjs} +210 -47
  43. package/dist/runtime-CBMrX8Wl.mjs.map +1 -0
  44. package/dist/signal-D2UTcaaG.mjs +105 -0
  45. package/dist/signal-D2UTcaaG.mjs.map +1 -0
  46. package/dist/{standard-services-BFBq-4lo.mjs → standard-services-GBZ32tll.mjs} +200 -16
  47. package/dist/standard-services-GBZ32tll.mjs.map +1 -0
  48. package/dist/standard-services.d.mts +3 -3
  49. package/dist/standard-services.mjs +3 -3
  50. package/dist/testing.d.mts +65 -6
  51. package/dist/testing.d.mts.map +1 -1
  52. package/dist/testing.mjs +191 -8
  53. package/dist/testing.mjs.map +1 -1
  54. package/package.json +8 -3
  55. package/dist/effect-2ZcGZI8A.mjs.map +0 -1
  56. package/dist/errors-Dnjhzbt0.mjs +0 -99
  57. package/dist/errors-Dnjhzbt0.mjs.map +0 -1
  58. package/dist/index-C7Qild0_.d.mts.map +0 -1
  59. package/dist/index-DStz0JMN.d.mts.map +0 -1
  60. package/dist/index-rQhZk3Nt.d.mts.map +0 -1
  61. package/dist/internal-identity-DmUpBeeL.mjs.map +0 -1
  62. package/dist/runtime/node.d.mts.map +0 -1
  63. package/dist/runtime/node.mjs.map +0 -1
  64. package/dist/runtime-DnMn0X0X.mjs.map +0 -1
  65. package/dist/scope-GGnmTQck.mjs.map +0 -1
  66. package/dist/signal-B97cs85Z.mjs.map +0 -1
  67. package/dist/standard-services-BFBq-4lo.mjs.map +0 -1
@@ -0,0 +1,105 @@
1
+ import { d as setDefaultRuntimeContextStorage, n as currentRuntimeContext } from "./context-BUEf1qjL.mjs";
2
+ import { n as nodeRuntimeContextStorage } from "./node-context-lggRF4bR.mjs";
3
+ //#region src/runtime/default.ts
4
+ /** The Node/Bun storage used by the main Runtime entrypoint. */
5
+ const defaultRuntimeContextStorage = nodeRuntimeContextStorage;
6
+ setDefaultRuntimeContextStorage(defaultRuntimeContextStorage);
7
+ //#endregion
8
+ //#region src/scope/errors.ts
9
+ /** Thrown when Scope context is accessed outside an active Scope execution. */
10
+ var ScopeRuntimeNotConfiguredError = class extends Error {
11
+ constructor() {
12
+ super("No Scope is available in the current execution context");
13
+ this.name = "ScopeRuntimeNotConfiguredError";
14
+ }
15
+ };
16
+ /** Thrown when a resource or finalizer is added after Scope closure begins. */
17
+ var ScopeClosedError = class extends Error {
18
+ constructor() {
19
+ super("Cannot add resources or finalizers to a closed Scope");
20
+ this.name = "ScopeClosedError";
21
+ }
22
+ };
23
+ /** Aggregates finalizer failures encountered while closing a Scope. */
24
+ var ScopeCloseError = class extends Error {
25
+ causes;
26
+ constructor(causes) {
27
+ super(`Failed to close Scope (${causes.length} finalizer${causes.length === 1 ? "" : "s"} failed)`);
28
+ this.causes = causes;
29
+ this.name = "ScopeCloseError";
30
+ }
31
+ };
32
+ /** Thrown when a value has neither Symbol.dispose nor Symbol.asyncDispose. */
33
+ var ResourceNotDisposableError = class extends Error {
34
+ constructor() {
35
+ super("Resource does not implement Symbol.dispose or Symbol.asyncDispose");
36
+ this.name = "ResourceNotDisposableError";
37
+ }
38
+ };
39
+ //#endregion
40
+ //#region src/scope/disposable.ts
41
+ const SCOPE_SUCCESS = { status: "success" };
42
+ /** Return a Scope finalizer for a value's async or sync disposal protocol. */
43
+ const getDisposeFinalizer = (resource) => {
44
+ const candidate = Object(resource);
45
+ const asyncDispose = candidate[Symbol.asyncDispose];
46
+ if (asyncDispose instanceof Function) return () => asyncDispose.call(resource);
47
+ const dispose = candidate[Symbol.dispose];
48
+ if (dispose instanceof Function) return () => dispose.call(resource);
49
+ };
50
+ /** Dispose a value immediately when it implements a disposal protocol. */
51
+ const disposeResource = (resource) => {
52
+ return getDisposeFinalizer(resource)?.(SCOPE_SUCCESS);
53
+ };
54
+ //#endregion
55
+ //#region src/runtime/signal.ts
56
+ const neverAbortedSignal = new AbortController().signal;
57
+ /** Link caller, Runtime and shutdown signals without owning the caller's controller. */
58
+ const linkAbortSignals = (...signals) => {
59
+ const active = signals.filter((signal) => signal !== void 0);
60
+ if (active.length === 0) return {
61
+ signal: neverAbortedSignal,
62
+ dispose: () => {}
63
+ };
64
+ if (active.length === 1) return {
65
+ signal: active[0],
66
+ dispose: () => {}
67
+ };
68
+ const controller = new AbortController();
69
+ const listeners = [];
70
+ let disposed = false;
71
+ const dispose = () => {
72
+ if (disposed) return;
73
+ disposed = true;
74
+ for (const [source, listener] of listeners) source.removeEventListener("abort", listener);
75
+ listeners.length = 0;
76
+ };
77
+ const abortFrom = (source) => {
78
+ if (controller.signal.aborted) return;
79
+ controller.abort(source.reason);
80
+ dispose();
81
+ };
82
+ for (const source of active) {
83
+ if (source.aborted) {
84
+ abortFrom(source);
85
+ break;
86
+ }
87
+ const listener = () => abortFrom(source);
88
+ listeners.push([source, listener]);
89
+ source.addEventListener("abort", listener, { once: true });
90
+ }
91
+ return {
92
+ signal: controller.signal,
93
+ dispose
94
+ };
95
+ };
96
+ /** Return the current cooperative-cancellation signal. */
97
+ const currentAbortSignal = () => currentRuntimeContext().signal ?? neverAbortedSignal;
98
+ /** Yieldable access to the signal of the current Runtime execution. */
99
+ const CurrentAbortSignal = { *[Symbol.iterator]() {
100
+ return currentAbortSignal();
101
+ } };
102
+ //#endregion
103
+ export { ResourceNotDisposableError as a, ScopeRuntimeNotConfiguredError as c, getDisposeFinalizer as i, defaultRuntimeContextStorage as l, linkAbortSignals as n, ScopeCloseError as o, disposeResource as r, ScopeClosedError as s, CurrentAbortSignal as t };
104
+
105
+ //# sourceMappingURL=signal-D2UTcaaG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-D2UTcaaG.mjs","names":[],"sources":["../src/runtime/default.ts","../src/scope/errors.ts","../src/scope/disposable.ts","../src/runtime/signal.ts"],"sourcesContent":["import { nodeRuntimeContextStorage } from './node-context'\n\nimport { setDefaultRuntimeContextStorage } from './context'\n\n/** The Node/Bun storage used by the main Runtime entrypoint. */\nexport const defaultRuntimeContextStorage = nodeRuntimeContextStorage\n\nsetDefaultRuntimeContextStorage(defaultRuntimeContextStorage)\n","/** Thrown when Scope context is accessed outside an active Scope execution. */\nexport class ScopeRuntimeNotConfiguredError extends Error {\n constructor() {\n super('No Scope is available in the current execution context')\n\n this.name = 'ScopeRuntimeNotConfiguredError'\n }\n}\n\n/** Thrown when a resource or finalizer is added after Scope closure begins. */\nexport class ScopeClosedError extends Error {\n constructor() {\n super('Cannot add resources or finalizers to a closed Scope')\n\n this.name = 'ScopeClosedError'\n }\n}\n\n/** Aggregates finalizer failures encountered while closing a Scope. */\nexport class ScopeCloseError extends Error {\n constructor(readonly causes: readonly unknown[]) {\n super(\n `Failed to close Scope (${causes.length} finalizer${causes.length === 1 ? '' : 's'} failed)`\n )\n\n this.name = 'ScopeCloseError'\n }\n}\n\n/** Thrown when a value has neither Symbol.dispose nor Symbol.asyncDispose. */\nexport class ResourceNotDisposableError extends Error {\n constructor() {\n super('Resource does not implement Symbol.dispose or Symbol.asyncDispose')\n\n this.name = 'ResourceNotDisposableError'\n }\n}\n","import type { ScopeFinalizer } from './types'\n\nconst SCOPE_SUCCESS = { status: 'success' } as const\n\ntype Disposer = (...args: never[]) => void | PromiseLike<void>\n\ntype DisposableCandidate = {\n [Symbol.dispose]?: Disposer\n\n [Symbol.asyncDispose]?: Disposer\n}\n\n/** Return a Scope finalizer for a value's async or sync disposal protocol. */\nexport const getDisposeFinalizer = <Resource>(resource: Resource): ScopeFinalizer | undefined => {\n // SAFETY: Object() provides a property-bearing view for protocol lookup; each method is checked for callability before invocation.\n const candidate = Object(resource) as DisposableCandidate\n const asyncDispose = candidate[Symbol.asyncDispose]\n\n if (asyncDispose instanceof Function) {\n return () => asyncDispose.call(resource)\n }\n\n const dispose = candidate[Symbol.dispose]\n\n if (dispose instanceof Function) {\n return () => dispose.call(resource)\n }\n\n return undefined\n}\n\n/** Dispose a value immediately when it implements a disposal protocol. */\nexport const disposeResource = <Resource>(resource: Resource): void | PromiseLike<void> => {\n const finalizer = getDisposeFinalizer(resource)\n\n return finalizer?.(SCOPE_SUCCESS)\n}\n","import { currentRuntimeContext } from './context'\n\nconst neverAbortedSignal = new AbortController().signal\n\ntype SignalListener = readonly [AbortSignal, () => void]\n\nexport type AbortSignalLink = {\n readonly signal: AbortSignal\n readonly dispose: () => void\n}\n\n/** Link caller, Runtime and shutdown signals without owning the caller's controller. */\nexport const linkAbortSignals = (\n ...signals: readonly (AbortSignal | undefined)[]\n): AbortSignalLink => {\n const active = signals.filter((signal): signal is AbortSignal => signal !== undefined)\n\n if (active.length === 0) {\n return { signal: neverAbortedSignal, dispose: () => {} }\n }\n\n if (active.length === 1) {\n return { signal: active[0]!, dispose: () => {} }\n }\n\n const controller = new AbortController()\n const listeners: SignalListener[] = []\n let disposed = false\n\n const dispose = (): void => {\n if (disposed) {\n return\n }\n\n disposed = true\n\n for (const [source, listener] of listeners) {\n source.removeEventListener('abort', listener)\n }\n\n listeners.length = 0\n }\n\n const abortFrom = (source: AbortSignal): void => {\n if (controller.signal.aborted) {\n return\n }\n\n controller.abort(source.reason)\n dispose()\n }\n\n for (const source of active) {\n if (source.aborted) {\n abortFrom(source)\n break\n }\n\n const listener = (): void => abortFrom(source)\n listeners.push([source, listener])\n source.addEventListener('abort', listener, { once: true })\n }\n\n return { signal: controller.signal, dispose }\n}\n\n/** Return the current cooperative-cancellation signal. */\nexport const currentAbortSignal = (): AbortSignal =>\n currentRuntimeContext().signal ?? neverAbortedSignal\n\n/** Yieldable access to the signal of the current Runtime execution. */\nexport const CurrentAbortSignal = {\n // oxlint-disable-next-line require-yield\n *[Symbol.iterator](): Generator<never, AbortSignal, unknown> {\n return currentAbortSignal()\n }\n} as const\n"],"mappings":";;;;AAKA,MAAa,+BAA+B;AAE5C,gCAAgC,4BAA4B;;;;ACN5D,IAAa,iCAAb,cAAoD,MAAM;CACxD,cAAc;EACZ,MAAM,wDAAwD;EAE9D,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,mBAAb,cAAsC,MAAM;CAC1C,cAAc;EACZ,MAAM,sDAAsD;EAE5D,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,kBAAb,cAAqC,MAAM;CACpB;CAArB,YAAY,QAAqC;EAC/C,MACE,0BAA0B,OAAO,OAAO,YAAY,OAAO,WAAW,IAAI,KAAK,IAAI,SACrF;EAHmB,KAAA,SAAA;EAKnB,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,6BAAb,cAAgD,MAAM;CACpD,cAAc;EACZ,MAAM,mEAAmE;EAEzE,KAAK,OAAO;CACd;AACF;;;AClCA,MAAM,gBAAgB,EAAE,QAAQ,UAAU;;AAW1C,MAAa,uBAAiC,aAAmD;CAE/F,MAAM,YAAY,OAAO,QAAQ;CACjC,MAAM,eAAe,UAAU,OAAO;CAEtC,IAAI,wBAAwB,UAC1B,aAAa,aAAa,KAAK,QAAQ;CAGzC,MAAM,UAAU,UAAU,OAAO;CAEjC,IAAI,mBAAmB,UACrB,aAAa,QAAQ,KAAK,QAAQ;AAItC;;AAGA,MAAa,mBAA6B,aAAiD;CAGzF,OAFkB,oBAAoB,QAEvB,CAAC,GAAG,aAAa;AAClC;;;AClCA,MAAM,qBAAqB,IAAI,gBAAgB,CAAC,CAAC;;AAUjD,MAAa,oBACX,GAAG,YACiB;CACpB,MAAM,SAAS,QAAQ,QAAQ,WAAkC,WAAW,KAAA,CAAS;CAErF,IAAI,OAAO,WAAW,GACpB,OAAO;EAAE,QAAQ;EAAoB,eAAe,CAAC;CAAE;CAGzD,IAAI,OAAO,WAAW,GACpB,OAAO;EAAE,QAAQ,OAAO;EAAK,eAAe,CAAC;CAAE;CAGjD,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,YAA8B,CAAC;CACrC,IAAI,WAAW;CAEf,MAAM,gBAAsB;EAC1B,IAAI,UACF;EAGF,WAAW;EAEX,KAAK,MAAM,CAAC,QAAQ,aAAa,WAC/B,OAAO,oBAAoB,SAAS,QAAQ;EAG9C,UAAU,SAAS;CACrB;CAEA,MAAM,aAAa,WAA8B;EAC/C,IAAI,WAAW,OAAO,SACpB;EAGF,WAAW,MAAM,OAAO,MAAM;EAC9B,QAAQ;CACV;CAEA,KAAK,MAAM,UAAU,QAAQ;EAC3B,IAAI,OAAO,SAAS;GAClB,UAAU,MAAM;GAChB;EACF;EAEA,MAAM,iBAAuB,UAAU,MAAM;EAC7C,UAAU,KAAK,CAAC,QAAQ,QAAQ,CAAC;EACjC,OAAO,iBAAiB,SAAS,UAAU,EAAE,MAAM,KAAK,CAAC;CAC3D;CAEA,OAAO;EAAE,QAAQ,WAAW;EAAQ;CAAQ;AAC9C;;AAGA,MAAa,2BACX,sBAAsB,CAAC,CAAC,UAAU;;AAGpC,MAAa,qBAAqB,EAEhC,EAAE,OAAO,YAAoD;CAC3D,OAAO,mBAAmB;AAC5B,EACF"}
@@ -1,4 +1,4 @@
1
- import { r as Layer, u as Service } from "./signal-B97cs85Z.mjs";
1
+ import { n as Service, t as Layer } from "./layer-D56fRdLt.mjs";
2
2
  import { Result, TaggedError } from "better-result";
3
3
  //#region src/standard-services/config.ts
4
4
  /** A schema validation failure. Raw source values are intentionally omitted. */
@@ -159,22 +159,140 @@ var Config = class Config extends Service()("Config") {
159
159
  /** The default lazy host-environment Config provider. */
160
160
  const ConfigLive = Config.layerFromEnv();
161
161
  //#endregion
162
+ //#region src/standard-services/id-generator.ts
163
+ const hostCrypto = () => {
164
+ return globalThis.crypto;
165
+ };
166
+ /** Thrown when the host does not expose the supported Web Crypto UUID API. */
167
+ var IdGeneratorUnavailableError = class extends Error {
168
+ constructor() {
169
+ super("IdGenerator requires a host crypto.randomUUID implementation");
170
+ this.name = "IdGeneratorUnavailableError";
171
+ }
172
+ };
173
+ /** Thrown when an {@link IdGeneratorTest} queue has no IDs left. */
174
+ var IdGeneratorExhaustedError = class extends Error {
175
+ generated;
176
+ constructor(generated) {
177
+ super(`IdGeneratorTest queue exhausted after ${generated} generated ID${generated === 1 ? "" : "s"}`);
178
+ this.generated = generated;
179
+ this.name = "IdGeneratorExhaustedError";
180
+ }
181
+ };
182
+ /** Host-backed UUID string ID service. */
183
+ var IdGenerator = class extends Service()("IdGenerator") {
184
+ /** Generate a cryptographically strong UUID string using the host Web Crypto API. */
185
+ next() {
186
+ const crypto = hostCrypto();
187
+ if (crypto === void 0 || typeof crypto.randomUUID !== "function") throw new IdGeneratorUnavailableError();
188
+ return crypto.randomUUID();
189
+ }
190
+ };
191
+ /** The default host IdGenerator provider. */
192
+ const IdGeneratorLive = Layer.make(IdGenerator);
193
+ const validateId = (value, source) => {
194
+ if (typeof value !== "string") throw new TypeError(`${source} must return a string ID`);
195
+ return value;
196
+ };
197
+ /** Deterministic queue- or factory-backed IdGenerator implementation for tests. */
198
+ var IdGeneratorTest = class IdGeneratorTest {
199
+ queue;
200
+ factory;
201
+ generatedCount = 0;
202
+ constructor(ids = []) {
203
+ this.queue = ids.map((id) => validateId(id, "IdGeneratorTest queue values"));
204
+ }
205
+ /** Number of IDs successfully returned by this test generator. */
206
+ get generated() {
207
+ return this.generatedCount;
208
+ }
209
+ /** Number of queued IDs still available, or Infinity for a factory. */
210
+ get remaining() {
211
+ return this.factory === void 0 ? this.queue.length : Infinity;
212
+ }
213
+ /** Return the next queued or factory-generated string ID. */
214
+ next() {
215
+ if (this.factory === void 0) {
216
+ const value = this.queue.shift();
217
+ if (value === void 0) throw new IdGeneratorExhaustedError(this.generatedCount);
218
+ this.generatedCount += 1;
219
+ return value;
220
+ }
221
+ const value = validateId(this.factory(this.generatedCount), "IdGeneratorTest factory");
222
+ this.generatedCount += 1;
223
+ return value;
224
+ }
225
+ /**
226
+ * Create a factory-backed generator. The factory receives a monotonic
227
+ * zero-based index: the first call receives 0, then 1, and so on.
228
+ */
229
+ static from(factory) {
230
+ if (typeof factory !== "function") throw new TypeError("IdGeneratorTest.from requires a factory function");
231
+ const generator = new IdGeneratorTest();
232
+ generator.factory = factory;
233
+ return generator;
234
+ }
235
+ /** Create a Layer for this generator, allocating a fresh empty queue by default. */
236
+ static layer(generator = new IdGeneratorTest()) {
237
+ return Layer.succeed(IdGenerator, generator);
238
+ }
239
+ };
240
+ /** Compatible functional shorthand for {@link IdGeneratorTest.layer}. */
241
+ const IdGeneratorTestLayer = (generator) => IdGeneratorTest.layer(generator);
242
+ //#endregion
162
243
  //#region src/standard-services/index.ts
163
244
  const assertDelay = (milliseconds) => {
164
245
  if (!Number.isFinite(milliseconds) || milliseconds < 0) throw new RangeError("Delay must be a finite non-negative number");
165
246
  };
247
+ const abortError = () => new DOMException("The operation was aborted", "AbortError");
248
+ const abortReason = (signal) => signal.reason === void 0 ? abortError() : signal.reason;
166
249
  /** Host-backed time and waiting service. */
167
250
  var Clock = class extends Service()("Clock") {
168
251
  now() {
169
252
  return /* @__PURE__ */ new Date();
170
253
  }
171
- sleep(milliseconds) {
254
+ /** Rejects with the signal reason, or an AbortError-compatible fallback. */
255
+ sleep(milliseconds, options) {
172
256
  assertDelay(milliseconds);
173
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
257
+ const signal = options?.signal;
258
+ if (signal?.aborted) return Promise.reject(abortReason(signal));
259
+ return new Promise((resolve, reject) => {
260
+ let timer;
261
+ let settled = false;
262
+ function cleanup() {
263
+ if (timer !== void 0) {
264
+ clearTimeout(timer);
265
+ timer = void 0;
266
+ }
267
+ signal?.removeEventListener("abort", onAbort);
268
+ }
269
+ function settle(callback) {
270
+ if (settled) return;
271
+ settled = true;
272
+ cleanup();
273
+ callback();
274
+ }
275
+ function onAbort() {
276
+ if (signal !== void 0) settle(() => reject(abortReason(signal)));
277
+ }
278
+ try {
279
+ signal?.addEventListener("abort", onAbort, { once: true });
280
+ if (signal?.aborted) {
281
+ onAbort();
282
+ return;
283
+ }
284
+ const handle = setTimeout(() => settle(resolve), milliseconds);
285
+ if (settled) clearTimeout(handle);
286
+ else timer = handle;
287
+ } catch (cause) {
288
+ settle(() => reject(cause));
289
+ }
290
+ });
174
291
  }
175
292
  };
176
293
  /** The default host Clock provider. */
177
294
  const ClockLive = Layer.make(Clock);
295
+ const defaultClockTestMaxSteps = 1e3;
178
296
  /** Deterministic Clock implementation for tests. */
179
297
  var ClockTest = class ClockTest {
180
298
  currentTime;
@@ -183,9 +301,14 @@ var ClockTest = class ClockTest {
183
301
  this.currentTime = initial instanceof Date ? initial.getTime() : initial;
184
302
  if (!Number.isFinite(this.currentTime)) throw new RangeError("ClockTest time must be finite");
185
303
  }
304
+ /** Number of sleeps that have not been settled or cancelled. */
305
+ get pendingSleeps() {
306
+ return this.waiters.length;
307
+ }
186
308
  now() {
187
309
  return new Date(this.currentTime);
188
310
  }
311
+ /** Set an absolute time; moving backward leaves existing absolute deadlines unchanged. */
189
312
  setTime(value) {
190
313
  const next = value instanceof Date ? value.getTime() : value;
191
314
  if (!Number.isFinite(next)) throw new RangeError("ClockTest time must be finite");
@@ -197,28 +320,89 @@ var ClockTest = class ClockTest {
197
320
  this.currentTime += milliseconds;
198
321
  this.flushWaiters();
199
322
  }
200
- sleep(milliseconds) {
323
+ sleep(milliseconds, options) {
201
324
  assertDelay(milliseconds);
202
- return new Promise((resolve) => {
203
- this.waiters.push({
325
+ const signal = options?.signal;
326
+ if (signal?.aborted) return Promise.reject(abortReason(signal));
327
+ return new Promise((resolve, reject) => {
328
+ let waiter;
329
+ const onAbort = () => {
330
+ if (signal !== void 0) this.cancelWaiter(waiter, abortReason(signal));
331
+ };
332
+ waiter = {
204
333
  at: this.currentTime + milliseconds,
205
- resolve
206
- });
334
+ resolve,
335
+ reject,
336
+ signal,
337
+ onAbort,
338
+ settled: false
339
+ };
340
+ this.enqueueWaiter(waiter);
341
+ try {
342
+ signal?.addEventListener("abort", onAbort, { once: true });
343
+ if (signal?.aborted) {
344
+ this.cancelWaiter(waiter, abortReason(signal));
345
+ return;
346
+ }
347
+ } catch (cause) {
348
+ this.cancelWaiter(waiter, cause);
349
+ return;
350
+ }
207
351
  this.flushWaiters();
208
352
  });
209
353
  }
354
+ /** Advance synchronously; due Promise continuations run after this method returns. */
355
+ advanceToNext() {
356
+ const next = this.waiters[0];
357
+ if (next === void 0) return false;
358
+ this.currentTime = next.at;
359
+ this.flushWaiters();
360
+ return true;
361
+ }
362
+ /** Advance all pending deadlines, guarding at 1,000 steps by default. */
363
+ async runAll(options) {
364
+ const maxSteps = getMaxSteps(options);
365
+ let steps = 0;
366
+ while (this.pendingSleeps > 0) {
367
+ if (steps >= maxSteps) throw new RangeError(`ClockTest.runAll exceeded maxSteps (${maxSteps})`);
368
+ this.advanceToNext();
369
+ steps += 1;
370
+ await Promise.resolve();
371
+ }
372
+ return steps;
373
+ }
210
374
  static layer(initial = 0) {
211
375
  return Layer.succeed(Clock, new ClockTest(initial));
212
376
  }
377
+ enqueueWaiter(waiter) {
378
+ const index = this.waiters.findIndex((pending) => pending.at > waiter.at);
379
+ if (index === -1) this.waiters.push(waiter);
380
+ else this.waiters.splice(index, 0, waiter);
381
+ }
382
+ cancelWaiter(waiter, cause) {
383
+ const index = this.waiters.indexOf(waiter);
384
+ if (index !== -1) this.waiters.splice(index, 1);
385
+ this.finishWaiter(waiter, () => waiter.reject(cause));
386
+ }
213
387
  flushWaiters() {
214
- for (let index = this.waiters.length - 1; index >= 0; index--) {
215
- const waiter = this.waiters[index];
216
- if (waiter.at <= this.currentTime) {
217
- this.waiters.splice(index, 1);
218
- waiter.resolve();
219
- }
388
+ while (true) {
389
+ const waiter = this.waiters[0];
390
+ if (waiter === void 0 || waiter.at > this.currentTime) return;
391
+ this.waiters.shift();
392
+ this.finishWaiter(waiter, waiter.resolve);
220
393
  }
221
394
  }
395
+ finishWaiter(waiter, finish) {
396
+ if (waiter.settled) return;
397
+ waiter.settled = true;
398
+ waiter.signal?.removeEventListener("abort", waiter.onAbort);
399
+ finish();
400
+ }
401
+ };
402
+ const getMaxSteps = (options) => {
403
+ const maxSteps = options?.maxSteps ?? defaultClockTestMaxSteps;
404
+ if (!Number.isInteger(maxSteps) || maxSteps < 0) throw new RangeError("ClockTest.runAll maxSteps must be a finite non-negative integer");
405
+ return maxSteps;
222
406
  };
223
407
  const ClockTestLayer = (initial = 0) => ClockTest.layer(initial);
224
408
  /** Host-backed pseudo-random number service. */
@@ -335,6 +519,6 @@ var CurrentRequest = class CurrentRequest extends Service()("CurrentRequest") {
335
519
  };
336
520
  const CurrentRequestLayer = (value) => CurrentRequest.layer(value);
337
521
  //#endregion
338
- export { ConfigSourceError as _, CurrentRequest as a, LoggerLive as c, Random as d, RandomLive as f, ConfigLive as g, Config as h, ClockTestLayer as i, LoggerTest as l, RandomSeededLayer as m, ClockLive as n, CurrentRequestLayer as o, RandomSeeded as p, ClockTest as r, Logger as s, Clock as t, LoggerTestLayer as u, ConfigValidationError as v };
522
+ export { ConfigSourceError as C, ConfigLive as S, IdGeneratorLive as _, CurrentRequest as a, IdGeneratorUnavailableError as b, LoggerLive as c, Random as d, RandomLive as f, IdGeneratorExhaustedError as g, IdGenerator as h, ClockTestLayer as i, LoggerTest as l, RandomSeededLayer as m, ClockLive as n, CurrentRequestLayer as o, RandomSeeded as p, ClockTest as r, Logger as s, Clock as t, LoggerTestLayer as u, IdGeneratorTest as v, ConfigValidationError as w, Config as x, IdGeneratorTestLayer as y };
339
523
 
340
- //# sourceMappingURL=standard-services-BFBq-4lo.mjs.map
524
+ //# sourceMappingURL=standard-services-GBZ32tll.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard-services-GBZ32tll.mjs","names":[],"sources":["../src/standard-services/config.ts","../src/standard-services/id-generator.ts","../src/standard-services/index.ts"],"sourcesContent":["import {\n Result,\n TaggedError,\n type Err,\n type Result as ResultType,\n type StandardSchemaV1,\n type UnhandledException\n} from 'better-result'\n\nimport type { ServiceRequirement } from '../effect/types'\nimport { Layer } from '../layer'\nimport { Service } from '../service'\n\n/** Raw string values supplied to a configuration schema. */\nexport type ConfigSource = Readonly<Record<string, string | undefined>>\n\n/** Optional sources used by environment-backed configuration values. */\nexport type ConfigSourceOptions = {\n readonly dotEnvPath?: string\n readonly envSource?: ConfigSource\n}\n\n/** Options for the one-call schema-bound configuration API. */\nexport type ConfigFromEnvOptions<Schema extends StandardSchemaV1> = ConfigSourceOptions & {\n readonly schema: Schema\n}\n\n/** The decoded value produced by a Standard Schema. */\nexport type ConfigOutput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferOutput<Schema>\n\n/** The raw input type described by a Standard Schema. */\nexport type ConfigInput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferInput<Schema>\n\n/** A safe validation issue exposed by a configuration failure. */\nexport type ConfigIssue = StandardSchemaV1.Issue\n\n/** A schema validation failure. Raw source values are intentionally omitted. */\nexport class ConfigValidationError extends TaggedError('ConfigValidationError')<{\n readonly issues: ReadonlyArray<ConfigIssue>\n readonly message: string\n}> {}\n\n/** A dotenv or host-source loading failure. Raw source values are intentionally omitted. */\nexport class ConfigSourceError extends TaggedError('ConfigSourceError')<{\n readonly cause: unknown\n readonly message: string\n readonly path?: string\n}> {}\n\n/** Errors that may be returned while loading or validating configuration. */\nexport type ConfigError = ConfigValidationError | ConfigSourceError | UnhandledException\n\ntype ConfigYield<Requirements extends Service.Any, Error> =\n | Err<never, Error>\n | ServiceRequirement<Requirements>\n\n/** A reusable, async-yieldable Standard Schema configuration descriptor. */\nexport type ConfigValue<\n Schema extends StandardSchemaV1,\n Requirements extends Service.Any = never,\n Error = ConfigError\n> = {\n [Symbol.asyncIterator](): AsyncGenerator<\n ConfigYield<Requirements, Error>,\n ConfigOutput<Schema>,\n unknown\n >\n}\n\ntype RuntimeConfigYield = Err<never, ConfigError> | ServiceRequirement<Service.Any>\n\ntype ConfigValueSpec<Schema extends StandardSchemaV1> = {\n readonly schema: Schema\n readonly source: 'bound' | 'provider'\n readonly sourceOptions?: ConfigSourceOptions\n}\n\ntype MutableConfigSourceOptions = {\n dotEnvPath?: string\n envSource?: ConfigSource\n}\n\ntype RuntimeGlobal = typeof globalThis & {\n readonly process?: { readonly env?: ConfigSource }\n readonly Bun?: {\n readonly env?: ConfigSource\n readonly file?: (path: string) => { text(): Promise<string> }\n }\n}\n\n// SAFETY: Hosts may expose process/Bun globals at runtime without declaring them on globalThis.\nconst runtimeGlobal = globalThis as RuntimeGlobal\n\nconst hostEnvironment = (): ConfigSource => {\n const processEnvironment = runtimeGlobal.process?.env\n\n if (processEnvironment !== undefined) {\n return processEnvironment\n }\n\n return runtimeGlobal.Bun?.env ?? {}\n}\n\nconst readText = async (path: string): Promise<string> => {\n const file = runtimeGlobal.Bun?.file\n\n if (file !== undefined) {\n return await file(path).text()\n }\n\n const { readFile } = await import('node:fs/promises')\n\n return await readFile(path, 'utf8')\n}\n\nconst parseDotEnv = (text: string) => {\n const values = new Map<string, string>()\n\n for (const [lineIndex, rawLine] of text\n .replace(/^\\uFEFF/, '')\n .split(/\\r?\\n/u)\n .entries()) {\n let line = rawLine.trim()\n\n if (line === '' || line.startsWith('#')) {\n continue\n }\n\n if (/^export\\s/u.test(line)) {\n line = line.replace(/^export\\s+/u, '')\n }\n\n const separator = line.indexOf('=')\n\n if (separator <= 0) {\n throw new Error(`Invalid dotenv entry at line ${lineIndex + 1}`)\n }\n\n const key = line.slice(0, separator).trim()\n\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) {\n throw new Error(`Invalid dotenv key at line ${lineIndex + 1}`)\n }\n\n let value = line.slice(separator + 1).trim()\n const quote = value[0]\n\n if (quote === '\"' || quote === \"'\") {\n const closingQuote = value.indexOf(quote, 1)\n const trailing = closingQuote < 0 ? '' : value.slice(closingQuote + 1).trim()\n\n if (closingQuote < 0 || (trailing !== '' && !trailing.startsWith('#'))) {\n throw new Error(`Unterminated dotenv value at line ${lineIndex + 1}`)\n }\n\n value = value.slice(1, closingQuote)\n } else {\n const comment = value.search(/\\s+#/u)\n\n if (comment >= 0) {\n value = value.slice(0, comment).trimEnd()\n }\n }\n\n values.set(key, value)\n }\n\n return Object.fromEntries(values)\n}\n\nconst loadSource = async (options: ConfigSourceOptions = {}): Promise<ConfigSource> => {\n const dotenv =\n options.dotEnvPath === undefined ? {} : parseDotEnv(await readText(options.dotEnvPath))\n const explicit = options.envSource ?? hostEnvironment()\n\n return {\n ...dotenv,\n ...explicit\n }\n}\n\nconst sourceError = (path: string | undefined, cause: unknown): ConfigSourceError => {\n const message =\n path === undefined\n ? 'Failed to load configuration source'\n : `Failed to load configuration source at ${path}`\n\n if (path === undefined) {\n return new ConfigSourceError({ cause, message })\n }\n\n return new ConfigSourceError({ cause, message, path })\n}\n\nconst validate = async <Schema extends StandardSchemaV1>(\n schema: Schema,\n source: ConfigSource\n): Promise<ResultType<ConfigOutput<Schema>, ConfigValidationError | UnhandledException>> => {\n const checked = await Result.tryPromise(() =>\n Promise.resolve(schema['~standard'].validate(source))\n )\n\n if (checked.status === 'error') {\n return checked\n }\n\n if (checked.value.issues !== undefined) {\n return Result.err(\n new ConfigValidationError({\n issues: Object.freeze([...checked.value.issues]),\n message: 'Configuration validation failed'\n })\n )\n }\n\n // SAFETY: A successful Standard Schema result carries its declared output type.\n return Result.ok(checked.value.value as ConfigOutput<Schema>)\n}\n\nconst evaluate = async <Schema extends StandardSchemaV1>(\n spec: ConfigValueSpec<Schema>,\n provider?: Config\n): Promise<ResultType<ConfigOutput<Schema>, ConfigError>> => {\n const loaded = await Result.tryPromise({\n try: async () => {\n if (spec.source === 'provider') {\n const base = provider?.source\n\n if (base === undefined) {\n throw new Error('Config provider did not expose a source')\n }\n\n if (spec.sourceOptions === undefined) {\n return base\n }\n\n return {\n ...base,\n ...(await loadSource(spec.sourceOptions))\n }\n }\n\n return await loadSource(spec.sourceOptions)\n },\n catch: (cause) => sourceError(spec.sourceOptions?.dotEnvPath, cause)\n })\n\n if (loaded.status === 'error') {\n return loaded\n }\n\n return await validate(spec.schema, loaded.value)\n}\n\nconst makeConfigValue = <Schema extends StandardSchemaV1, Requirements extends Service.Any>(\n spec: ConfigValueSpec<Schema>\n): ConfigValue<Schema, Requirements> => {\n const value = {\n ...spec,\n async *[Symbol.asyncIterator](): AsyncGenerator<\n RuntimeConfigYield,\n ConfigOutput<Schema>,\n unknown\n > {\n let provider: Config | undefined\n\n if (spec.source === 'provider') {\n provider = yield* Config\n }\n\n return yield* Result.await(evaluate(spec, provider))\n }\n }\n\n // SAFETY: The runtime iterator only yields Result errors and Service values; the public cast restores the phantom requirement.\n return value as ConfigValue<Schema, Requirements>\n}\n\n/** Host-backed raw configuration source and provider token. */\nexport class Config extends Service<Config>()('Config') {\n constructor(readonly source: ConfigSource) {\n super()\n }\n\n /** Read one raw value from the configured source. */\n get(key: string): string | undefined {\n return this.source[key]\n }\n\n /** Create a provider from an already-loaded source. */\n static layer(source: ConfigSource) {\n return Layer.succeed(Config, new Config(source))\n }\n\n /** Create a provider whose source is loaded from dotenv and the host environment. */\n static layerFromEnv(options: ConfigSourceOptions = {}) {\n return Layer.make(Config, async () => {\n try {\n return new Config(await loadSource(options))\n } catch (cause) {\n throw sourceError(options.dotEnvPath, cause)\n }\n })\n }\n\n /** Describe a configuration read from the contextual Config provider. */\n static schema<Schema extends StandardSchemaV1>(schema: Schema): ConfigValue<Schema, Config> {\n return makeConfigValue({ schema, source: 'provider' })\n }\n\n /** Describe a reusable configuration loaded from dotenv and the host environment. */\n static fromEnv<Schema extends StandardSchemaV1>(\n options: ConfigFromEnvOptions<Schema>\n ): ConfigValue<Schema> {\n const sourceOptions: MutableConfigSourceOptions = {}\n\n if (options.dotEnvPath !== undefined) {\n sourceOptions.dotEnvPath = options.dotEnvPath\n }\n\n if (options.envSource !== undefined) {\n sourceOptions.envSource = options.envSource\n }\n\n return makeConfigValue({\n schema: options.schema,\n source: 'bound',\n sourceOptions\n })\n }\n\n /** Add an environment source to a provider-backed descriptor for functional `pipe` composition. */\n static withEnv(options: ConfigSourceOptions) {\n return <Schema extends StandardSchemaV1, Requirements extends Service.Any, Error = ConfigError>(\n value: ConfigValue<Schema, Requirements, Error>\n ): ConfigValue<Schema, Requirements, Error> => {\n // SAFETY: Values produced by this module retain their schema/source descriptor fields; custom iterables pass through unchanged.\n const internal = value as ConfigValue<Schema, Requirements> & ConfigValueSpec<Schema>\n\n if (!('schema' in internal)) {\n return value\n }\n\n // SAFETY: The internal descriptor preserves the original schema and requirement phantom.\n return makeConfigValue({\n schema: internal.schema,\n source: internal.source,\n sourceOptions: options\n }) as ConfigValue<Schema, Requirements, Error>\n }\n }\n}\n\n/** The default lazy host-environment Config provider. */\nexport const ConfigLive = Config.layerFromEnv()\n\nexport type { StandardSchemaV1 }\n","import { Layer } from '../layer'\nimport { Service } from '../service'\n\ntype HostCrypto = {\n readonly randomUUID?: () => string\n}\n\nconst hostCrypto = (): HostCrypto | undefined => {\n // SAFETY: Supported hosts expose crypto on globalThis, but the optional property models older or restricted hosts.\n return (globalThis as { readonly crypto?: HostCrypto }).crypto\n}\n\n/** Thrown when the host does not expose the supported Web Crypto UUID API. */\nexport class IdGeneratorUnavailableError extends Error {\n constructor() {\n super('IdGenerator requires a host crypto.randomUUID implementation')\n\n this.name = 'IdGeneratorUnavailableError'\n }\n}\n\n/** Thrown when an {@link IdGeneratorTest} queue has no IDs left. */\nexport class IdGeneratorExhaustedError extends Error {\n constructor(readonly generated: number) {\n super(\n `IdGeneratorTest queue exhausted after ${generated} generated ID${generated === 1 ? '' : 's'}`\n )\n\n this.name = 'IdGeneratorExhaustedError'\n }\n}\n\n/** Host-backed UUID string ID service. */\nexport class IdGenerator extends Service<IdGenerator>()('IdGenerator') {\n /** Generate a cryptographically strong UUID string using the host Web Crypto API. */\n next(): string {\n const crypto = hostCrypto()\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- host feature detection must verify the callable Web Crypto method.\n if (crypto === undefined || typeof crypto.randomUUID !== 'function') {\n throw new IdGeneratorUnavailableError()\n }\n\n return crypto.randomUUID()\n }\n}\n\n/** The default host IdGenerator provider. */\nexport const IdGeneratorLive = Layer.make(IdGenerator)\n\ntype IdFactory = (index: number) => string\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- runtime validation protects the public string-ID boundary.\nconst validateId = (value: unknown, source: string): string => {\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- runtime validation rejects unsafe test doubles.\n if (typeof value !== 'string') {\n throw new TypeError(`${source} must return a string ID`)\n }\n\n return value\n}\n\n/** Deterministic queue- or factory-backed IdGenerator implementation for tests. */\nexport class IdGeneratorTest implements Service.Contract<IdGenerator> {\n private readonly queue: string[]\n\n private factory: IdFactory | undefined\n\n private generatedCount = 0\n\n constructor(ids: readonly string[] = []) {\n this.queue = ids.map((id) => validateId(id, 'IdGeneratorTest queue values'))\n }\n\n /** Number of IDs successfully returned by this test generator. */\n get generated(): number {\n return this.generatedCount\n }\n\n /** Number of queued IDs still available, or Infinity for a factory. */\n get remaining(): number {\n return this.factory === undefined ? this.queue.length : Infinity\n }\n\n /** Return the next queued or factory-generated string ID. */\n next(): string {\n if (this.factory === undefined) {\n const value = this.queue.shift()\n\n if (value === undefined) {\n throw new IdGeneratorExhaustedError(this.generatedCount)\n }\n\n this.generatedCount += 1\n return value\n }\n\n const value = validateId(this.factory(this.generatedCount), 'IdGeneratorTest factory')\n this.generatedCount += 1\n return value\n }\n\n /**\n * Create a factory-backed generator. The factory receives a monotonic\n * zero-based index: the first call receives 0, then 1, and so on.\n */\n static from(factory: IdFactory): IdGeneratorTest {\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- runtime validation rejects an invalid factory at the API boundary.\n if (typeof factory !== 'function') {\n throw new TypeError('IdGeneratorTest.from requires a factory function')\n }\n\n const generator = new IdGeneratorTest()\n generator.factory = factory\n return generator\n }\n\n /** Create a Layer for this generator, allocating a fresh empty queue by default. */\n static layer(generator: IdGeneratorTest = new IdGeneratorTest()) {\n return Layer.succeed(IdGenerator, generator)\n }\n}\n\n/** Compatible functional shorthand for {@link IdGeneratorTest.layer}. */\nexport const IdGeneratorTestLayer = (generator?: IdGeneratorTest) =>\n IdGeneratorTest.layer(generator)\n","import { CurrentAbortSignal } from '../runtime'\nimport { Layer } from '../layer'\nimport { Service } from '../service'\n\nexport { CurrentAbortSignal }\n\nexport { Config, ConfigLive, ConfigSourceError, ConfigValidationError } from './config'\n\nexport type {\n ConfigError,\n ConfigFromEnvOptions,\n ConfigInput,\n ConfigIssue,\n ConfigOutput,\n ConfigSource,\n ConfigSourceOptions,\n ConfigValue,\n StandardSchemaV1\n} from './config'\n\nexport {\n IdGenerator,\n IdGeneratorExhaustedError,\n IdGeneratorLive,\n IdGeneratorTest,\n IdGeneratorTestLayer,\n IdGeneratorUnavailableError\n} from './id-generator'\n\nconst assertDelay = (milliseconds: number): void => {\n if (!Number.isFinite(milliseconds) || milliseconds < 0) {\n throw new RangeError('Delay must be a finite non-negative number')\n }\n}\n\nconst abortError = (): DOMException => new DOMException('The operation was aborted', 'AbortError')\n\nconst abortReason = (signal: AbortSignal): AbortSignal['reason'] =>\n signal.reason === undefined ? abortError() : signal.reason\n\n/** Optional cooperative cancellation for a host or test Clock sleep. */\nexport type ClockSleepOptions = {\n readonly signal?: AbortSignal\n}\n\n/** Host-backed time and waiting service. */\nexport class Clock extends Service<Clock>()('Clock') {\n now(): Date {\n return new Date()\n }\n\n /** Rejects with the signal reason, or an AbortError-compatible fallback. */\n sleep(milliseconds: number, options?: ClockSleepOptions): Promise<void> {\n assertDelay(milliseconds)\n const signal = options?.signal\n\n if (signal?.aborted) {\n return Promise.reject(abortReason(signal))\n }\n\n return new Promise<void>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined\n let settled = false\n\n function cleanup(): void {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n\n signal?.removeEventListener('abort', onAbort)\n }\n\n function settle(callback: () => void): void {\n if (settled) {\n return\n }\n\n settled = true\n cleanup()\n callback()\n }\n\n function onAbort(): void {\n if (signal !== undefined) {\n settle(() => reject(abortReason(signal)))\n }\n }\n\n try {\n signal?.addEventListener('abort', onAbort, { once: true })\n\n if (signal?.aborted) {\n onAbort()\n return\n }\n\n const handle = setTimeout(() => settle(resolve), milliseconds)\n\n if (settled) {\n clearTimeout(handle)\n } else {\n timer = handle\n }\n } catch (cause) {\n settle(() => reject(cause))\n }\n })\n }\n}\n\n/** The default host Clock provider. */\nexport const ClockLive = Layer.make(Clock)\n\ntype ClockWaiter = {\n readonly at: number\n readonly resolve: () => void\n readonly reject: (cause?: unknown) => void\n readonly signal: AbortSignal | undefined\n readonly onAbort: () => void\n settled: boolean\n}\n\n/** Limits deadline advances performed by `ClockTest.runAll`. */\nexport type ClockTestRunAllOptions = {\n readonly maxSteps?: number\n}\n\nconst defaultClockTestMaxSteps = 1_000\n\n/** Deterministic Clock implementation for tests. */\nexport class ClockTest implements Service.Contract<Clock> {\n private currentTime: number\n\n private readonly waiters: ClockWaiter[] = []\n\n constructor(initial: Date | number = 0) {\n this.currentTime = initial instanceof Date ? initial.getTime() : initial\n\n if (!Number.isFinite(this.currentTime)) {\n throw new RangeError('ClockTest time must be finite')\n }\n }\n\n /** Number of sleeps that have not been settled or cancelled. */\n get pendingSleeps(): number {\n return this.waiters.length\n }\n\n now(): Date {\n return new Date(this.currentTime)\n }\n\n /** Set an absolute time; moving backward leaves existing absolute deadlines unchanged. */\n setTime(value: Date | number): void {\n const next = value instanceof Date ? value.getTime() : value\n\n if (!Number.isFinite(next)) {\n throw new RangeError('ClockTest time must be finite')\n }\n\n this.currentTime = next\n this.flushWaiters()\n }\n\n advance(milliseconds: number): void {\n assertDelay(milliseconds)\n this.currentTime += milliseconds\n this.flushWaiters()\n }\n\n sleep(milliseconds: number, options?: ClockSleepOptions): Promise<void> {\n assertDelay(milliseconds)\n const signal = options?.signal\n\n if (signal?.aborted) {\n return Promise.reject(abortReason(signal))\n }\n\n return new Promise<void>((resolve, reject) => {\n let waiter!: ClockWaiter\n const onAbort = (): void => {\n if (signal !== undefined) {\n this.cancelWaiter(waiter, abortReason(signal))\n }\n }\n\n waiter = {\n at: this.currentTime + milliseconds,\n resolve,\n reject,\n signal,\n onAbort,\n settled: false\n }\n this.enqueueWaiter(waiter)\n\n try {\n signal?.addEventListener('abort', onAbort, { once: true })\n\n if (signal?.aborted) {\n this.cancelWaiter(waiter, abortReason(signal))\n return\n }\n } catch (cause) {\n this.cancelWaiter(waiter, cause)\n return\n }\n\n this.flushWaiters()\n })\n }\n\n /** Advance synchronously; due Promise continuations run after this method returns. */\n advanceToNext(): boolean {\n const next = this.waiters[0]\n\n if (next === undefined) {\n return false\n }\n\n this.currentTime = next.at\n this.flushWaiters()\n return true\n }\n\n /** Advance all pending deadlines, guarding at 1,000 steps by default. */\n async runAll(options?: ClockTestRunAllOptions): Promise<number> {\n const maxSteps = getMaxSteps(options)\n let steps = 0\n\n while (this.pendingSleeps > 0) {\n if (steps >= maxSteps) {\n throw new RangeError(`ClockTest.runAll exceeded maxSteps (${maxSteps})`)\n }\n\n this.advanceToNext()\n steps += 1\n await Promise.resolve()\n }\n\n return steps\n }\n\n static layer(initial: Date | number = 0) {\n return Layer.succeed(Clock, new ClockTest(initial))\n }\n\n private enqueueWaiter(waiter: ClockWaiter): void {\n const index = this.waiters.findIndex((pending) => pending.at > waiter.at)\n\n if (index === -1) {\n this.waiters.push(waiter)\n } else {\n this.waiters.splice(index, 0, waiter)\n }\n }\n\n private cancelWaiter(waiter: ClockWaiter, cause: unknown): void {\n const index = this.waiters.indexOf(waiter)\n\n if (index !== -1) {\n this.waiters.splice(index, 1)\n }\n\n this.finishWaiter(waiter, () => waiter.reject(cause))\n }\n\n private flushWaiters(): void {\n while (true) {\n const waiter = this.waiters[0]\n\n if (waiter === undefined || waiter.at > this.currentTime) {\n return\n }\n\n this.waiters.shift()\n this.finishWaiter(waiter, waiter.resolve)\n }\n }\n\n private finishWaiter(waiter: ClockWaiter, finish: () => void): void {\n if (waiter.settled) {\n return\n }\n\n waiter.settled = true\n waiter.signal?.removeEventListener('abort', waiter.onAbort)\n finish()\n }\n}\n\nconst getMaxSteps = (options?: ClockTestRunAllOptions): number => {\n const maxSteps = options?.maxSteps ?? defaultClockTestMaxSteps\n\n if (!Number.isInteger(maxSteps) || maxSteps < 0) {\n throw new RangeError('ClockTest.runAll maxSteps must be a finite non-negative integer')\n }\n\n return maxSteps\n}\n\nexport const ClockTestLayer = (initial: Date | number = 0) => ClockTest.layer(initial)\n\n/** Host-backed pseudo-random number service. */\nexport class Random extends Service<Random>()('Random') {\n next(): number {\n return Math.random()\n }\n\n nextInt(maxExclusive: number): number {\n if (!Number.isInteger(maxExclusive) || maxExclusive <= 0) {\n throw new RangeError('Random.nextInt maxExclusive must be a positive integer')\n }\n\n return Math.floor(this.next() * maxExclusive)\n }\n}\n\n/** The default host Random provider. */\nexport const RandomLive = Layer.make(Random)\n\n/** Reproducible pseudo-random implementation with isolated mutable state. */\nexport class RandomSeeded implements Service.Contract<Random> {\n private state: number\n\n constructor(seed: number) {\n if (!Number.isFinite(seed)) {\n throw new RangeError('RandomSeeded seed must be finite')\n }\n\n this.state = seed >>> 0\n }\n\n next(): number {\n this.state = (1664525 * this.state + 1013904223) >>> 0\n return this.state / 0x1_0000_0000\n }\n\n nextInt(maxExclusive: number): number {\n if (!Number.isInteger(maxExclusive) || maxExclusive <= 0) {\n throw new RangeError('RandomSeeded.nextInt maxExclusive must be a positive integer')\n }\n\n return Math.floor(this.next() * maxExclusive)\n }\n\n static layer(seed: number) {\n return Layer.succeed(Random, new RandomSeeded(seed))\n }\n}\n\nexport const RandomSeededLayer = (seed: number) => RandomSeeded.layer(seed)\n\nexport type LoggerLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport type LoggerEvent = {\n level: LoggerLevel\n message: string\n data?: LoggerData\n}\n\nexport type LoggerData =\n | string\n | number\n | boolean\n | bigint\n | null\n | readonly LoggerData[]\n | { readonly [key: string]: LoggerData }\ntype LoggerInput = LoggerEvent | LoggerLevel\n\nconst isLoggerLevel = (value: LoggerInput): value is LoggerLevel =>\n value === 'debug' || value === 'info' || value === 'warn' || value === 'error'\n\nconst toLoggerEvent = (input: LoggerInput, message?: string, data?: LoggerData): LoggerEvent => {\n if (!isLoggerLevel(input)) {\n return input\n }\n\n const event: LoggerEvent = { level: input, message: message ?? '' }\n\n if (data !== undefined) {\n event.data = data\n }\n\n return event\n}\n\n/** Structured host logger bridge. */\nexport class Logger extends Service<Logger>()('Logger') {\n log(event: LoggerEvent): void\n log(level: LoggerLevel, message: string, data?: LoggerData): void\n log(first: LoggerInput, message?: string, data?: LoggerData): void {\n const event = toLoggerEvent(first, message, data)\n\n const write = console[event.level]\n\n if (event.data === undefined) {\n write.call(console, event.message)\n } else {\n write.call(console, event.message, event.data)\n }\n }\n\n debug(message: string, data?: LoggerData): void {\n this.log('debug', message, data)\n }\n\n info(message: string, data?: LoggerData): void {\n this.log('info', message, data)\n }\n\n warn(message: string, data?: LoggerData): void {\n this.log('warn', message, data)\n }\n\n error(message: string, data?: LoggerData): void {\n this.log('error', message, data)\n }\n}\n\n/** The default host Logger provider. */\nexport const LoggerLive = Layer.make(Logger)\n\n/** Ordered in-memory Logger implementation for tests. */\nexport class LoggerTest implements Service.Contract<Logger> {\n readonly events: LoggerEvent[] = []\n\n log(event: LoggerEvent): void\n log(level: LoggerLevel, message: string, data?: LoggerData): void\n log(first: LoggerInput, message?: string, data?: LoggerData): void {\n const event = toLoggerEvent(first, message, data)\n\n this.events.push(event)\n }\n\n debug(message: string, data?: LoggerData): void {\n this.log('debug', message, data)\n }\n\n info(message: string, data?: LoggerData): void {\n this.log('info', message, data)\n }\n\n warn(message: string, data?: LoggerData): void {\n this.log('warn', message, data)\n }\n\n error(message: string, data?: LoggerData): void {\n this.log('error', message, data)\n }\n\n clear(): void {\n this.events.length = 0\n }\n\n static make() {\n const logger = new LoggerTest()\n return { logger, layer: Layer.succeed(Logger, logger) }\n }\n\n static layer(logger: LoggerTest = new LoggerTest()) {\n return Layer.succeed(Logger, logger)\n }\n}\n\nexport const LoggerTestLayer = () => LoggerTest.layer()\n\n/** Execution-local request value carried by a normal Service provider. */\nexport class CurrentRequest extends Service<CurrentRequest>()('CurrentRequest') {\n readonly request: unknown\n\n // oxlint-disable-next-line anti-slop/no-unknown-parameters\n constructor(readonly value: unknown) {\n super()\n this.request = value\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-parameters\n static layer(value: unknown) {\n return Layer.succeed(CurrentRequest, new CurrentRequest(value))\n }\n}\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters\nexport const CurrentRequestLayer = (value: unknown) => CurrentRequest.layer(value)\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { d as CurrentAbortSignal } from "./index-C7Qild0_.mjs";
2
- import { A as StandardSchemaV1, C as ConfigLive, D as ConfigSourceOptions, E as ConfigSourceError, O as ConfigValidationError, S as ConfigIssue, T as ConfigSource, _ as RandomSeededLayer, a as CurrentRequest, b as ConfigFromEnvOptions, c as LoggerData, d as LoggerLive, f as LoggerTest, g as RandomSeeded, h as RandomLive, i as ClockTestLayer, k as ConfigValue, l as LoggerEvent, m as Random, n as ClockLive, o as CurrentRequestLayer, p as LoggerTestLayer, r as ClockTest, s as Logger, t as Clock, u as LoggerLevel, v as Config, w as ConfigOutput, x as ConfigInput, y as ConfigError } from "./index-DStz0JMN.mjs";
3
- export { Clock, ClockLive, ClockTest, ClockTestLayer, Config, type ConfigError, type ConfigFromEnvOptions, type ConfigInput, type ConfigIssue, ConfigLive, type ConfigOutput, type ConfigSource, ConfigSourceError, type ConfigSourceOptions, ConfigValidationError, type ConfigValue, CurrentAbortSignal, CurrentRequest, CurrentRequestLayer, Logger, LoggerData, LoggerEvent, LoggerLevel, LoggerLive, LoggerTest, LoggerTestLayer, Random, RandomLive, RandomSeeded, RandomSeededLayer, type StandardSchemaV1 };
1
+ import { c as CurrentAbortSignal } from "./index-GYJ4qm9K.mjs";
2
+ import { A as ConfigIssue, C as IdGeneratorTest, D as ConfigError, E as Config, F as ConfigSourceOptions, I as ConfigValidationError, L as ConfigValue, M as ConfigOutput, N as ConfigSource, O as ConfigFromEnvOptions, P as ConfigSourceError, R as StandardSchemaV1, S as IdGeneratorLive, T as IdGeneratorUnavailableError, _ as RandomLive, a as ClockTestLayer, b as IdGenerator, c as CurrentRequestLayer, d as LoggerEvent, f as LoggerLevel, g as Random, h as LoggerTestLayer, i as ClockTest, j as ConfigLive, k as ConfigInput, l as Logger, m as LoggerTest, n as ClockLive, o as ClockTestRunAllOptions, p as LoggerLive, r as ClockSleepOptions, s as CurrentRequest, t as Clock, u as LoggerData, v as RandomSeeded, w as IdGeneratorTestLayer, x as IdGeneratorExhaustedError, y as RandomSeededLayer } from "./index-B1nH4luq.mjs";
3
+ export { Clock, ClockLive, ClockSleepOptions, ClockTest, ClockTestLayer, ClockTestRunAllOptions, Config, type ConfigError, type ConfigFromEnvOptions, type ConfigInput, type ConfigIssue, ConfigLive, type ConfigOutput, type ConfigSource, ConfigSourceError, type ConfigSourceOptions, ConfigValidationError, type ConfigValue, CurrentAbortSignal, CurrentRequest, CurrentRequestLayer, IdGenerator, IdGeneratorExhaustedError, IdGeneratorLive, IdGeneratorTest, IdGeneratorTestLayer, IdGeneratorUnavailableError, Logger, LoggerData, LoggerEvent, LoggerLevel, LoggerLive, LoggerTest, LoggerTestLayer, Random, RandomLive, RandomSeeded, RandomSeededLayer, type StandardSchemaV1 };
@@ -1,3 +1,3 @@
1
- import { t as CurrentAbortSignal } from "./signal-B97cs85Z.mjs";
2
- import { _ as ConfigSourceError, a as CurrentRequest, c as LoggerLive, d as Random, f as RandomLive, g as ConfigLive, h as Config, i as ClockTestLayer, l as LoggerTest, m as RandomSeededLayer, n as ClockLive, o as CurrentRequestLayer, p as RandomSeeded, r as ClockTest, s as Logger, t as Clock, u as LoggerTestLayer, v as ConfigValidationError } from "./standard-services-BFBq-4lo.mjs";
3
- export { Clock, ClockLive, ClockTest, ClockTestLayer, Config, ConfigLive, ConfigSourceError, ConfigValidationError, CurrentAbortSignal, CurrentRequest, CurrentRequestLayer, Logger, LoggerLive, LoggerTest, LoggerTestLayer, Random, RandomLive, RandomSeeded, RandomSeededLayer };
1
+ import { t as CurrentAbortSignal } from "./signal-D2UTcaaG.mjs";
2
+ import { C as ConfigSourceError, S as ConfigLive, _ as IdGeneratorLive, a as CurrentRequest, b as IdGeneratorUnavailableError, c as LoggerLive, d as Random, f as RandomLive, g as IdGeneratorExhaustedError, h as IdGenerator, i as ClockTestLayer, l as LoggerTest, m as RandomSeededLayer, n as ClockLive, o as CurrentRequestLayer, p as RandomSeeded, r as ClockTest, s as Logger, t as Clock, u as LoggerTestLayer, v as IdGeneratorTest, w as ConfigValidationError, x as Config, y as IdGeneratorTestLayer } from "./standard-services-GBZ32tll.mjs";
3
+ export { Clock, ClockLive, ClockTest, ClockTestLayer, Config, ConfigLive, ConfigSourceError, ConfigValidationError, CurrentAbortSignal, CurrentRequest, CurrentRequestLayer, IdGenerator, IdGeneratorExhaustedError, IdGeneratorLive, IdGeneratorTest, IdGeneratorTestLayer, IdGeneratorUnavailableError, Logger, LoggerLive, LoggerTest, LoggerTestLayer, Random, RandomLive, RandomSeeded, RandomSeededLayer };
@@ -1,7 +1,8 @@
1
1
  import { M as AnyService, c as RuntimeContextStorage } from "./index-heuaRmXR.mjs";
2
- import { A as OverrideLayerResult, C as CompleteExecutionLayer, D as LayerInputState, E as LayerInput, F as ValidateOverrides, I as ValidateOverridesWitness, M as RequiredEnvironment, N as ValidateLayerInput, R as MissingDependencies, S as CompleteExecution, _ as MapLayerBackend, c as RuntimeObserver, d as RuntimeServiceResolveEvent, i as RuntimeRunOptions, j as ProvidedEnvironment, l as RuntimeResourceReleaseEvent, n as RuntimeDisposeOptions, o as RuntimeExecutionEndEvent, r as RuntimeOptions, s as RuntimeExecutionStartEvent, u as RuntimeServiceAcquireEvent, v as LayerBackend } from "./index-rQhZk3Nt.mjs";
3
- import { f as Runtime } from "./index-C7Qild0_.mjs";
4
- import { f as LoggerTest, g as RandomSeeded, r as ClockTest } from "./index-DStz0JMN.mjs";
2
+ import { C as CompleteExecution, D as LayerInput, I as ValidateOverrides, L as ValidateOverridesWitness, M as ProvidedEnvironment, N as RequiredEnvironment, O as LayerInputState, P as ValidateLayerInput, d as RuntimeResourceReleaseEvent, f as RuntimeServiceAcquireEvent, i as RuntimeRunOptions, j as OverrideLayerResult, l as RuntimeExecutionStartEvent, n as RuntimeDisposeOptions, p as RuntimeServiceResolveEvent, r as RuntimeOptions, s as RuntimeExecutionEndEvent, u as RuntimeObserver, w as CompleteExecutionLayer, y as LayerBackend, z as MissingDependencies } from "./outcome-CB43bnFW.mjs";
3
+ import { t as MapLayerBackend } from "./index-BMdU_Z72.mjs";
4
+ import { l as Runtime } from "./index-GYJ4qm9K.mjs";
5
+ import { C as IdGeneratorTest, i as ClockTest, m as LoggerTest, v as RandomSeeded } from "./index-B1nH4luq.mjs";
5
6
  //#region src/testing/contracts.d.ts
6
7
  /** One runner-neutral conformance scenario. */
7
8
  interface ContractScenario {
@@ -95,6 +96,59 @@ declare class RecordedRuntimeObserver implements RuntimeObserver {
95
96
  private record;
96
97
  }
97
98
  //#endregion
99
+ //#region src/testing/runtime-graph-observer.d.ts
100
+ /** One logical Service node in a Runtime graph snapshot. */
101
+ type RuntimeGraphNode = {
102
+ readonly tag: string;
103
+ readonly resolutions: number;
104
+ readonly acquisitions: number;
105
+ readonly failures: number;
106
+ };
107
+ /** One observed parent-to-child Service edge in a Runtime graph snapshot. */
108
+ type RuntimeGraphEdge = {
109
+ readonly from: string;
110
+ readonly to: string;
111
+ readonly resolutions: number;
112
+ };
113
+ /** Detached, sorted and immutable Runtime graph data. */
114
+ type RuntimeGraphSnapshot = {
115
+ readonly nodes: readonly RuntimeGraphNode[];
116
+ readonly edges: readonly RuntimeGraphEdge[];
117
+ };
118
+ /** Options for a diagnostic Runtime graph observer. */
119
+ type RuntimeGraphObserverOptions = {
120
+ /** Count failed resolution and acquisition events in each node's failures field. */
121
+ readonly includeFailures?: boolean;
122
+ /** Add this label as a synthetic root in Mermaid output for top-level resolutions. */
123
+ readonly rootLabel?: string;
124
+ };
125
+ /**
126
+ * Observe the public Runtime resolution and acquisition events as a diagnostic graph.
127
+ *
128
+ * The graph is deliberately an observation of paths emitted by Runtime. It does
129
+ * not inspect Layer provenance, retain Service instances, or plan dependencies.
130
+ */
131
+ declare class RuntimeGraphObserver implements RuntimeObserver {
132
+ private readonly includeFailures;
133
+ private readonly rootLabel;
134
+ private readonly nodes;
135
+ private readonly edges;
136
+ private readonly rootEdges;
137
+ private constructor();
138
+ /** Create an empty Runtime graph observer. */
139
+ static make(options?: RuntimeGraphObserverOptions): RuntimeGraphObserver;
140
+ readonly onServiceResolve: (event: RuntimeServiceResolveEvent) => void;
141
+ readonly onServiceAcquire: (event: RuntimeServiceAcquireEvent) => void;
142
+ /** Return a fresh, deeply detached and sorted graph snapshot. */
143
+ toJSON(): RuntimeGraphSnapshot;
144
+ /** Render the current graph as deterministic Mermaid flowchart source. */
145
+ toMermaid(): string;
146
+ /** Discard all observed graph data while keeping this observer reusable. */
147
+ clear(): void;
148
+ private ensureNode;
149
+ private ensurePath;
150
+ }
151
+ //#endregion
98
152
  //#region src/testing/test-runtime.d.ts
99
153
  declare const TestRuntimeOptionsOverridesTypeId: unique symbol;
100
154
  type ValidatedTestServiceOption<Base extends LayerInput, Overrides extends readonly LayerInput[], Replacement extends LayerInput, Value> = Value & ValidateOverridesWitness<Base, readonly [...Overrides, Replacement]>;
@@ -110,6 +164,8 @@ type TestRuntimeOptions<Base extends LayerInput = LayerInput, Overrides extends
110
164
  readonly logger?: ValidatedTestServiceOption<Base, Overrides, LoggerTestLayer, LoggerTest>;
111
165
  /** Seeded Random provider to install for this TestRuntime. */
112
166
  readonly random?: ValidatedTestServiceOption<Base, Overrides, RandomSeededLayer, RandomSeeded>;
167
+ /** Deterministic IdGenerator provider to install for this TestRuntime. */
168
+ readonly idGenerator?: ValidatedTestServiceOption<Base, Overrides, IdGeneratorTestLayer, IdGeneratorTest>;
113
169
  /** Additional best-effort observers after the default recorder. */
114
170
  readonly observers?: readonly RuntimeObserver[];
115
171
  } & {
@@ -118,20 +174,22 @@ type TestRuntimeOptions<Base extends LayerInput = LayerInput, Overrides extends
118
174
  type ClockTestLayer = ReturnType<typeof ClockTest.layer>;
119
175
  type LoggerTestLayer = ReturnType<typeof LoggerTest.layer>;
120
176
  type RandomSeededLayer = ReturnType<typeof RandomSeeded.layer>;
177
+ type IdGeneratorTestLayer = ReturnType<typeof IdGeneratorTest.layer>;
121
178
  type TestRuntimeOptionsInput = Omit<RuntimeOptions, 'backend' | 'observers'> & {
122
179
  readonly overrides?: readonly LayerInput[];
123
180
  readonly backend?: LayerBackend;
124
181
  readonly clock?: ClockTest;
125
182
  readonly logger?: LoggerTest;
126
183
  readonly random?: RandomSeeded;
184
+ readonly idGenerator?: IdGeneratorTest;
127
185
  readonly observers?: readonly RuntimeObserver[];
128
186
  };
129
187
  type ExplicitOverrides<Options extends TestRuntimeOptionsInput> = typeof TestRuntimeOptionsOverridesTypeId extends keyof Options ? Exclude<Options[typeof TestRuntimeOptionsOverridesTypeId], undefined> extends (infer Overrides extends readonly LayerInput[]) ? Overrides : readonly [] : Exclude<Options['overrides'], undefined> extends (infer Overrides extends readonly LayerInput[]) ? Overrides : readonly [];
130
188
  type HasDefinedOption<Options, Key extends PropertyKey> = true extends (Options extends unknown ? Key extends keyof Options ? {} extends Pick<Options, Key> ? false : undefined extends Options[Key] ? false : true : false : never) ? true : false;
131
189
  type HasPotentialOption<Options, Key extends PropertyKey> = true extends (Options extends unknown ? Key extends keyof Options ? [Exclude<Options[Key], undefined>] extends [never] ? false : true : false : never) ? true : false;
132
190
  type HasGuaranteedOption<Options, Key extends PropertyKey> = [Options] extends [never] ? false : false extends (Options extends unknown ? HasDefinedOption<Options, Key> : never) ? false : true;
133
- type TestRuntimeOptionLayers<Options extends TestRuntimeOptionsInput> = [...ExplicitOverrides<Options>, ...(HasGuaranteedOption<Options, 'clock'> extends true ? [ClockTestLayer] : []), ...(HasGuaranteedOption<Options, 'logger'> extends true ? [LoggerTestLayer] : []), ...(HasGuaranteedOption<Options, 'random'> extends true ? [RandomSeededLayer] : [])];
134
- type TestRuntimeValidationLayers<Options extends TestRuntimeOptionsInput> = [...ExplicitOverrides<Options>, ...(HasPotentialOption<Options, 'clock'> extends true ? [ClockTestLayer] : []), ...(HasPotentialOption<Options, 'logger'> extends true ? [LoggerTestLayer] : []), ...(HasPotentialOption<Options, 'random'> extends true ? [RandomSeededLayer] : [])];
191
+ type TestRuntimeOptionLayers<Options extends TestRuntimeOptionsInput> = [...ExplicitOverrides<Options>, ...(HasGuaranteedOption<Options, 'clock'> extends true ? [ClockTestLayer] : []), ...(HasGuaranteedOption<Options, 'logger'> extends true ? [LoggerTestLayer] : []), ...(HasGuaranteedOption<Options, 'random'> extends true ? [RandomSeededLayer] : []), ...(HasGuaranteedOption<Options, 'idGenerator'> extends true ? [IdGeneratorTestLayer] : [])];
192
+ type TestRuntimeValidationLayers<Options extends TestRuntimeOptionsInput> = [...ExplicitOverrides<Options>, ...(HasPotentialOption<Options, 'clock'> extends true ? [ClockTestLayer] : []), ...(HasPotentialOption<Options, 'logger'> extends true ? [LoggerTestLayer] : []), ...(HasPotentialOption<Options, 'random'> extends true ? [RandomSeededLayer] : []), ...(HasPotentialOption<Options, 'idGenerator'> extends true ? [IdGeneratorTestLayer] : [])];
135
193
  type TestRuntimeLayer<Base extends LayerInput, Options extends TestRuntimeOptionsInput> = OverrideLayerResult<Base, TestRuntimeOptionLayers<Options>>;
136
194
  type TestRuntimeOptionsValidation<Base extends LayerInput, Options extends TestRuntimeOptionsInput> = (Options extends {
137
195
  readonly overrides?: infer Overrides extends readonly LayerInput[];
@@ -162,6 +220,7 @@ declare class TestRuntime<Provided extends AnyService = any, Options extends obj
162
220
  readonly clock: ConfiguredService<Options, 'clock', ClockTest>;
163
221
  readonly logger: ConfiguredService<Options, 'logger', LoggerTest>;
164
222
  readonly random: ConfiguredService<Options, 'random', RandomSeeded>;
223
+ readonly idGenerator: ConfiguredService<Options, 'idGenerator', IdGeneratorTest>;
165
224
  private disposalPromise;
166
225
  private constructor();
167
226
  /** Create a TestRuntime with a fresh backend and default lifecycle recorder. */
@@ -194,5 +253,5 @@ declare namespace TestRuntime {
194
253
  type DisposeOptions = RuntimeDisposeOptions;
195
254
  }
196
255
  //#endregion
197
- export { ClockTest, ConformanceError, type ContextConcurrency, type ContractScenario, type LayerBackendAcquisitionFailure, type LayerBackendContractOptions, LoggerTest, MapLayerBackend as MemoryLayerBackend, RandomSeeded, RecordedRuntimeObserver, type RecordedRuntimeObserverSnapshot, type RuntimeContextStorageContractOptions, RuntimeObserver, type RuntimeObserverEvent, TestRuntime, TestRuntimeObserverError, type TestRuntimeOptions, layerBackendContract, runtimeContextStorageContract };
256
+ export { ClockTest, ConformanceError, type ContextConcurrency, type ContractScenario, IdGeneratorTest, type LayerBackendAcquisitionFailure, type LayerBackendContractOptions, LoggerTest, MapLayerBackend as MemoryLayerBackend, RandomSeeded, RecordedRuntimeObserver, type RecordedRuntimeObserverSnapshot, type RuntimeContextStorageContractOptions, type RuntimeGraphEdge, type RuntimeGraphNode, RuntimeGraphObserver, type RuntimeGraphObserverOptions, type RuntimeGraphSnapshot, RuntimeObserver, type RuntimeObserverEvent, TestRuntime, TestRuntimeObserverError, type TestRuntimeOptions, layerBackendContract, runtimeContextStorageContract };
198
257
  //# sourceMappingURL=testing.d.mts.map