@dxos/effect 0.10.0 → 0.11.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 (36) hide show
  1. package/dist/lib/index.mjs +919 -0
  2. package/dist/lib/index.mjs.map +1 -0
  3. package/dist/lib/testing.mjs +23 -0
  4. package/dist/lib/testing.mjs.map +1 -0
  5. package/dist/types/src/EffectEx.d.ts +1 -1
  6. package/dist/types/src/EffectEx.d.ts.map +1 -1
  7. package/dist/types/src/RuntimeProvider.d.ts +6 -0
  8. package/dist/types/src/RuntimeProvider.d.ts.map +1 -1
  9. package/dist/types/src/atom-kvs.d.ts +7 -1
  10. package/dist/types/src/atom-kvs.d.ts.map +1 -1
  11. package/dist/types/src/index.d.ts +1 -0
  12. package/dist/types/src/index.d.ts.map +1 -1
  13. package/dist/types/src/internal/errors.d.ts +0 -5
  14. package/dist/types/src/internal/errors.d.ts.map +1 -1
  15. package/dist/types/tsconfig.tsbuildinfo +1 -1
  16. package/package.json +15 -17
  17. package/src/EffectEx.ts +1 -9
  18. package/src/RuntimeProvider.ts +8 -0
  19. package/src/atom-kvs.ts +7 -1
  20. package/src/index.ts +1 -0
  21. package/src/internal/errors.ts +0 -16
  22. package/src/otel.test.ts +1 -1
  23. package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
  24. package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
  25. package/dist/lib/browser/index.mjs +0 -849
  26. package/dist/lib/browser/index.mjs.map +0 -7
  27. package/dist/lib/browser/meta.json +0 -1
  28. package/dist/lib/browser/testing.mjs +0 -31
  29. package/dist/lib/browser/testing.mjs.map +0 -7
  30. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  31. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  32. package/dist/lib/node-esm/index.mjs +0 -849
  33. package/dist/lib/node-esm/index.mjs.map +0 -7
  34. package/dist/lib/node-esm/meta.json +0 -1
  35. package/dist/lib/node-esm/testing.mjs +0 -31
  36. package/dist/lib/node-esm/testing.mjs.map +0 -7
@@ -0,0 +1,919 @@
1
+ import { Atom } from "@effect-atom/atom";
2
+ import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore";
3
+ import * as Context$1 from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Exit from "effect/Exit";
6
+ import * as Option from "effect/Option";
7
+ import * as Runtime from "effect/Runtime";
8
+ import * as Cause from "effect/Cause";
9
+ import * as Chunk from "effect/Chunk";
10
+ import * as GlobalValue from "effect/GlobalValue";
11
+ import * as Function from "effect/Function";
12
+ import { pipe } from "effect/Function";
13
+ import * as Layer from "effect/Layer";
14
+ import * as Predicate from "effect/Predicate";
15
+ import * as Tracer$1 from "effect/Tracer";
16
+ import { Context } from "@dxos/context";
17
+ import * as Resource from "@effect/opentelemetry/Resource";
18
+ import * as Tracer from "@effect/opentelemetry/Tracer";
19
+ import { trace } from "@opentelemetry/api";
20
+ import * as Schema from "effect/Schema";
21
+ import * as SchemaAST from "effect/SchemaAST";
22
+ import { invariant } from "@dxos/invariant";
23
+ import { decamelize, getDeep, isNonNullable, setDeep } from "@dxos/util";
24
+ import { JSONPath } from "jsonpath-plus";
25
+ //#region \0rolldown/runtime.js
26
+ var __defProp = Object.defineProperty;
27
+ var __exportAll = (all, no_symbols) => {
28
+ let target = {};
29
+ for (var name in all) __defProp(target, name, {
30
+ get: all[name],
31
+ enumerable: true
32
+ });
33
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
34
+ return target;
35
+ };
36
+ //#endregion
37
+ //#region src/atom-kvs.ts
38
+ var defaultRuntime = Atom.runtime(BrowserKeyValueStore.layerLocalStorage);
39
+ /**
40
+ * Creates a KVS-backed atom for structured settings using Atom.kvs.
41
+ * The entire object is stored as a single localStorage key with JSON serialization.
42
+ *
43
+ * @param options.key - The localStorage key to store the value under.
44
+ * @param options.schema - Effect Schema for the value type.
45
+ * @param options.defaultValue - Function returning the default value.
46
+ * @param options.runtime - Optional custom Atom runtime (defaults to localStorage).
47
+ * @returns A writable atom that persists to localStorage.
48
+ *
49
+ * @idiom org.dxos.effect.kvsStore
50
+ * applies: Persisting a plugin's settings/config as one schema-validated localStorage blob (a global user preference set infrequently)
51
+ * instead-of: hand-rolled localStorage reads/writes, or scattering per-field atoms, or using this for per-context UI state
52
+ * uses: {@link createKvsStore}
53
+ * related: org.dxos.react-ui-attention.viewState
54
+ */
55
+ var createKvsStore = (options) => {
56
+ const runtime = options.runtime ?? defaultRuntime;
57
+ return Atom.kvs({
58
+ runtime,
59
+ key: options.key,
60
+ schema: options.schema,
61
+ defaultValue: options.defaultValue
62
+ }).pipe(Atom.keepAlive);
63
+ };
64
+ //#endregion
65
+ //#region src/internal/errors.ts
66
+ var spanSymbol = Symbol.for("effect/SpanAnnotation");
67
+ var spanToTrace = GlobalValue.globalValue("effect/Tracer/spanToTrace", () => /* @__PURE__ */ new WeakMap());
68
+ var locationRegex = /\((.*)\)/g;
69
+ /**
70
+ * Adds effect spans.
71
+ * Removes effect internal functions.
72
+ * Unwraps error proxy.
73
+ */
74
+ var prettyErrorStack = (error, appendStacks = []) => {
75
+ if (typeof error !== "object" || error === null) return error;
76
+ const span = error[spanSymbol];
77
+ const lines = typeof error.stack === "string" ? error.stack.split("\n") : [];
78
+ const out = [];
79
+ let atStack = false, inCore = false, passedScheduler = false;
80
+ for (let i = 0; i < lines.length; i++) {
81
+ if (!atStack && !lines[i].startsWith(" at ")) {
82
+ out.push(lines[i]);
83
+ continue;
84
+ }
85
+ atStack = true;
86
+ if (lines[i].includes(" at new BaseEffectError") || lines[i].includes(" at new YieldableError")) {
87
+ i++;
88
+ continue;
89
+ }
90
+ if (lines[i].includes("Generator.next")) break;
91
+ if (lines[i].includes("effect_internal_function")) break;
92
+ const filename = lines[i].match(/\/([a-zA-Z0-9_\-.]+):\d+:\d+\)$/)?.[1];
93
+ if (!inCore && ["core-effect.ts"].includes(filename)) inCore = true;
94
+ if (inCore && !passedScheduler && ["Scheduler.ts"].includes(filename)) {
95
+ passedScheduler = true;
96
+ continue;
97
+ }
98
+ if (passedScheduler && !["Scheduler.ts"].includes(filename)) inCore = false;
99
+ if (inCore) continue;
100
+ out.push(lines[i].replace(/at .*effect_instruction_i.*\((.*)\)/, "at $1").replace(/EffectPrimitive\.\w+/, "<anonymous>").replace(/at Arguments\./, "at "));
101
+ }
102
+ if (span) {
103
+ let current = span;
104
+ let i = 0;
105
+ while (current && current._tag === "Span" && i < 10) {
106
+ const stackFn = spanToTrace.get(current);
107
+ if (typeof stackFn === "function") {
108
+ const stack = stackFn();
109
+ if (typeof stack === "string") {
110
+ const locationMatchAll = stack.matchAll(locationRegex);
111
+ let match = false;
112
+ for (const [, location] of locationMatchAll) {
113
+ match = true;
114
+ out.push(` at ${current.name} (${location})`);
115
+ }
116
+ if (!match) out.push(` at ${current.name} (${stack.replace(/^at /, "")})`);
117
+ } else out.push(` at ${current.name}`);
118
+ } else out.push(` at ${current.name}`);
119
+ current = Option.getOrUndefined(current.parent);
120
+ i++;
121
+ }
122
+ }
123
+ out.push(...appendStacks);
124
+ error = Cause.originalError(error);
125
+ if (error.cause) error.cause = prettyErrorStack(error.cause);
126
+ Object.defineProperty(error, "stack", {
127
+ value: out.join("\n"),
128
+ writable: true,
129
+ enumerable: false,
130
+ configurable: true
131
+ });
132
+ return error;
133
+ };
134
+ /**
135
+ * Converts a cause to an error.
136
+ * Inserts effect spans as stack frames.
137
+ * The error will have stack frames of where the effect was run (if stack trace limit allows).
138
+ * Removes effect runtime internal stack frames.
139
+ *
140
+ * To be used in place of `Effect.runPromise`.
141
+ *
142
+ * @throws AggregateError if there are multiple errors.
143
+ */
144
+ var causeToError = (cause) => {
145
+ if (Cause.isEmpty(cause)) return /* @__PURE__ */ new Error("Fiber failed without a cause");
146
+ else if (Cause.isInterruptedOnly(cause)) return /* @__PURE__ */ new Error("Fiber was interrupted");
147
+ else {
148
+ const errors = [...Chunk.toArray(Cause.failures(cause)), ...Chunk.toArray(Cause.defects(cause))];
149
+ const getStackFrames = () => {
150
+ const err = /* @__PURE__ */ new Error();
151
+ Error.captureStackTrace(err, causeToError);
152
+ return err.stack.split("\n").slice(1);
153
+ };
154
+ const stackFrames = getStackFrames();
155
+ const newErrors = errors.map((error) => prettyErrorStack(error, stackFrames));
156
+ if (newErrors.length === 1) return newErrors[0];
157
+ else return new AggregateError(newErrors);
158
+ }
159
+ };
160
+ /**
161
+ * Throws an error based on the cause.
162
+ * Inserts effect spans as stack frames.
163
+ * The error will have stack frames of where the effect was run (if stack trace limit allows).
164
+ * Removes effect runtime internal stack frames.
165
+ *
166
+ * To be used in place of `Effect.runPromise`.
167
+ *
168
+ * @throws AggregateError if there are multiple errors.
169
+ */
170
+ var throwCause = (cause) => {
171
+ throw causeToError(cause);
172
+ };
173
+ var unwrapExit = (exit) => {
174
+ if (Exit.isSuccess(exit)) return exit.value;
175
+ return throwCause(exit.cause);
176
+ };
177
+ /**
178
+ * Runs the embedded effect asynchronously and throws any failures and defects as errors.
179
+ * Inserts effect spans as stack frames.
180
+ * The error will have stack frames of where the effect was run (if stack trace limit allows).
181
+ * Removes effect runtime internal stack frames.
182
+ *
183
+ * To be used in place of `Effect.runPromise`.
184
+ *
185
+ * @throws AggregateError if there are multiple errors.
186
+ */
187
+ var runAndForwardErrors = async (effect, options) => {
188
+ return unwrapExit(await Effect.runPromiseExit(effect, options));
189
+ };
190
+ /** Alias for {@link runAndForwardErrors} — preferred name when accessed via `EffectEx.runPromise`. */
191
+ var runPromise$1 = runAndForwardErrors;
192
+ /**
193
+ * Runs the embedded effect asynchronously and throws any failures and defects as errors.
194
+ */
195
+ var runInRuntime = (...args) => {
196
+ if (args.length === 1) {
197
+ const [runtime] = args;
198
+ return async (effect, options) => {
199
+ return unwrapExit(await Runtime.runPromiseExit(runtime, effect, options));
200
+ };
201
+ } else {
202
+ const [runtime, effect, options] = args;
203
+ return (async () => {
204
+ return unwrapExit(await Runtime.runPromiseExit(runtime, effect, options));
205
+ })();
206
+ }
207
+ };
208
+ //#endregion
209
+ //#region src/dynamic-runtime.ts
210
+ var dynamic_runtime_exports = /* @__PURE__ */ __exportAll({ make: () => make });
211
+ /**
212
+ * Validate that all required tags are present in the runtime context.
213
+ */
214
+ var validateTags = (context, tags) => Effect.gen(function* () {
215
+ const missingTags = [];
216
+ for (const tag of tags) {
217
+ const option = Context$1.getOption(context, tag);
218
+ if (Option.isNone(option)) missingTags.push(tag.key);
219
+ }
220
+ if (missingTags.length > 0) return yield* Effect.die(/* @__PURE__ */ new Error(`Missing required tags in runtime: ${missingTags.join(", ")}`));
221
+ });
222
+ /**
223
+ * Create a dynamic runtime from a managed runtime and validate required tags.
224
+ */
225
+ function make(managedRuntime, tags) {
226
+ const managedRuntimeAny = managedRuntime;
227
+ let cachedRuntime;
228
+ let validatedRuntimePromise;
229
+ const getValidatedRuntimeAsync = async () => {
230
+ if (!validatedRuntimePromise) validatedRuntimePromise = managedRuntimeAny.runPromise(Effect.gen(function* () {
231
+ const rt = yield* managedRuntimeAny.runtimeEffect;
232
+ yield* validateTags(rt.context, tags);
233
+ return rt;
234
+ }));
235
+ return validatedRuntimePromise;
236
+ };
237
+ const getValidatedRuntime = () => {
238
+ return unwrapExit(managedRuntimeAny.runSyncExit(Effect.gen(function* () {
239
+ const rt = yield* managedRuntimeAny.runtimeEffect;
240
+ yield* validateTags(rt.context, tags);
241
+ return rt;
242
+ })));
243
+ };
244
+ return {
245
+ managedRuntime: managedRuntimeAny,
246
+ runPromise: async (effect) => {
247
+ const runtime = await getValidatedRuntimeAsync();
248
+ return Runtime.runPromise(runtime)(effect);
249
+ },
250
+ runSync: (effect) => {
251
+ const runtime = getValidatedRuntime();
252
+ return Runtime.runSync(runtime)(effect);
253
+ },
254
+ runSyncExit: (effect) => {
255
+ const validationExit = managedRuntimeAny.runSyncExit(Effect.gen(function* () {
256
+ const rt = yield* managedRuntimeAny.runtimeEffect;
257
+ yield* validateTags(rt.context, tags);
258
+ return rt;
259
+ }));
260
+ if (Exit.isSuccess(validationExit)) {
261
+ const runtime = validationExit.value;
262
+ return Runtime.runSyncExit(runtime)(effect);
263
+ }
264
+ return validationExit;
265
+ },
266
+ runPromiseExit: async (effect) => {
267
+ try {
268
+ const runtime = await getValidatedRuntimeAsync();
269
+ return Runtime.runPromiseExit(runtime)(effect);
270
+ } catch (error) {
271
+ return Exit.die(error);
272
+ }
273
+ },
274
+ runFork: (effect) => {
275
+ const runtime = getValidatedRuntime();
276
+ return Runtime.runFork(runtime)(effect);
277
+ },
278
+ runtimeEffect: Effect.gen(function* () {
279
+ if (cachedRuntime) return cachedRuntime;
280
+ const rt = yield* managedRuntimeAny.runtimeEffect;
281
+ yield* validateTags(rt.context, tags);
282
+ const runtime = rt;
283
+ cachedRuntime = runtime;
284
+ return runtime;
285
+ }).pipe(Effect.catchAll(() => Effect.die(/* @__PURE__ */ new Error("Unexpected error in runtimeEffect validation")))),
286
+ dispose: async () => {
287
+ await managedRuntimeAny.dispose();
288
+ }
289
+ };
290
+ }
291
+ //#endregion
292
+ //#region src/internal/async-task-tagging.ts
293
+ var runInTask = Symbol("runInTask");
294
+ /**
295
+ * Traces effect frames using console.createTask so that the proper stack-trace is visible in Chrome Devtools debugger.
296
+ */
297
+ var asyncTaskTaggingLayer = () => {
298
+ if (Predicate.hasProperty(console, "createTask") === false) return Layer.empty;
299
+ return pipe(Effect.gen(function* () {
300
+ const oldTracer = yield* Effect.tracer;
301
+ return Tracer$1.make({
302
+ span: (name, ...args) => {
303
+ const span = oldTracer.span(name, ...args);
304
+ const trace = console.createTask(name);
305
+ span[runInTask] = (f) => trace.run(f);
306
+ return span;
307
+ },
308
+ context: (f, fiber) => {
309
+ const maybeParentSpan = Context$1.getOption(Tracer$1.ParentSpan)(fiber.currentContext);
310
+ if (maybeParentSpan._tag === "None") return oldTracer.context(f, fiber);
311
+ const parentSpan = maybeParentSpan.value;
312
+ if (parentSpan._tag === "ExternalSpan") return oldTracer.context(f, fiber);
313
+ const span = parentSpan;
314
+ if (runInTask in span && typeof span[runInTask] === "function") return span[runInTask](() => oldTracer.context(f, fiber));
315
+ return oldTracer.context(f, fiber);
316
+ }
317
+ });
318
+ }), Effect.map(Layer.setTracer), Layer.unwrapEffect);
319
+ };
320
+ //#endregion
321
+ //#region src/internal/context.ts
322
+ var __dxlog_file$2 = "/__w/dxos/dxos/packages/common/effect/src/internal/context.ts";
323
+ var contextFromScope = () => Effect.gen(function* () {
324
+ const ctx = new Context(void 0, {
325
+ "~LogMeta": "~LogMeta",
326
+ F: __dxlog_file$2,
327
+ L: 13
328
+ });
329
+ yield* Effect.addFinalizer(() => Effect.promise(() => ctx.dispose()));
330
+ return ctx;
331
+ });
332
+ //#endregion
333
+ //#region src/internal/resource.ts
334
+ /**
335
+ * Acquires a resource and releases it when the scope is closed.
336
+ */
337
+ var acquireReleaseResource = (getResource) => Effect.acquireRelease(Effect.gen(function* () {
338
+ const resource = getResource();
339
+ yield* Effect.promise(async () => {
340
+ await resource.open?.();
341
+ });
342
+ return resource;
343
+ }), (resource) => Effect.promise(async () => {
344
+ await resource.close?.();
345
+ }));
346
+ //#endregion
347
+ //#region src/EffectEx.ts
348
+ var EffectEx_exports = /* @__PURE__ */ __exportAll({
349
+ acquireReleaseResource: () => acquireReleaseResource,
350
+ asyncTaskTaggingLayer: () => asyncTaskTaggingLayer,
351
+ causeToError: () => causeToError,
352
+ contextFromScope: () => contextFromScope,
353
+ runAndForwardErrors: () => runAndForwardErrors,
354
+ runInRuntime: () => runInRuntime,
355
+ runPromise: () => runPromise$1,
356
+ throwCause: () => throwCause,
357
+ unwrapExit: () => unwrapExit
358
+ });
359
+ //#endregion
360
+ //#region src/otel.ts
361
+ var layerOtel = (evaluate) => Layer.unwrapEffect(Effect.map(Effect.isEffect(evaluate) ? evaluate : Effect.sync(evaluate), (config) => {
362
+ const ResourceLive = Resource.layerFromEnv(config.resource && Resource.configToAttributes(config.resource));
363
+ const provider = trace.getTracerProvider();
364
+ const TracerLive = Layer.provide(Tracer.layer, Layer.succeed(Tracer.OtelTracerProvider, provider));
365
+ const MetricsLive = Layer.empty;
366
+ const LoggerLive = Layer.empty;
367
+ return Layer.mergeAll(TracerLive, MetricsLive, LoggerLive).pipe(Layer.provideMerge(ResourceLive));
368
+ }));
369
+ //#endregion
370
+ //#region src/Performance.ts
371
+ var Performance_exports = /* @__PURE__ */ __exportAll({ addTrackEntry: () => addTrackEntry });
372
+ /**
373
+ * Puts the effect span on the performance timeline in DevTools.
374
+ */
375
+ var addTrackEntry = (options) => (effect) => Effect.gen(function* () {
376
+ const start = performance.now();
377
+ const exit = yield* Effect.exit(effect);
378
+ const resolvedOptions = typeof options === "function" ? options(exit) : options;
379
+ performance.measure(resolvedOptions.name, {
380
+ start,
381
+ detail: {
382
+ ...resolvedOptions.detail,
383
+ devtools: resolvedOptions.devtools
384
+ }
385
+ });
386
+ return yield* exit;
387
+ });
388
+ //#endregion
389
+ //#region src/RuntimeProvider.ts
390
+ var RuntimeProvider_exports = /* @__PURE__ */ __exportAll({
391
+ currentRuntime: () => currentRuntime,
392
+ provide: () => provide,
393
+ runPromise: () => runPromise,
394
+ toLayer: () => toLayer
395
+ });
396
+ /**
397
+ * Bridges a runtime provider into a {@link Layer} exposing its services, so a stack that resolves
398
+ * dependencies via `RuntimeProvider.currentRuntime` can be provided from an existing runtime.
399
+ */
400
+ var toLayer = (provider) => Layer.effectContext(Effect.map(provider, (runtime) => runtime.context));
401
+ /**
402
+ * @returns Runtime provider from the current context.
403
+ */
404
+ var currentRuntime = () => Effect.runtime().pipe(Effect.map(Effect.succeed));
405
+ /**
406
+ * Run effect, within runitme, clean errors and fix stack-traces.
407
+ */
408
+ var runPromise = (provider) => async (effect) => {
409
+ const runtime = await runAndForwardErrors(provider);
410
+ return unwrapExit(await effect.pipe(Runtime.runPromiseExit(runtime)));
411
+ };
412
+ /**
413
+ * Provide services from runtime provider to effect.
414
+ */
415
+ var provide = (runtimeProvider) => (effect) => Effect.flatMap(runtimeProvider, (runtime) => Effect.provide(effect, runtime));
416
+ //#endregion
417
+ //#region src/internal/ast.ts
418
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/common/effect/src/internal/ast.ts";
419
+ /**
420
+ * Unwraps and collects refinement filters.
421
+ */
422
+ var reduceRefinements = (type, refinements = []) => {
423
+ if (SchemaAST.isRefinement(type)) {
424
+ const filter = type.filter;
425
+ return reduceRefinements({
426
+ ...type.from,
427
+ annotations: {
428
+ ...type.from.annotations,
429
+ ...type.annotations
430
+ }
431
+ }, [...refinements, filter]);
432
+ }
433
+ return {
434
+ type,
435
+ refinements
436
+ };
437
+ };
438
+ /**
439
+ * Get the base type of a property.
440
+ *
441
+ * Unwraps refinements and optional unions.
442
+ */
443
+ var getBaseType = (prop) => {
444
+ const encoded = SchemaAST.encodedBoundAST(prop.type);
445
+ return reduceRefinements(prop.isOptional && encoded._tag === "Union" ? encoded.types[0] : encoded);
446
+ };
447
+ /**
448
+ * Get the property types of an AST.
449
+ */
450
+ var getProperties = (ast) => {
451
+ return SchemaAST.getPropertySignatures(ast).map((prop) => {
452
+ const { type, refinements } = getBaseType(prop);
453
+ return {
454
+ type: prop.annotations && Reflect.ownKeys(prop.annotations).length > 0 ? {
455
+ ...type,
456
+ annotations: {
457
+ ...type.annotations,
458
+ ...prop.annotations
459
+ }
460
+ } : type,
461
+ refinements,
462
+ name: prop.name,
463
+ isOptional: prop.isOptional,
464
+ isReadonly: prop.isReadonly
465
+ };
466
+ });
467
+ };
468
+ var VisitResult = /* @__PURE__ */ function(VisitResult) {
469
+ VisitResult[VisitResult["CONTINUE"] = 0] = "CONTINUE";
470
+ /**
471
+ * Skip visiting children.
472
+ */
473
+ VisitResult[VisitResult["SKIP"] = 1] = "SKIP";
474
+ /**
475
+ * Stop traversing immediately.
476
+ */
477
+ VisitResult[VisitResult["EXIT"] = 2] = "EXIT";
478
+ return VisitResult;
479
+ }({});
480
+ /**
481
+ * Visit leaf nodes.
482
+ * Refs:
483
+ * - https://github.com/syntax-tree/unist-util-visit?tab=readme-ov-file#visitor
484
+ * - https://github.com/syntax-tree/unist-util-is?tab=readme-ov-file#test
485
+ */
486
+ var visit = (node, testOrVisitor, visitor) => {
487
+ visitNode(node, testOrVisitor, visitor);
488
+ };
489
+ var visitNode = (node, test, visitor, path = [], depth = 0) => {
490
+ const $result = test?.(node, path, depth);
491
+ const result = $result === void 0 ? 0 : typeof $result === "boolean" ? $result ? 0 : 1 : $result;
492
+ if (result === 2) return result;
493
+ if (result !== 1) visitor(node, path, depth);
494
+ if (SchemaAST.isTypeLiteral(node)) for (const prop of SchemaAST.getPropertySignatures(node)) {
495
+ const currentPath = [...path, prop.name.toString()];
496
+ const result = visitNode(prop.type, test, visitor, currentPath, depth + 1);
497
+ if (result === 2) return result;
498
+ }
499
+ else if (SchemaAST.isTupleType(node)) for (const [i, element] of node.elements.entries()) {
500
+ const currentPath = [...path, i];
501
+ const result = visitNode(element.type, test, visitor, currentPath, depth);
502
+ if (result === 2) return result;
503
+ }
504
+ else if (SchemaAST.isUnion(node)) for (const type of node.types) {
505
+ const result = visitNode(type, test, visitor, path, depth);
506
+ if (result === 2) return result;
507
+ }
508
+ else if (SchemaAST.isRefinement(node)) {
509
+ const result = visitNode(node.from, test, visitor, path, depth);
510
+ if (result === 2) return result;
511
+ }
512
+ };
513
+ /**
514
+ * Recursively descend into AST to find first node that passes the test.
515
+ */
516
+ var findNode = (node, test) => {
517
+ if (test(node)) return node;
518
+ else if (SchemaAST.isTypeLiteral(node)) {
519
+ for (const prop of SchemaAST.getPropertySignatures(node)) {
520
+ const child = findNode(prop.type, test);
521
+ if (child) return child;
522
+ }
523
+ for (const prop of getIndexSignatures(node)) {
524
+ const child = findNode(prop.type, test);
525
+ if (child) return child;
526
+ }
527
+ } else if (SchemaAST.isTupleType(node)) for (const [_, element] of node.elements.entries()) {
528
+ const child = findNode(element.type, test);
529
+ if (child) return child;
530
+ }
531
+ else if (SchemaAST.isUnion(node)) {
532
+ if (isLiteralUnion(node)) return;
533
+ for (const type of node.types) {
534
+ const child = findNode(type, test);
535
+ if (child) return child;
536
+ }
537
+ } else if (SchemaAST.isRefinement(node)) return findNode(node.from, test);
538
+ };
539
+ /**
540
+ * Get the AST node for the given property (dot-path).
541
+ */
542
+ var findProperty = (schema, path) => {
543
+ const getProp = (node, path) => {
544
+ const [name, ...rest] = path;
545
+ const typeNode = findNode(node, SchemaAST.isTypeLiteral);
546
+ invariant(typeNode, void 0, {
547
+ "~LogMeta": "~LogMeta",
548
+ F: __dxlog_file$1,
549
+ L: 247,
550
+ S: void 0,
551
+ A: ["typeNode", ""]
552
+ });
553
+ for (const prop of SchemaAST.getPropertySignatures(typeNode)) if (prop.name === name) if (rest.length) return getProp(prop.type, rest);
554
+ else return prop.type;
555
+ };
556
+ return getProp(schema.ast, path.split("."));
557
+ };
558
+ var defaultAnnotations = {
559
+ ObjectKeyword: SchemaAST.objectKeyword,
560
+ StringKeyword: SchemaAST.stringKeyword,
561
+ NumberKeyword: SchemaAST.numberKeyword,
562
+ BooleanKeyword: SchemaAST.booleanKeyword
563
+ };
564
+ /**
565
+ * Get annotation or return undefined.
566
+ * @param annotationId
567
+ * @param noDefault If true, then return undefined for effect library defined values.
568
+ */
569
+ var getAnnotation = (annotationId, noDefault = true) => (node) => {
570
+ const id = Function.pipe(SchemaAST.getIdentifierAnnotation(node), Option.getOrUndefined);
571
+ const value = Function.pipe(SchemaAST.getAnnotation(annotationId)(node), Option.getOrUndefined);
572
+ if (noDefault && (value === defaultAnnotations[node._tag]?.annotations[annotationId] || value === id)) return;
573
+ return value;
574
+ };
575
+ /**
576
+ * Recursively descend into AST to find first matching annotations.
577
+ * Optionally skips default annotations for basic types (e.g., 'a string').
578
+ */
579
+ var findAnnotation = (node, annotationId, noDefault = true) => {
580
+ const getAnnotationById = getAnnotation(annotationId, noDefault);
581
+ const getBaseAnnotation = (node) => {
582
+ const value = getAnnotationById(node);
583
+ if (value !== void 0) return value;
584
+ if (SchemaAST.isUnion(node)) {
585
+ if (isOption(node)) return getAnnotationById(node.types[0]);
586
+ }
587
+ };
588
+ return getBaseAnnotation(node);
589
+ };
590
+ /**
591
+ * Effect Schema.optional creates a union type with undefined as the second type.
592
+ */
593
+ var isOption = (node) => {
594
+ return SchemaAST.isUnion(node) && node.types.length === 2 && SchemaAST.isUndefinedKeyword(node.types[1]);
595
+ };
596
+ /**
597
+ * Determines if the node is a union of literal types.
598
+ */
599
+ var isLiteralUnion = (node) => {
600
+ return SchemaAST.isUnion(node) && node.types.every(SchemaAST.isLiteral);
601
+ };
602
+ /**
603
+ * Extracts the literal values from a schema that is a union of literals
604
+ * (e.g. `Schema.Literal('a', 'b')` or `Schema.Union(Schema.Literal('a'), Schema.Literal('b'))`).
605
+ * Returns an empty array if the schema is not a literal union.
606
+ */
607
+ var getLiteralValues = (schema) => {
608
+ if (!isLiteralUnion(schema.ast)) return [];
609
+ return schema.ast.types.map((node) => node.literal);
610
+ };
611
+ /**
612
+ * Determines if the node is an array type.
613
+ */
614
+ var isArrayType = (node) => {
615
+ return SchemaAST.isTupleType(node) && node.elements.length === 0 && node.rest.length === 1;
616
+ };
617
+ /**
618
+ * Get the type of the array elements.
619
+ */
620
+ var getArrayElementType = (node) => {
621
+ return isArrayType(node) ? node.rest.at(0)?.type : void 0;
622
+ };
623
+ /**
624
+ * Determines if the node is a tuple type.
625
+ */
626
+ var isTupleType = (node) => {
627
+ return SchemaAST.isTupleType(node) && node.elements.length > 0;
628
+ };
629
+ /**
630
+ * Determines if the node is a discriminated union.
631
+ */
632
+ var isDiscriminatedUnion = (node) => {
633
+ return SchemaAST.isUnion(node) && !!getDiscriminatingProps(node)?.length;
634
+ };
635
+ /**
636
+ * Get the discriminating properties for the given union type.
637
+ */
638
+ var getDiscriminatingProps = (node) => {
639
+ invariant(SchemaAST.isUnion(node), void 0, {
640
+ "~LogMeta": "~LogMeta",
641
+ F: __dxlog_file$1,
642
+ L: 379,
643
+ S: void 0,
644
+ A: ["SchemaAST.isUnion(node)", ""]
645
+ });
646
+ if (isOption(node)) return;
647
+ return node.types.reduce((shared, type) => {
648
+ const props = SchemaAST.getPropertySignatures(type).filter((p) => SchemaAST.isLiteral(p.type)).map((p) => p.name.toString());
649
+ return shared.length === 0 ? props : shared.filter((prop) => props.includes(prop));
650
+ }, []);
651
+ };
652
+ /**
653
+ * Get the discriminated type for the given value.
654
+ */
655
+ var getDiscriminatedType = (node, value = {}) => {
656
+ invariant(SchemaAST.isUnion(node), void 0, {
657
+ "~LogMeta": "~LogMeta",
658
+ F: __dxlog_file$1,
659
+ L: 403,
660
+ S: void 0,
661
+ A: ["SchemaAST.isUnion(node)", ""]
662
+ });
663
+ invariant(value, void 0, {
664
+ "~LogMeta": "~LogMeta",
665
+ F: __dxlog_file$1,
666
+ L: 404,
667
+ S: void 0,
668
+ A: ["value", ""]
669
+ });
670
+ const props = getDiscriminatingProps(node);
671
+ if (!props?.length) return;
672
+ for (const type of node.types) if (SchemaAST.getPropertySignatures(type).filter((prop) => props?.includes(prop.name.toString())).every((prop) => {
673
+ invariant(SchemaAST.isLiteral(prop.type), void 0, {
674
+ "~LogMeta": "~LogMeta",
675
+ F: __dxlog_file$1,
676
+ L: 415,
677
+ S: void 0,
678
+ A: ["SchemaAST.isLiteral(prop.type)", ""]
679
+ });
680
+ return prop.type.literal === value[prop.name.toString()];
681
+ })) return type;
682
+ const fields = Object.fromEntries(props.map((prop) => {
683
+ const literals = node.types.map((type) => {
684
+ const literal = SchemaAST.getPropertySignatures(type).find((p) => p.name.toString() === prop);
685
+ invariant(SchemaAST.isLiteral(literal.type), void 0, {
686
+ "~LogMeta": "~LogMeta",
687
+ F: __dxlog_file$1,
688
+ L: 433,
689
+ S: void 0,
690
+ A: ["SchemaAST.isLiteral(literal.type)", ""]
691
+ });
692
+ return literal.type.literal;
693
+ }).filter(isNonNullable);
694
+ return literals.length ? [prop, Schema.Literal(...literals)] : void 0;
695
+ }).filter(isNonNullable));
696
+ return Schema.Struct(fields).ast;
697
+ };
698
+ /**
699
+ * If a property signature is optional (T | undefined), returns the inner non-undefined AST node.
700
+ * Otherwise returns the property signature unchanged, preserving its annotations.
701
+ */
702
+ var unwrapOptional = (property) => {
703
+ if (!property.isOptional || !SchemaAST.isUnion(property.type) || !isOption(property.type)) return property;
704
+ return property.type.types[0];
705
+ };
706
+ /**
707
+ * Determines if the node is a nested object type.
708
+ */
709
+ var isNestedType = (node) => {
710
+ return SchemaAST.isDeclaration(node) || SchemaAST.isObjectKeyword(node) || SchemaAST.isTypeLiteral(node) || isTupleType(node) || isDiscriminatedUnion(node);
711
+ };
712
+ /**
713
+ * Maps AST nodes.
714
+ * The user is responsible for recursively calling {@link mapAst} on the SchemaAST.
715
+ * NOTE: Will evaluate suspended ASTs.
716
+ */
717
+ var mapAst = (ast, f) => {
718
+ switch (ast._tag) {
719
+ case "TypeLiteral": return new SchemaAST.TypeLiteral(ast.propertySignatures.map((prop) => new SchemaAST.PropertySignature(prop.name, f(prop.type, prop.name), prop.isOptional, prop.isReadonly, prop.annotations)), ast.indexSignatures, ast.annotations);
720
+ case "Union": return SchemaAST.Union.make(ast.types.map(f), ast.annotations);
721
+ case "TupleType": return new SchemaAST.TupleType(ast.elements.map((t, index) => new SchemaAST.OptionalType(f(t.type, index), t.isOptional, t.annotations)), ast.rest.map((t) => new SchemaAST.Type(f(t.type, void 0), t.annotations)), ast.isReadonly, ast.annotations);
722
+ case "Suspend": {
723
+ const newAst = f(ast.f(), void 0);
724
+ return new SchemaAST.Suspend(() => newAst, ast.annotations);
725
+ }
726
+ default: return ast;
727
+ }
728
+ };
729
+ var getIndexSignatures = (ast) => {
730
+ const annotation = SchemaAST.getSurrogateAnnotation(ast);
731
+ if (Option.isSome(annotation)) return getIndexSignatures(annotation.value);
732
+ switch (ast._tag) {
733
+ case "TypeLiteral": return ast.indexSignatures.slice();
734
+ case "Suspend": return getIndexSignatures(ast.f());
735
+ case "Refinement": return getIndexSignatures(ast.from);
736
+ }
737
+ return [];
738
+ };
739
+ //#endregion
740
+ //#region src/internal/json-path.ts
741
+ var __dxlog_file = "/__w/dxos/dxos/packages/common/effect/src/internal/json-path.ts";
742
+ var PATH_REGEX = /^($|[a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*|\[\d+\](?:\.)?)*$)/;
743
+ var PROP_REGEX = /^\w+$/;
744
+ /**
745
+ * https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html
746
+ */
747
+ var JsonPath = Schema.String.pipe(Schema.pattern(PATH_REGEX)).annotations({
748
+ title: "JSON path",
749
+ description: "JSON path to a property"
750
+ });
751
+ var JsonProp = Schema.NonEmptyString.pipe(Schema.pattern(PROP_REGEX, { message: () => "Property name must contain only letters, numbers, and underscores" }));
752
+ var isJsonPath = (value) => {
753
+ return Option.isSome(Schema.validateOption(JsonPath)(value));
754
+ };
755
+ /**
756
+ * Creates a JsonPath from an array of path segments.
757
+ *
758
+ * Currently supports:
759
+ * - Simple property access (e.g., 'foo.bar')
760
+ * - Array indexing with non-negative integers (e.g., 'foo[0]')
761
+ * - Identifiers starting with letters, underscore, or $ (e.g., '$foo', '_bar')
762
+ * - Dot notation for nested properties (e.g., 'foo.bar.baz')
763
+ *
764
+ * Does not support (yet?).
765
+ * - Recursive descent (..)
766
+ * - Wildcards (*)
767
+ * - Array slicing
768
+ * - Filters
769
+ * - Negative indices
770
+ *
771
+ * @param path Array of string or number segments
772
+ * @returns Valid JsonPath or undefined if invalid
773
+ */
774
+ var createJsonPath = (path) => {
775
+ const candidatePath = path.map((p, i) => {
776
+ if (typeof p === "number") return `[${p}]`;
777
+ else return i === 0 ? p : `.${p}`;
778
+ }).join("");
779
+ invariant(isJsonPath(candidatePath), `Invalid JsonPath: ${candidatePath}`, {
780
+ "~LogMeta": "~LogMeta",
781
+ F: __dxlog_file,
782
+ L: 69,
783
+ S: void 0,
784
+ A: ["isJsonPath(candidatePath)", "`Invalid JsonPath: ${candidatePath}`"]
785
+ });
786
+ return candidatePath;
787
+ };
788
+ /**
789
+ * Converts Effect validation path format (e.g. "addresses.[0].zip")
790
+ * to JsonPath format (e.g., "addresses[0].zip")
791
+ */
792
+ var fromEffectValidationPath = (effectPath) => {
793
+ const jsonPath = effectPath.replace(/\.\[(\d+)\]/g, "[$1]");
794
+ invariant(isJsonPath(jsonPath), `Invalid JsonPath: ${jsonPath}`, {
795
+ "~LogMeta": "~LogMeta",
796
+ F: __dxlog_file,
797
+ L: 80,
798
+ S: void 0,
799
+ A: ["isJsonPath(jsonPath)", "`Invalid JsonPath: ${jsonPath}`"]
800
+ });
801
+ return jsonPath;
802
+ };
803
+ /**
804
+ * Splits a JsonPath into its constituent parts.
805
+ * Handles property access and array indexing.
806
+ */
807
+ var splitJsonPath = (path) => {
808
+ if (!isJsonPath(path)) return [];
809
+ return path.match(/[a-zA-Z_$][\w$]*|\[\d+\]/g)?.map((part) => part.replace(/[[\]]/g, "")).map((part) => {
810
+ const parsed = Number.parseInt(part, 10);
811
+ return Number.isNaN(parsed) ? part : parsed;
812
+ }) ?? [];
813
+ };
814
+ /**
815
+ * Applies a JsonPath to an object.
816
+ */
817
+ var getField = (object, path) => {
818
+ return JSONPath({
819
+ path,
820
+ json: object
821
+ })[0];
822
+ };
823
+ /**
824
+ * Get value from object using JsonPath.
825
+ */
826
+ var getValue = (obj, path) => {
827
+ return getDeep(obj, splitJsonPath(path));
828
+ };
829
+ /**
830
+ * Set value on object using JsonPath.
831
+ */
832
+ var setValue = (obj, path, value) => {
833
+ return setDeep(obj, splitJsonPath(path), value);
834
+ };
835
+ //#endregion
836
+ //#region src/internal/url.ts
837
+ var ParamKeyAnnotationId = Symbol.for("@dxos/schema/annotation/ParamKey");
838
+ var getParamKeyAnnotation = SchemaAST.getAnnotation(ParamKeyAnnotationId);
839
+ var ParamKeyAnnotation = (value) => (self) => self.annotations({ [ParamKeyAnnotationId]: value });
840
+ /**
841
+ * HTTP params parser.
842
+ * Supports custom key serialization.
843
+ */
844
+ var UrlParser = class {
845
+ _schema;
846
+ constructor(_schema) {
847
+ this._schema = _schema;
848
+ }
849
+ /**
850
+ * Parse URL params.
851
+ */
852
+ parse(_url) {
853
+ const url = new URL(_url);
854
+ return Object.entries(this._schema.fields).reduce((params, [key, type]) => {
855
+ let value = url.searchParams.get(decamelize(key));
856
+ if (value == null) value = url.searchParams.get(key);
857
+ if (value != null) if (SchemaAST.isNumberKeyword(type.ast)) params[key] = parseInt(value);
858
+ else if (SchemaAST.isBooleanKeyword(type.ast)) params[key] = value === "true" || value === "1";
859
+ else params[key] = value;
860
+ return params;
861
+ }, {});
862
+ }
863
+ /**
864
+ * Return URL with encoded params.
865
+ */
866
+ create(_url, params) {
867
+ const url = new URL(_url);
868
+ Object.entries(params).forEach(([key, value]) => {
869
+ if (value !== void 0) {
870
+ const field = this._schema.fields[key];
871
+ if (field) {
872
+ const { key: serializedKey } = Function.pipe(getParamKeyAnnotation(field.ast), Option.getOrElse(() => ({ key: decamelize(key) })));
873
+ url.searchParams.set(serializedKey, String(value));
874
+ }
875
+ }
876
+ });
877
+ return url;
878
+ }
879
+ };
880
+ //#endregion
881
+ //#region src/SchemaEx.ts
882
+ var SchemaEx_exports = /* @__PURE__ */ __exportAll({
883
+ JsonPath: () => JsonPath,
884
+ JsonProp: () => JsonProp,
885
+ ParamKeyAnnotation: () => ParamKeyAnnotation,
886
+ UrlParser: () => UrlParser,
887
+ VisitResult: () => VisitResult,
888
+ createJsonPath: () => createJsonPath,
889
+ findAnnotation: () => findAnnotation,
890
+ findNode: () => findNode,
891
+ findProperty: () => findProperty,
892
+ fromEffectValidationPath: () => fromEffectValidationPath,
893
+ getAnnotation: () => getAnnotation,
894
+ getArrayElementType: () => getArrayElementType,
895
+ getBaseType: () => getBaseType,
896
+ getDiscriminatedType: () => getDiscriminatedType,
897
+ getDiscriminatingProps: () => getDiscriminatingProps,
898
+ getField: () => getField,
899
+ getLiteralValues: () => getLiteralValues,
900
+ getParamKeyAnnotation: () => getParamKeyAnnotation,
901
+ getProperties: () => getProperties,
902
+ getValue: () => getValue,
903
+ isArrayType: () => isArrayType,
904
+ isDiscriminatedUnion: () => isDiscriminatedUnion,
905
+ isJsonPath: () => isJsonPath,
906
+ isLiteralUnion: () => isLiteralUnion,
907
+ isNestedType: () => isNestedType,
908
+ isOption: () => isOption,
909
+ isTupleType: () => isTupleType,
910
+ mapAst: () => mapAst,
911
+ setValue: () => setValue,
912
+ splitJsonPath: () => splitJsonPath,
913
+ unwrapOptional: () => unwrapOptional,
914
+ visit: () => visit
915
+ });
916
+ //#endregion
917
+ export { dynamic_runtime_exports as DynamicRuntime, EffectEx_exports as EffectEx, Performance_exports as Performance, RuntimeProvider_exports as RuntimeProvider, SchemaEx_exports as SchemaEx, createKvsStore, layerOtel };
918
+
919
+ //# sourceMappingURL=index.mjs.map