@dxos/context 0.8.4-main.fd6878d → 0.8.4-main.fdfb99ef29

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.
@@ -18,7 +18,6 @@ function _ts_decorate(decorators, target, key, desc) {
18
18
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
19
19
  return c > 3 && r && Object.defineProperty(target, key, r), r;
20
20
  }
21
- var __dxlog_file = "/__w/dxos/dxos/packages/common/context/src/context.ts";
22
21
  var DEBUG_LOG_DISPOSE = false;
23
22
  var MAX_SAFE_DISPOSE_CALLBACKS = 300;
24
23
  var DEFAULT_ERROR_HANDLER = (error, ctx) => {
@@ -41,6 +40,7 @@ var Context = class _Context {
41
40
  #onError;
42
41
  #flags = 0;
43
42
  #disposePromise = void 0;
43
+ #signal = void 0;
44
44
  maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
45
45
  constructor(params = {}, callMeta) {
46
46
  this.#name = getContextName(params, callMeta);
@@ -66,6 +66,15 @@ var Context = class _Context {
66
66
  get disposeCallbacksLength() {
67
67
  return this.#disposeCallbacks.length;
68
68
  }
69
+ get signal() {
70
+ if (this.#signal) {
71
+ return this.#signal;
72
+ }
73
+ const controller = new AbortController();
74
+ this.#signal = controller.signal;
75
+ this.onDispose(() => controller.abort());
76
+ return this.#signal;
77
+ }
69
78
  /**
70
79
  * Schedules a callback to run when the context is disposed.
71
80
  * May be async, in this case the disposer might choose to wait for all resource to released.
@@ -83,11 +92,6 @@ var Context = class _Context {
83
92
  } catch (error) {
84
93
  log.catch(error, {
85
94
  context: this.#name
86
- }, {
87
- F: __dxlog_file,
88
- L: 119,
89
- S: this,
90
- C: (f, a) => f(...a)
91
95
  });
92
96
  }
93
97
  })();
@@ -100,11 +104,6 @@ var Context = class _Context {
100
104
  context: this.#name,
101
105
  callSite,
102
106
  count: this.#disposeCallbacks.length
103
- }, {
104
- F: __dxlog_file,
105
- L: 128,
106
- S: this,
107
- C: (f, a) => f(...a)
108
107
  });
109
108
  }
110
109
  return () => {
@@ -138,11 +137,6 @@ var Context = class _Context {
138
137
  log("disposing", {
139
138
  context: this.#name,
140
139
  count: callbacks.length
141
- }, {
142
- F: __dxlog_file,
143
- L: 173,
144
- S: this,
145
- C: (f, a) => f(...a)
146
140
  });
147
141
  }
148
142
  let i = 0;
@@ -161,11 +155,6 @@ var Context = class _Context {
161
155
  context: this.#name,
162
156
  callback: i,
163
157
  count: callbacks.length
164
- }, {
165
- F: __dxlog_file,
166
- L: 188,
167
- S: this,
168
- C: (f, a) => f(...a)
169
158
  });
170
159
  }
171
160
  }
@@ -177,11 +166,6 @@ var Context = class _Context {
177
166
  if (DEBUG_LOG_DISPOSE) {
178
167
  log("disposed", {
179
168
  context: this.#name
180
- }, {
181
- F: __dxlog_file,
182
- L: 199,
183
- S: this,
184
- C: (f, a) => f(...a)
185
169
  });
186
170
  }
187
171
  return clean;
@@ -203,6 +187,7 @@ var Context = class _Context {
203
187
  }
204
188
  derive({ onError, attributes } = {}) {
205
189
  const newCtx = new _Context({
190
+ parent: this,
206
191
  // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.
207
192
  onError: async (error) => {
208
193
  if (!onError) {
@@ -268,13 +253,14 @@ var cancelWithContext = (ctx, promise) => {
268
253
  };
269
254
 
270
255
  // src/resource.ts
256
+ import "@hazae41/symbol-dispose-polyfill";
271
257
  import { throwUnhandledError } from "@dxos/util";
272
- var LifecycleState = /* @__PURE__ */ function(LifecycleState2) {
258
+ var LifecycleState = /* @__PURE__ */ (function(LifecycleState2) {
273
259
  LifecycleState2["CLOSED"] = "CLOSED";
274
260
  LifecycleState2["OPEN"] = "OPEN";
275
261
  LifecycleState2["ERROR"] = "ERROR";
276
262
  return LifecycleState2;
277
- }({});
263
+ })({});
278
264
  var CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;
279
265
  var Resource = class {
280
266
  #lifecycleState = "CLOSED";
@@ -291,6 +277,16 @@ var Resource = class {
291
277
  * Provided in the open method.
292
278
  */
293
279
  #parentCtx = this.#createParentContext();
280
+ /**
281
+ * ```ts
282
+ * await using resource = new Resource();
283
+ * await resource.open();
284
+ * ```
285
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
286
+ */
287
+ async [Symbol.asyncDispose]() {
288
+ await this.close();
289
+ }
294
290
  get #name() {
295
291
  return Object.getPrototypeOf(this).constructor.name;
296
292
  }
@@ -306,12 +302,12 @@ var Resource = class {
306
302
  /**
307
303
  * To be overridden by subclasses.
308
304
  */
309
- async _open(ctx) {
305
+ async _open(_ctx) {
310
306
  }
311
307
  /**
312
308
  * To be overridden by subclasses.
313
309
  */
314
- async _close(ctx) {
310
+ async _close(_ctx) {
315
311
  }
316
312
  /**
317
313
  * Error handler for errors that are caught by the context.
@@ -328,6 +324,19 @@ var Resource = class {
328
324
  throw err;
329
325
  }
330
326
  /**
327
+ * Calls the provided function, opening and closing the resource.
328
+ * NOTE: Consider using `using` instead.
329
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
330
+ */
331
+ async use(fn) {
332
+ try {
333
+ await this.open();
334
+ return await fn(this);
335
+ } finally {
336
+ await this.close();
337
+ }
338
+ }
339
+ /**
331
340
  * Opens the resource.
332
341
  * If the resource is already open, it does nothing.
333
342
  * If the resource is in an error state, it throws an error.
@@ -373,14 +382,12 @@ var Resource = class {
373
382
  }
374
383
  await this.#openPromise;
375
384
  }
376
- async [Symbol.asyncDispose]() {
377
- await this.close();
378
- }
379
385
  async #open(ctx) {
380
386
  this.#closePromise = null;
381
387
  this.#parentCtx = ctx?.derive({
382
388
  name: this.#name
383
389
  }) ?? this.#createParentContext();
390
+ this.#internalCtx = this.#createContext(this.#parentCtx);
384
391
  await this._open(this.#parentCtx);
385
392
  this.#lifecycleState = "OPEN";
386
393
  }
@@ -391,9 +398,10 @@ var Resource = class {
391
398
  this.#internalCtx = this.#createContext();
392
399
  this.#lifecycleState = "CLOSED";
393
400
  }
394
- #createContext() {
401
+ #createContext(attributeParent) {
395
402
  return new Context({
396
403
  name: this.#name,
404
+ parent: attributeParent,
397
405
  onError: (error) => queueMicrotask(async () => {
398
406
  try {
399
407
  await this._catch(error);
@@ -415,11 +423,48 @@ var openInContext = async (ctx, resource) => {
415
423
  ctx.onDispose(() => resource.close?.());
416
424
  return resource;
417
425
  };
426
+
427
+ // src/trace-context.ts
428
+ var TRACE_SPAN_ATTRIBUTE = "dxos.trace-span";
429
+ var ContextRpcCodec = class {
430
+ /**
431
+ * Read the W3C trace context from a DXOS `Context` for an outgoing RPC.
432
+ *
433
+ * @returns `TraceContextData` to attach to the wire message, or `undefined`
434
+ * if the context has no active trace.
435
+ */
436
+ static encode(ctx) {
437
+ const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE);
438
+ if (traceCtx == null || typeof traceCtx.traceparent !== "string") {
439
+ return void 0;
440
+ }
441
+ return traceCtx;
442
+ }
443
+ /**
444
+ * Reconstruct a DXOS `Context` from W3C trace context received in an
445
+ * incoming RPC request.
446
+ *
447
+ * @returns A `Context` carrying the trace context, or `Context.default()`
448
+ * if the data is missing/invalid.
449
+ */
450
+ static decode(traceContext) {
451
+ if (typeof traceContext.traceparent !== "string" || traceContext.traceparent.length === 0) {
452
+ return Context.default();
453
+ }
454
+ return new Context({
455
+ attributes: {
456
+ [TRACE_SPAN_ATTRIBUTE]: traceContext
457
+ }
458
+ });
459
+ }
460
+ };
418
461
  export {
419
462
  Context,
420
463
  ContextDisposedError,
464
+ ContextRpcCodec,
421
465
  LifecycleState,
422
466
  Resource,
467
+ TRACE_SPAN_ATTRIBUTE,
423
468
  cancelWithContext,
424
469
  openInContext,
425
470
  rejectOnDispose
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/context.ts", "../../../src/context-disposed-error.ts", "../../../src/promise-utils.ts", "../../../src/resource.ts"],
4
- "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { inspect } from 'node:util';\n\nimport { StackTrace } from '@dxos/debug';\nimport { type CallMetadata, log } from '@dxos/log';\nimport { safeInstanceof } from '@dxos/util';\n\nimport { ContextDisposedError } from './context-disposed-error';\n\nexport type ContextErrorHandler = (error: Error, ctx: Context) => void;\n\nexport type DisposeCallback = () => any | Promise<any>;\n\nexport type CreateContextParams = {\n name?: string;\n parent?: Context;\n attributes?: Record<string, any>;\n onError?: ContextErrorHandler;\n};\n\nconst DEBUG_LOG_DISPOSE = false;\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\nconst DEFAULT_ERROR_HANDLER: ContextErrorHandler = (error, ctx) => {\n if (error instanceof ContextDisposedError) {\n return;\n }\n\n void ctx.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n};\n\ntype ContextFlags = number;\n\nconst CONTEXT_FLAG_IS_DISPOSED: ContextFlags = 1 << 0;\n\n/**\n * Whether the dispose callback leak was detected.\n */\nconst CONTEXT_FLAG_LEAK_DETECTED: ContextFlags = 1 << 1;\n\n/**\n * NOTE: Context is not reusable after it is disposed.\n */\n@safeInstanceof('Context')\nexport class Context {\n static default(): Context {\n return new Context();\n }\n\n readonly #disposeCallbacks: DisposeCallback[] = [];\n\n readonly #name?: string = undefined;\n readonly #parent?: Context = undefined;\n readonly #attributes: Record<string, any>;\n readonly #onError: ContextErrorHandler;\n\n #flags: ContextFlags = 0;\n #disposePromise?: Promise<boolean> = undefined;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor(params: CreateContextParams = {}, callMeta?: Partial<CallMetadata>) {\n this.#name = getContextName(params, callMeta);\n this.#parent = params.parent;\n this.#attributes = params.attributes ?? {};\n this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;\n }\n\n get #isDisposed() {\n return !!(this.#flags & CONTEXT_FLAG_IS_DISPOSED);\n }\n\n set #isDisposed(value: boolean) {\n this.#flags = value ? this.#flags | CONTEXT_FLAG_IS_DISPOSED : this.#flags & ~CONTEXT_FLAG_IS_DISPOSED;\n }\n\n get #leakDetected() {\n return !!(this.#flags & CONTEXT_FLAG_LEAK_DETECTED);\n }\n\n set #leakDetected(value: boolean) {\n this.#flags = value ? this.#flags | CONTEXT_FLAG_LEAK_DETECTED : this.#flags & ~CONTEXT_FLAG_LEAK_DETECTED;\n }\n\n get disposed() {\n return this.#isDisposed;\n }\n\n get disposeCallbacksLength() {\n return this.#disposeCallbacks.length;\n }\n\n /**\n * Schedules a callback to run when the context is disposed.\n * May be async, in this case the disposer might choose to wait for all resource to released.\n * Throwing an error inside the callback will result in the error being logged, but not re-thrown.\n *\n * NOTE: Will call the callback immediately if the context is already disposed.\n *\n * @returns A function that can be used to remove the callback from the dispose list.\n */\n onDispose(callback: DisposeCallback): () => void {\n if (this.#isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error, { context: this.#name });\n }\n })();\n }\n\n this.#disposeCallbacks.push(callback);\n if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks && !this.#leakDetected) {\n this.#leakDetected = true;\n const callSite = new StackTrace().getStackArray(1)[0].trim();\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\n context: this.#name,\n callSite,\n count: this.#disposeCallbacks.length,\n });\n }\n\n // Remove handler.\n return () => {\n const index = this.#disposeCallbacks.indexOf(callback);\n if (index !== -1) {\n this.#disposeCallbacks.splice(index, 1);\n }\n };\n }\n\n /**\n * Runs all dispose callbacks.\n * Callbacks are run in the reverse order they were added.\n * This function never throws.\n * It is safe to ignore the returned promise if the caller does not wish to wait for callbacks to complete.\n * Disposing context means that onDispose will throw an error and any errors raised will be logged and not propagated.\n * @returns true if there were no errors during the dispose process.\n */\n async dispose(throwOnError = false): Promise<boolean> {\n if (this.#disposePromise) {\n return this.#disposePromise;\n }\n\n // TODO(burdon): Probably should not be set until the dispose is complete, but causes tests to fail if moved.\n this.#isDisposed = true;\n\n // Set the promise before running the callbacks.\n let resolveDispose!: (value: boolean) => void;\n const promise = new Promise<boolean>((resolve) => {\n resolveDispose = resolve;\n });\n this.#disposePromise = promise;\n\n // Process last first.\n // Clone the array so that any mutations to the original array don't affect the dispose process.\n const callbacks = Array.from(this.#disposeCallbacks).reverse();\n this.#disposeCallbacks.length = 0;\n\n if (DEBUG_LOG_DISPOSE) {\n log('disposing', { context: this.#name, count: callbacks.length });\n }\n\n let i = 0;\n let clean = true;\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n await callback();\n i++;\n } catch (err: any) {\n clean = false;\n if (throwOnError) {\n errors.push(err);\n } else {\n log.catch(err, { context: this.#name, callback: i, count: callbacks.length });\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors);\n }\n\n resolveDispose(clean);\n if (DEBUG_LOG_DISPOSE) {\n log('disposed', { context: this.#name });\n }\n\n return clean;\n }\n\n /**\n * Raise the error inside the context.\n * The error will be propagated to the error handler.\n * IF the error handler is not set, the error will dispose the context and cause an unhandled rejection.\n */\n raise(error: Error): void {\n if (this.#isDisposed) {\n // TODO(dmaretskyi): Don't log those.\n // log.warn('Error in disposed context', error);\n return;\n }\n\n try {\n this.#onError(error, this);\n } catch (err) {\n // Generate an unhandled rejection and stop the error propagation.\n void Promise.reject(err);\n }\n }\n\n derive({ onError, attributes }: CreateContextParams = {}): Context {\n const newCtx = new Context({\n // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.\n onError: async (error) => {\n if (!onError) {\n this.raise(error);\n } else {\n try {\n await onError(error, this);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\n });\n\n const clearDispose = this.onDispose(() => newCtx.dispose());\n newCtx.onDispose(clearDispose);\n return newCtx;\n }\n\n getAttribute(key: string): any {\n if (key in this.#attributes) {\n return this.#attributes[key];\n }\n if (this.#parent) {\n return this.#parent.getAttribute(key);\n }\n\n return undefined;\n }\n\n [Symbol.toStringTag] = 'Context';\n [inspect.custom] = () => this.toString();\n\n toString(): string {\n return `Context(${this.#isDisposed ? 'disposed' : 'active'})`;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\nconst getContextName = (params: CreateContextParams, callMeta?: Partial<CallMetadata>): string | undefined => {\n if (params.name) {\n return params.name;\n }\n if (callMeta?.F?.length) {\n const pathSegments = callMeta?.F.split('/');\n return `${pathSegments[pathSegments.length - 1]}#${callMeta?.L ?? 0}`;\n }\n return undefined;\n};\n", "//\n// Copyright 2023 DXOS.org\n//\n\nexport class ContextDisposedError extends Error {\n constructor() {\n super('Context disposed.');\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context } from './context';\nimport { ContextDisposedError } from './context-disposed-error';\n\n/**\n * @returns A promise that rejects when the context is disposed.\n */\n// TODO(dmaretskyi): Memory leak.\nexport const rejectOnDispose = (ctx: Context, error = new ContextDisposedError()): Promise<never> =>\n new Promise((resolve, reject) => {\n ctx.onDispose(() => reject(error));\n });\n\n/**\n * Rejects the promise if the context is disposed.\n */\nexport const cancelWithContext = <T>(ctx: Context, promise: Promise<T>): Promise<T> => {\n let clearDispose: () => void;\n return Promise.race([\n promise,\n new Promise<never>((resolve, reject) => {\n // Will be called before .finally() handlers.\n clearDispose = ctx.onDispose(() => reject(new ContextDisposedError()));\n }),\n ]).finally(() => clearDispose?.());\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { throwUnhandledError } from '@dxos/util';\n\nimport { Context } from './context';\n\nexport enum LifecycleState {\n CLOSED = 'CLOSED',\n OPEN = 'OPEN',\n ERROR = 'ERROR',\n}\n\nexport interface Lifecycle {\n open?(ctx?: Context): Promise<any> | any;\n close?(): Promise<any> | any;\n}\n\n// Feature flag to be enabled later.\nconst CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;\n\n/**\n * Base class for resources that need to be opened and closed.\n */\nexport abstract class Resource implements Lifecycle {\n #lifecycleState = LifecycleState.CLOSED;\n #openPromise: Promise<void> | null = null;\n #closePromise: Promise<void> | null = null;\n\n /**\n * Managed internally by the resource.\n * Recreated on close.\n * Errors are propagated to the `_catch` method and the parent context.\n */\n #internalCtx: Context = this.#createContext();\n\n /**\n * Context that is used to bubble up errors that are not handled by the resource.\n * Provided in the open method.\n */\n #parentCtx: Context = this.#createParentContext();\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\n }\n\n get isOpen() {\n return this.#lifecycleState === LifecycleState.OPEN && this.#closePromise == null;\n }\n\n protected get _lifecycleState() {\n return this.#lifecycleState;\n }\n\n protected get _ctx() {\n return this.#internalCtx;\n }\n\n /**\n * To be overridden by subclasses.\n */\n protected async _open(ctx: Context): Promise<void> {}\n\n /**\n * To be overridden by subclasses.\n */\n protected async _close(ctx: Context): Promise<void> {}\n\n /**\n * Error handler for errors that are caught by the context.\n * By default, errors are bubbled up to the parent context which is passed to the open method.\n */\n protected async _catch(err: Error): Promise<void> {\n if (CLOSE_RESOURCE_ON_UNHANDLED_ERROR) {\n try {\n await this.close();\n } catch (doubleErr: any) {\n throwUnhandledError(doubleErr);\n }\n }\n throw err;\n }\n\n /**\n * Opens the resource.\n * If the resource is already open, it does nothing.\n * If the resource is in an error state, it throws an error.\n * If the resource is closed, it waits for it to close and then opens it.\n * @param ctx - Context to use for opening the resource. This context will receive errors that are not handled in `_catch`.\n */\n async open(ctx?: Context): Promise<this> {\n switch (this.#lifecycleState) {\n case LifecycleState.OPEN:\n return this;\n case LifecycleState.ERROR:\n throw new Error(`Invalid state: ${this.#lifecycleState}`);\n default:\n }\n\n await this.#closePromise;\n await (this.#openPromise ??= this.#open(ctx));\n\n return this;\n }\n\n /**\n * Closes the resource.\n * If the resource is already closed, it does nothing.\n */\n async close(ctx?: Context): Promise<this> {\n if (this.#lifecycleState === LifecycleState.CLOSED) {\n return this;\n }\n await this.#openPromise;\n await (this.#closePromise ??= this.#close(ctx));\n\n return this;\n }\n\n /**\n * Waits until the resource is open.\n */\n async waitUntilOpen(): Promise<void> {\n switch (this.#lifecycleState) {\n case LifecycleState.OPEN:\n return;\n case LifecycleState.ERROR:\n throw new Error(`Invalid state: ${this.#lifecycleState}`);\n }\n\n if (!this.#openPromise) {\n throw new Error('Resource is not being opened');\n }\n await this.#openPromise;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n async #open(ctx?: Context): Promise<void> {\n this.#closePromise = null;\n this.#parentCtx = ctx?.derive({ name: this.#name }) ?? this.#createParentContext();\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()): Promise<void> {\n this.#openPromise = null;\n await this.#internalCtx.dispose();\n await this._close(ctx);\n this.#internalCtx = this.#createContext();\n this.#lifecycleState = LifecycleState.CLOSED;\n }\n\n #createContext(): Context {\n return new Context({\n name: this.#name,\n onError: (error) =>\n queueMicrotask(async () => {\n try {\n await this._catch(error);\n } catch (err: any) {\n this.#lifecycleState = LifecycleState.ERROR;\n this.#parentCtx.raise(err);\n }\n }),\n });\n }\n\n #createParentContext(): Context {\n return new Context({ name: this.#name });\n }\n}\n\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n return resource;\n};\n"],
5
- "mappings": "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",
6
- "names": ["inspect", "StackTrace", "log", "safeInstanceof", "ContextDisposedError", "Error", "DEBUG_LOG_DISPOSE", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "ContextDisposedError", "dispose", "CONTEXT_FLAG_IS_DISPOSED", "CONTEXT_FLAG_LEAK_DETECTED", "Context", "default", "undefined", "maxSafeDisposeCallbacks", "params", "callMeta", "getContextName", "parent", "attributes", "onError", "value", "disposed", "disposeCallbacksLength", "length", "onDispose", "callback", "log", "catch", "context", "push", "callSite", "StackTrace", "getStackArray", "trim", "warn", "count", "index", "indexOf", "splice", "throwOnError", "resolveDispose", "promise", "Promise", "resolve", "callbacks", "Array", "from", "reverse", "i", "clean", "errors", "err", "AggregateError", "raise", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "Symbol", "toStringTag", "inspect", "custom", "toString", "asyncDispose", "name", "F", "pathSegments", "split", "L", "rejectOnDispose", "ctx", "error", "ContextDisposedError", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally", "throwUnhandledError", "LifecycleState", "CLOSE_RESOURCE_ON_UNHANDLED_ERROR", "Resource", "Object", "getPrototypeOf", "name", "isOpen", "_lifecycleState", "_ctx", "_open", "ctx", "_close", "_catch", "err", "close", "doubleErr", "throwUnhandledError", "open", "Error", "waitUntilOpen", "Symbol", "asyncDispose", "derive", "Context", "default", "dispose", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose"]
3
+ "sources": ["../../../src/context.ts", "../../../src/context-disposed-error.ts", "../../../src/promise-utils.ts", "../../../src/resource.ts", "../../../src/trace-context.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { inspect } from 'node:util';\n\nimport { StackTrace } from '@dxos/debug';\nimport { type CallMetadata, log } from '@dxos/log';\nimport { safeInstanceof } from '@dxos/util';\n\nimport { ContextDisposedError } from './context-disposed-error';\n\nexport type ContextErrorHandler = (error: Error, ctx: Context) => void;\n\nexport type DisposeCallback = () => any | Promise<any>;\n\nexport type CreateContextProps = {\n name?: string;\n parent?: Context;\n attributes?: Record<string, any>;\n onError?: ContextErrorHandler;\n};\n\nconst DEBUG_LOG_DISPOSE = false;\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\nconst DEFAULT_ERROR_HANDLER: ContextErrorHandler = (error, ctx) => {\n if (error instanceof ContextDisposedError) {\n return;\n }\n\n void ctx.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n};\n\ntype ContextFlags = number;\n\nconst CONTEXT_FLAG_IS_DISPOSED: ContextFlags = 1 << 0;\n\n/**\n * Whether the dispose callback leak was detected.\n */\nconst CONTEXT_FLAG_LEAK_DETECTED: ContextFlags = 1 << 1;\n\n/**\n * NOTE: Context is not reusable after it is disposed.\n */\n@safeInstanceof('Context')\nexport class Context {\n static default(): Context {\n return new Context();\n }\n\n readonly #disposeCallbacks: DisposeCallback[] = [];\n\n readonly #name?: string = undefined;\n readonly #parent?: Context = undefined;\n readonly #attributes: Record<string, any>;\n readonly #onError: ContextErrorHandler;\n\n #flags: ContextFlags = 0;\n #disposePromise?: Promise<boolean> = undefined;\n\n #signal: AbortSignal | undefined = undefined;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor(params: CreateContextProps = {}, callMeta?: Partial<CallMetadata>) {\n this.#name = getContextName(params, callMeta);\n this.#parent = params.parent;\n this.#attributes = params.attributes ?? {};\n this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;\n }\n\n get #isDisposed() {\n return !!(this.#flags & CONTEXT_FLAG_IS_DISPOSED);\n }\n\n set #isDisposed(value: boolean) {\n this.#flags = value ? this.#flags | CONTEXT_FLAG_IS_DISPOSED : this.#flags & ~CONTEXT_FLAG_IS_DISPOSED;\n }\n\n get #leakDetected() {\n return !!(this.#flags & CONTEXT_FLAG_LEAK_DETECTED);\n }\n\n set #leakDetected(value: boolean) {\n this.#flags = value ? this.#flags | CONTEXT_FLAG_LEAK_DETECTED : this.#flags & ~CONTEXT_FLAG_LEAK_DETECTED;\n }\n\n get disposed() {\n return this.#isDisposed;\n }\n\n get disposeCallbacksLength() {\n return this.#disposeCallbacks.length;\n }\n\n get signal(): AbortSignal {\n if (this.#signal) {\n return this.#signal;\n }\n const controller = new AbortController();\n this.#signal = controller.signal;\n this.onDispose(() => controller.abort());\n return this.#signal;\n }\n\n /**\n * Schedules a callback to run when the context is disposed.\n * May be async, in this case the disposer might choose to wait for all resource to released.\n * Throwing an error inside the callback will result in the error being logged, but not re-thrown.\n *\n * NOTE: Will call the callback immediately if the context is already disposed.\n *\n * @returns A function that can be used to remove the callback from the dispose list.\n */\n onDispose(callback: DisposeCallback): () => void {\n if (this.#isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error, { context: this.#name });\n }\n })();\n }\n\n this.#disposeCallbacks.push(callback);\n if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks && !this.#leakDetected) {\n this.#leakDetected = true;\n const callSite = new StackTrace().getStackArray(1)[0].trim();\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\n context: this.#name,\n callSite,\n count: this.#disposeCallbacks.length,\n });\n }\n\n // Remove handler.\n return () => {\n const index = this.#disposeCallbacks.indexOf(callback);\n if (index !== -1) {\n this.#disposeCallbacks.splice(index, 1);\n }\n };\n }\n\n /**\n * Runs all dispose callbacks.\n * Callbacks are run in the reverse order they were added.\n * This function never throws.\n * It is safe to ignore the returned promise if the caller does not wish to wait for callbacks to complete.\n * Disposing context means that onDispose will throw an error and any errors raised will be logged and not propagated.\n * @returns true if there were no errors during the dispose process.\n */\n async dispose(throwOnError = false): Promise<boolean> {\n if (this.#disposePromise) {\n return this.#disposePromise;\n }\n\n // TODO(burdon): Probably should not be set until the dispose is complete, but causes tests to fail if moved.\n this.#isDisposed = true;\n\n // Set the promise before running the callbacks.\n let resolveDispose!: (value: boolean) => void;\n const promise = new Promise<boolean>((resolve) => {\n resolveDispose = resolve;\n });\n this.#disposePromise = promise;\n\n // Process last first.\n // Clone the array so that any mutations to the original array don't affect the dispose process.\n const callbacks = Array.from(this.#disposeCallbacks).reverse();\n this.#disposeCallbacks.length = 0;\n\n if (DEBUG_LOG_DISPOSE) {\n log('disposing', { context: this.#name, count: callbacks.length });\n }\n\n let i = 0;\n let clean = true;\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n await callback();\n i++;\n } catch (err: any) {\n clean = false;\n if (throwOnError) {\n errors.push(err);\n } else {\n log.catch(err, { context: this.#name, callback: i, count: callbacks.length });\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors);\n }\n\n resolveDispose(clean);\n if (DEBUG_LOG_DISPOSE) {\n log('disposed', { context: this.#name });\n }\n\n return clean;\n }\n\n /**\n * Raise the error inside the context.\n * The error will be propagated to the error handler.\n * IF the error handler is not set, the error will dispose the context and cause an unhandled rejection.\n */\n raise(error: Error): void {\n if (this.#isDisposed) {\n // TODO(dmaretskyi): Don't log those.\n // log.warn('Error in disposed context', error);\n return;\n }\n\n try {\n this.#onError(error, this);\n } catch (err) {\n // Generate an unhandled rejection and stop the error propagation.\n void Promise.reject(err);\n }\n }\n\n derive({ onError, attributes }: CreateContextProps = {}): Context {\n const newCtx = new Context({\n parent: this,\n // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.\n onError: async (error) => {\n if (!onError) {\n this.raise(error);\n } else {\n try {\n await onError(error, this);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\n });\n\n const clearDispose = this.onDispose(() => newCtx.dispose());\n newCtx.onDispose(clearDispose);\n return newCtx;\n }\n\n getAttribute(key: string): any {\n if (key in this.#attributes) {\n return this.#attributes[key];\n }\n if (this.#parent) {\n return this.#parent.getAttribute(key);\n }\n\n return undefined;\n }\n\n [Symbol.toStringTag] = 'Context';\n [inspect.custom] = () => this.toString();\n\n toString(): string {\n return `Context(${this.#isDisposed ? 'disposed' : 'active'})`;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\nconst getContextName = (params: CreateContextProps, callMeta?: Partial<CallMetadata>): string | undefined => {\n if (params.name) {\n return params.name;\n }\n if (callMeta?.F?.length) {\n const pathSegments = callMeta?.F.split('/');\n return `${pathSegments[pathSegments.length - 1]}#${callMeta?.L ?? 0}`;\n }\n return undefined;\n};\n", "//\n// Copyright 2023 DXOS.org\n//\n\nexport class ContextDisposedError extends Error {\n constructor() {\n super('Context disposed.');\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context } from './context';\nimport { ContextDisposedError } from './context-disposed-error';\n\n/**\n * @returns A promise that rejects when the context is disposed.\n */\n// TODO(dmaretskyi): Memory leak.\nexport const rejectOnDispose = (ctx: Context, error = new ContextDisposedError()): Promise<never> =>\n new Promise((resolve, reject) => {\n ctx.onDispose(() => reject(error));\n });\n\n/**\n * Rejects the promise if the context is disposed.\n */\nexport const cancelWithContext = <T>(ctx: Context, promise: Promise<T>): Promise<T> => {\n let clearDispose: () => void;\n return Promise.race([\n promise,\n new Promise<never>((resolve, reject) => {\n // Will be called before .finally() handlers.\n clearDispose = ctx.onDispose(() => reject(new ContextDisposedError()));\n }),\n ]).finally(() => clearDispose?.());\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport '@hazae41/symbol-dispose-polyfill';\n\nimport { throwUnhandledError } from '@dxos/util';\n\nimport { Context } from './context';\n\nexport enum LifecycleState {\n CLOSED = 'CLOSED',\n OPEN = 'OPEN',\n ERROR = 'ERROR',\n}\n\nexport interface Lifecycle {\n open?(ctx?: Context): Promise<any> | any;\n close?(): Promise<any> | any;\n}\n\n// Feature flag to be enabled later.\nconst CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;\n\n/**\n * Base class for resources that need to be opened and closed.\n */\nexport abstract class Resource implements Lifecycle {\n #lifecycleState = LifecycleState.CLOSED;\n\n #openPromise: Promise<void> | null = null;\n #closePromise: Promise<void> | null = null;\n\n /**\n * Managed internally by the resource.\n * Recreated on close.\n * Errors are propagated to the `_catch` method and the parent context.\n */\n #internalCtx: Context = this.#createContext();\n\n /**\n * Context that is used to bubble up errors that are not handled by the resource.\n * Provided in the open method.\n */\n #parentCtx: Context = this.#createParentContext();\n\n /**\n * ```ts\n * await using resource = new Resource();\n * await resource.open();\n * ```\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\n }\n\n get isOpen() {\n return this.#lifecycleState === LifecycleState.OPEN && this.#closePromise == null;\n }\n\n protected get _lifecycleState() {\n return this.#lifecycleState;\n }\n\n protected get _ctx() {\n return this.#internalCtx;\n }\n\n /**\n * To be overridden by subclasses.\n */\n protected async _open(_ctx: Context): Promise<void> {}\n\n /**\n * To be overridden by subclasses.\n */\n protected async _close(_ctx: Context): Promise<void> {}\n\n /**\n * Error handler for errors that are caught by the context.\n * By default, errors are bubbled up to the parent context which is passed to the open method.\n */\n protected async _catch(err: Error): Promise<void> {\n if (CLOSE_RESOURCE_ON_UNHANDLED_ERROR) {\n try {\n await this.close();\n } catch (doubleErr: any) {\n throwUnhandledError(doubleErr);\n }\n }\n throw err;\n }\n\n /**\n * Calls the provided function, opening and closing the resource.\n * NOTE: Consider using `using` instead.\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using\n */\n async use<T>(fn: (resource: this) => Promise<T>): Promise<T> {\n try {\n await this.open();\n return await fn(this);\n } finally {\n await this.close();\n }\n }\n\n /**\n * Opens the resource.\n * If the resource is already open, it does nothing.\n * If the resource is in an error state, it throws an error.\n * If the resource is closed, it waits for it to close and then opens it.\n * @param ctx - Context to use for opening the resource. This context will receive errors that are not handled in `_catch`.\n */\n async open(ctx?: Context): Promise<this> {\n switch (this.#lifecycleState) {\n case LifecycleState.OPEN:\n return this;\n case LifecycleState.ERROR:\n throw new Error(`Invalid state: ${this.#lifecycleState}`);\n default:\n }\n\n await this.#closePromise;\n await (this.#openPromise ??= this.#open(ctx));\n return this;\n }\n\n /**\n * Closes the resource.\n * If the resource is already closed, it does nothing.\n */\n async close(ctx?: Context): Promise<this> {\n if (this.#lifecycleState === LifecycleState.CLOSED) {\n return this;\n }\n await this.#openPromise;\n await (this.#closePromise ??= this.#close(ctx));\n return this;\n }\n\n /**\n * Waits until the resource is open.\n */\n async waitUntilOpen(): Promise<void> {\n switch (this.#lifecycleState) {\n case LifecycleState.OPEN:\n return;\n case LifecycleState.ERROR:\n throw new Error(`Invalid state: ${this.#lifecycleState}`);\n }\n\n if (!this.#openPromise) {\n throw new Error('Resource is not being opened');\n }\n await this.#openPromise;\n }\n\n async #open(ctx?: Context): Promise<void> {\n this.#closePromise = null;\n this.#parentCtx = ctx?.derive({ name: this.#name }) ?? this.#createParentContext();\n this.#internalCtx = this.#createContext(this.#parentCtx);\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()): Promise<void> {\n this.#openPromise = null;\n await this.#internalCtx.dispose();\n await this._close(ctx);\n this.#internalCtx = this.#createContext();\n this.#lifecycleState = LifecycleState.CLOSED;\n }\n\n #createContext(attributeParent?: Context): Context {\n return new Context({\n name: this.#name,\n parent: attributeParent,\n onError: (error) =>\n queueMicrotask(async () => {\n try {\n await this._catch(error);\n } catch (err: any) {\n this.#lifecycleState = LifecycleState.ERROR;\n this.#parentCtx.raise(err);\n }\n }),\n });\n }\n\n #createParentContext(): Context {\n return new Context({ name: this.#name });\n }\n}\n\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n return resource;\n};\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport { Context } from './context';\n\n/**\n * Context attribute key for trace context data.\n * Stores {@link TraceContextData} (W3C traceparent/tracestate strings).\n */\nexport const TRACE_SPAN_ATTRIBUTE = 'dxos.trace-span';\n\n/**\n * W3C Trace Context wire format for propagating trace identity.\n * Stored on DXOS {@link Context} attributes and carried across RPC boundaries.\n *\n * Because these are plain strings (not live runtime objects), they remain valid\n * after the originating span ends — enabling long-lived contexts (`this._ctx`)\n * to serve as parents for later child spans without a retention cache.\n *\n * @see https://www.w3.org/TR/trace-context/\n */\nexport type TraceContextData = {\n /**\n * W3C `traceparent` header value.\n * Format: `{version}-{traceId}-{spanId}-{traceFlags}` (e.g., `00-abc...def-012...789-01`).\n */\n traceparent: string;\n /** Optional W3C `tracestate` header value carrying vendor-specific trace data. */\n tracestate?: string;\n};\n\n/**\n * Codec for propagating trace identity across RPC boundaries.\n *\n * Hardcoded in `RpcPeer` — every outgoing request calls {@link encode} to\n * extract W3C trace context from the DXOS `Context`, and every incoming\n * request calls {@link decode} to reconstruct a DXOS `Context` carrying the\n * caller's trace context.\n *\n * This works because `TRACE_SPAN_ATTRIBUTE` stores serializable\n * {@link TraceContextData} strings, not opaque runtime objects.\n */\nexport class ContextRpcCodec {\n /**\n * Read the W3C trace context from a DXOS `Context` for an outgoing RPC.\n *\n * @returns `TraceContextData` to attach to the wire message, or `undefined`\n * if the context has no active trace.\n */\n static encode(ctx: Context): TraceContextData | undefined {\n const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE);\n if (traceCtx == null || typeof traceCtx.traceparent !== 'string') {\n return undefined;\n }\n return traceCtx as TraceContextData;\n }\n\n /**\n * Reconstruct a DXOS `Context` from W3C trace context received in an\n * incoming RPC request.\n *\n * @returns A `Context` carrying the trace context, or `Context.default()`\n * if the data is missing/invalid.\n */\n static decode(traceContext: TraceContextData): Context {\n if (typeof traceContext.traceparent !== 'string' || traceContext.traceparent.length === 0) {\n return Context.default();\n }\n return new Context({ attributes: { [TRACE_SPAN_ATTRIBUTE]: traceContext } });\n }\n}\n"],
5
+ "mappings": ";AAIA,SAASA,eAAe;AAExB,SAASC,kBAAkB;AAC3B,SAA4BC,WAAW;AACvC,SAASC,sBAAsB;;;ACJxB,IAAMC,uBAAN,cAAmCC,MAAAA;EACxC,cAAc;AACZ,UAAM,mBAAA;EACR;AACF;;;;;;;;;ADeA,IAAMC,oBAAoB;AAK1B,IAAMC,6BAA6B;AAEnC,IAAMC,wBAA6C,CAACC,OAAOC,QAAAA;AACzD,MAAID,iBAAiBE,sBAAsB;AACzC;EACF;AAEA,OAAKD,IAAIE,QAAO;AAGhB,QAAMH;AACR;AAIA,IAAMI,2BAAyC,KAAK;AAKpD,IAAMC,6BAA2C,KAAK;AAM/C,IAAMC,UAAN,MAAMA,SAAAA;EACX,OAAOC,UAAmB;AACxB,WAAO,IAAID,SAAAA;EACb;EAES,oBAAuC,CAAA;EAEvC,QAAiBE;EACjB,UAAoBA;EACpB;EACA;EAET,SAAuB;EACvB,kBAAqCA;EAErC,UAAmCA;EAE5BC,0BAA0BX;EAEjC,YAAYY,SAA6B,CAAC,GAAGC,UAAkC;AAC7E,SAAK,QAAQC,eAAeF,QAAQC,QAAAA;AACpC,SAAK,UAAUD,OAAOG;AACtB,SAAK,cAAcH,OAAOI,cAAc,CAAC;AACzC,SAAK,WAAWJ,OAAOK,WAAWhB;EACpC;EAEA,IAAI,cAAW;AACb,WAAO,CAAC,EAAE,KAAK,SAASK;EAC1B;EAEA,IAAI,YAAYY,OAAc;AAC5B,SAAK,SAASA,QAAQ,KAAK,SAASZ,2BAA2B,KAAK,SAAS,CAACA;EAChF;EAEA,IAAI,gBAAa;AACf,WAAO,CAAC,EAAE,KAAK,SAASC;EAC1B;EAEA,IAAI,cAAcW,OAAc;AAC9B,SAAK,SAASA,QAAQ,KAAK,SAASX,6BAA6B,KAAK,SAAS,CAACA;EAClF;EAEA,IAAIY,WAAW;AACb,WAAO,KAAK;EACd;EAEA,IAAIC,yBAAyB;AAC3B,WAAO,KAAK,kBAAkBC;EAChC;EAEA,IAAIC,SAAsB;AACxB,QAAI,KAAK,SAAS;AAChB,aAAO,KAAK;IACd;AACA,UAAMC,aAAa,IAAIC,gBAAAA;AACvB,SAAK,UAAUD,WAAWD;AAC1B,SAAKG,UAAU,MAAMF,WAAWG,MAAK,CAAA;AACrC,WAAO,KAAK;EACd;;;;;;;;;;EAWAD,UAAUE,UAAuC;AAC/C,QAAI,KAAK,aAAa;AAEpB,YAAM,YAAA;AACJ,YAAI;AACF,gBAAMA,SAAAA;QACR,SAASzB,OAAY;AACnB0B,cAAIC,MAAM3B,OAAO;YAAE4B,SAAS,KAAK;UAAM,CAAA;QACzC;MACF,GAAA;IACF;AAEA,SAAK,kBAAkBC,KAAKJ,QAAAA;AAC5B,QAAI,KAAK,kBAAkBN,SAAS,KAAKV,2BAA2B,CAAC,KAAK,eAAe;AACvF,WAAK,gBAAgB;AACrB,YAAMqB,WAAW,IAAIC,WAAAA,EAAaC,cAAc,CAAA,EAAG,CAAA,EAAGC,KAAI;AAC1DP,UAAIQ,KAAK,kFAAkF;QACzFN,SAAS,KAAK;QACdE;QACAK,OAAO,KAAK,kBAAkBhB;MAChC,CAAA;IACF;AAGA,WAAO,MAAA;AACL,YAAMiB,QAAQ,KAAK,kBAAkBC,QAAQZ,QAAAA;AAC7C,UAAIW,UAAU,IAAI;AAChB,aAAK,kBAAkBE,OAAOF,OAAO,CAAA;MACvC;IACF;EACF;;;;;;;;;EAUA,MAAMjC,QAAQoC,eAAe,OAAyB;AACpD,QAAI,KAAK,iBAAiB;AACxB,aAAO,KAAK;IACd;AAGA,SAAK,cAAc;AAGnB,QAAIC;AACJ,UAAMC,UAAU,IAAIC,QAAiB,CAACC,YAAAA;AACpCH,uBAAiBG;IACnB,CAAA;AACA,SAAK,kBAAkBF;AAIvB,UAAMG,YAAYC,MAAMC,KAAK,KAAK,iBAAiB,EAAEC,QAAO;AAC5D,SAAK,kBAAkB5B,SAAS;AAEhC,QAAItB,mBAAmB;AACrB6B,UAAI,aAAa;QAAEE,SAAS,KAAK;QAAOO,OAAOS,UAAUzB;MAAO,CAAA;IAClE;AAEA,QAAI6B,IAAI;AACR,QAAIC,QAAQ;AACZ,UAAMC,SAAkB,CAAA;AACxB,eAAWzB,YAAYmB,WAAW;AAChC,UAAI;AACF,cAAMnB,SAAAA;AACNuB;MACF,SAASG,KAAU;AACjBF,gBAAQ;AACR,YAAIV,cAAc;AAChBW,iBAAOrB,KAAKsB,GAAAA;QACd,OAAO;AACLzB,cAAIC,MAAMwB,KAAK;YAAEvB,SAAS,KAAK;YAAOH,UAAUuB;YAAGb,OAAOS,UAAUzB;UAAO,CAAA;QAC7E;MACF;IACF;AAEA,QAAI+B,OAAO/B,SAAS,GAAG;AACrB,YAAM,IAAIiC,eAAeF,MAAAA;IAC3B;AAEAV,mBAAeS,KAAAA;AACf,QAAIpD,mBAAmB;AACrB6B,UAAI,YAAY;QAAEE,SAAS,KAAK;MAAM,CAAA;IACxC;AAEA,WAAOqB;EACT;;;;;;EAOAI,MAAMrD,OAAoB;AACxB,QAAI,KAAK,aAAa;AAGpB;IACF;AAEA,QAAI;AACF,WAAK,SAASA,OAAO,IAAI;IAC3B,SAASmD,KAAK;AAEZ,WAAKT,QAAQY,OAAOH,GAAAA;IACtB;EACF;EAEAI,OAAO,EAAExC,SAASD,WAAU,IAAyB,CAAC,GAAY;AAChE,UAAM0C,SAAS,IAAIlD,SAAQ;MACzBO,QAAQ;;MAERE,SAAS,OAAOf,UAAAA;AACd,YAAI,CAACe,SAAS;AACZ,eAAKsC,MAAMrD,KAAAA;QACb,OAAO;AACL,cAAI;AACF,kBAAMe,QAAQf,OAAO,IAAI;UAC3B,QAAQ;AACN,iBAAKqD,MAAMrD,KAAAA;UACb;QACF;MACF;MACAc;IACF,CAAA;AAEA,UAAM2C,eAAe,KAAKlC,UAAU,MAAMiC,OAAOrD,QAAO,CAAA;AACxDqD,WAAOjC,UAAUkC,YAAAA;AACjB,WAAOD;EACT;EAEAE,aAAaC,KAAkB;AAC7B,QAAIA,OAAO,KAAK,aAAa;AAC3B,aAAO,KAAK,YAAYA,GAAAA;IAC1B;AACA,QAAI,KAAK,SAAS;AAChB,aAAO,KAAK,QAAQD,aAAaC,GAAAA;IACnC;AAEA,WAAOnD;EACT;EAEA,CAACoD,OAAOC,WAAW,IAAI;EACvB,CAACC,QAAQC,MAAM,IAAI,MAAM,KAAKC,SAAQ;EAEtCA,WAAmB;AACjB,WAAO,WAAW,KAAK,cAAc,aAAa,QAAA;EACpD;EAEA,OAAOJ,OAAOK,YAAY,IAAmB;AAC3C,UAAM,KAAK9D,QAAO;EACpB;AACF;;;;AAEA,IAAMS,iBAAiB,CAACF,QAA4BC,aAAAA;AAClD,MAAID,OAAOwD,MAAM;AACf,WAAOxD,OAAOwD;EAChB;AACA,MAAIvD,UAAUwD,GAAGhD,QAAQ;AACvB,UAAMiD,eAAezD,UAAUwD,EAAEE,MAAM,GAAA;AACvC,WAAO,GAAGD,aAAaA,aAAajD,SAAS,CAAA,CAAE,IAAIR,UAAU2D,KAAK,CAAA;EACpE;AACA,SAAO9D;AACT;;;AExRO,IAAM+D,kBAAkB,CAACC,KAAcC,QAAQ,IAAIC,qBAAAA,MACxD,IAAIC,QAAQ,CAACC,SAASC,WAAAA;AACpBL,MAAIM,UAAU,MAAMD,OAAOJ,KAAAA,CAAAA;AAC7B,CAAA;AAKK,IAAMM,oBAAoB,CAAIP,KAAcQ,YAAAA;AACjD,MAAIC;AACJ,SAAON,QAAQO,KAAK;IAClBF;IACA,IAAIL,QAAe,CAACC,SAASC,WAAAA;AAE3BI,qBAAeT,IAAIM,UAAU,MAAMD,OAAO,IAAIH,qBAAAA,CAAAA,CAAAA;IAChD,CAAA;GACD,EAAES,QAAQ,MAAMF,eAAAA,CAAAA;AACnB;;;ACxBA,OAAO;AAEP,SAASG,2BAA2B;AAI7B,IAAKC,iBAAAA,0BAAAA,iBAAAA;;;;SAAAA;;AAYZ,IAAMC,oCAAoC;AAKnC,IAAeC,WAAf,MAAeA;EACpB,kBAAe;EAEf,eAAqC;EACrC,gBAAsC;;;;;;EAOtC,eAAwB,KAAK,eAAc;;;;;EAM3C,aAAsB,KAAK,qBAAoB;;;;;;;;EAS/C,OAAOC,OAAOC,YAAY,IAAmB;AAC3C,UAAM,KAAKC,MAAK;EAClB;EAEA,IAAI,QAAK;AACP,WAAOC,OAAOC,eAAe,IAAI,EAAE,YAAYC;EACjD;EAEA,IAAIC,SAAS;AACX,WAAO,KAAK,oBAAe,UAA4B,KAAK,iBAAiB;EAC/E;EAEA,IAAcC,kBAAkB;AAC9B,WAAO,KAAK;EACd;EAEA,IAAcC,OAAO;AACnB,WAAO,KAAK;EACd;;;;EAKA,MAAgBC,MAAMD,MAA8B;EAAC;;;;EAKrD,MAAgBE,OAAOF,MAA8B;EAAC;;;;;EAMtD,MAAgBG,OAAOC,KAA2B;AAChD,QAAId,mCAAmC;AACrC,UAAI;AACF,cAAM,KAAKI,MAAK;MAClB,SAASW,WAAgB;AACvBC,4BAAoBD,SAAAA;MACtB;IACF;AACA,UAAMD;EACR;;;;;;EAOA,MAAMG,IAAOC,IAAgD;AAC3D,QAAI;AACF,YAAM,KAAKC,KAAI;AACf,aAAO,MAAMD,GAAG,IAAI;IACtB,UAAA;AACE,YAAM,KAAKd,MAAK;IAClB;EACF;;;;;;;;EASA,MAAMe,KAAKC,KAA8B;AACvC,YAAQ,KAAK,iBAAe;MAC1B,KAAA;AACE,eAAO;MACT,KAAA;AACE,cAAM,IAAIC,MAAM,kBAAkB,KAAK,eAAe,EAAE;MAC1D;IACF;AAEA,UAAM,KAAK;AACX,WAAO,KAAK,iBAAiB,KAAK,MAAMD,GAAAA;AACxC,WAAO;EACT;;;;;EAMA,MAAMhB,MAAMgB,KAA8B;AACxC,QAAI,KAAK,oBAAe,UAA4B;AAClD,aAAO;IACT;AACA,UAAM,KAAK;AACX,WAAO,KAAK,kBAAkB,KAAK,OAAOA,GAAAA;AAC1C,WAAO;EACT;;;;EAKA,MAAME,gBAA+B;AACnC,YAAQ,KAAK,iBAAe;MAC1B,KAAA;AACE;MACF,KAAA;AACE,cAAM,IAAID,MAAM,kBAAkB,KAAK,eAAe,EAAE;IAC5D;AAEA,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAIA,MAAM,8BAAA;IAClB;AACA,UAAM,KAAK;EACb;EAEA,MAAM,MAAMD,KAAa;AACvB,SAAK,gBAAgB;AACrB,SAAK,aAAaA,KAAKG,OAAO;MAAEhB,MAAM,KAAK;IAAM,CAAA,KAAM,KAAK,qBAAoB;AAChF,SAAK,eAAe,KAAK,eAAe,KAAK,UAAU;AACvD,UAAM,KAAKI,MAAM,KAAK,UAAU;AAChC,SAAK,kBAAe;EACtB;EAEA,MAAM,OAAOS,MAAMI,QAAQC,QAAO,GAAE;AAClC,SAAK,eAAe;AACpB,UAAM,KAAK,aAAaC,QAAO;AAC/B,UAAM,KAAKd,OAAOQ,GAAAA;AAClB,SAAK,eAAe,KAAK,eAAc;AACvC,SAAK,kBAAe;EACtB;EAEA,eAAeO,iBAAyB;AACtC,WAAO,IAAIH,QAAQ;MACjBjB,MAAM,KAAK;MACXqB,QAAQD;MACRE,SAAS,CAACC,UACRC,eAAe,YAAA;AACb,YAAI;AACF,gBAAM,KAAKlB,OAAOiB,KAAAA;QACpB,SAAShB,KAAU;AACjB,eAAK,kBAAe;AACpB,eAAK,WAAWkB,MAAMlB,GAAAA;QACxB;MACF,CAAA;IACJ,CAAA;EACF;EAEA,uBAAoB;AAClB,WAAO,IAAIU,QAAQ;MAAEjB,MAAM,KAAK;IAAM,CAAA;EACxC;AACF;AAEO,IAAM0B,gBAAgB,OAA4Bb,KAAcc,aAAAA;AACrE,QAAMA,SAASf,OAAOC,GAAAA;AACtBA,MAAIe,UAAU,MAAMD,SAAS9B,QAAK,CAAA;AAClC,SAAO8B;AACT;;;AClMO,IAAME,uBAAuB;AAiC7B,IAAMC,kBAAN,MAAMA;;;;;;;EAOX,OAAOC,OAAOC,KAA4C;AACxD,UAAMC,WAAWD,IAAIE,aAAaL,oBAAAA;AAClC,QAAII,YAAY,QAAQ,OAAOA,SAASE,gBAAgB,UAAU;AAChE,aAAOC;IACT;AACA,WAAOH;EACT;;;;;;;;EASA,OAAOI,OAAOC,cAAyC;AACrD,QAAI,OAAOA,aAAaH,gBAAgB,YAAYG,aAAaH,YAAYI,WAAW,GAAG;AACzF,aAAOC,QAAQC,QAAO;IACxB;AACA,WAAO,IAAID,QAAQ;MAAEE,YAAY;QAAE,CAACb,oBAAAA,GAAuBS;MAAa;IAAE,CAAA;EAC5E;AACF;",
6
+ "names": ["inspect", "StackTrace", "log", "safeInstanceof", "ContextDisposedError", "Error", "DEBUG_LOG_DISPOSE", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "ContextDisposedError", "dispose", "CONTEXT_FLAG_IS_DISPOSED", "CONTEXT_FLAG_LEAK_DETECTED", "Context", "default", "undefined", "maxSafeDisposeCallbacks", "params", "callMeta", "getContextName", "parent", "attributes", "onError", "value", "disposed", "disposeCallbacksLength", "length", "signal", "controller", "AbortController", "onDispose", "abort", "callback", "log", "catch", "context", "push", "callSite", "StackTrace", "getStackArray", "trim", "warn", "count", "index", "indexOf", "splice", "throwOnError", "resolveDispose", "promise", "Promise", "resolve", "callbacks", "Array", "from", "reverse", "i", "clean", "errors", "err", "AggregateError", "raise", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "Symbol", "toStringTag", "inspect", "custom", "toString", "asyncDispose", "name", "F", "pathSegments", "split", "L", "rejectOnDispose", "ctx", "error", "ContextDisposedError", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally", "throwUnhandledError", "LifecycleState", "CLOSE_RESOURCE_ON_UNHANDLED_ERROR", "Resource", "Symbol", "asyncDispose", "close", "Object", "getPrototypeOf", "name", "isOpen", "_lifecycleState", "_ctx", "_open", "_close", "_catch", "err", "doubleErr", "throwUnhandledError", "use", "fn", "open", "ctx", "Error", "waitUntilOpen", "derive", "Context", "default", "dispose", "attributeParent", "parent", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose", "TRACE_SPAN_ATTRIBUTE", "ContextRpcCodec", "encode", "ctx", "traceCtx", "getAttribute", "traceparent", "undefined", "decode", "traceContext", "length", "Context", "default", "attributes"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"src/context-disposed-error.ts":{"bytes":784,"imports":[],"format":"esm"},"src/context.ts":{"bytes":27720,"imports":[{"path":"@dxos/node-std/util","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"src/promise-utils.ts":{"bytes":3027,"imports":[{"path":"src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"src/resource.ts":{"bytes":15450,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"src/index.ts":{"bytes":764,"imports":[{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"},{"path":"src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"src/resource.ts","kind":"import-statement","original":"./resource"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":21795},"dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/util","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Context","ContextDisposedError","LifecycleState","Resource","cancelWithContext","openInContext","rejectOnDispose"],"entryPoint":"src/index.ts","inputs":{"src/context.ts":{"bytesInOutput":7325},"src/context-disposed-error.ts":{"bytesInOutput":106},"src/index.ts":{"bytesInOutput":0},"src/promise-utils.ts":{"bytesInOutput":410},"src/resource.ts":{"bytesInOutput":3938}},"bytes":12063}}}
1
+ {"inputs":{"src/context-disposed-error.ts":{"bytes":693,"imports":[],"format":"esm"},"src/context.ts":{"bytes":28046,"imports":[{"path":"@dxos/node-std/util","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"src/promise-utils.ts":{"bytes":2945,"imports":[{"path":"src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"src/resource.ts":{"bytes":17570,"imports":[{"path":"@hazae41/symbol-dispose-polyfill","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"src/trace-context.ts":{"bytes":6308,"imports":[{"path":"src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"src/index.ts":{"bytes":791,"imports":[{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"},{"path":"src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"src/resource.ts","kind":"import-statement","original":"./resource"},{"path":"src/trace-context.ts","kind":"import-statement","original":"./trace-context"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":26561},"dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/util","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@hazae41/symbol-dispose-polyfill","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Context","ContextDisposedError","ContextRpcCodec","LifecycleState","Resource","TRACE_SPAN_ATTRIBUTE","cancelWithContext","openInContext","rejectOnDispose"],"entryPoint":"src/index.ts","inputs":{"src/context.ts":{"bytesInOutput":6992},"src/context-disposed-error.ts":{"bytesInOutput":106},"src/index.ts":{"bytesInOutput":0},"src/promise-utils.ts":{"bytesInOutput":410},"src/resource.ts":{"bytesInOutput":4620},"src/trace-context.ts":{"bytesInOutput":1013}},"bytes":13493}}}
@@ -20,7 +20,6 @@ function _ts_decorate(decorators, target, key, desc) {
20
20
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21
21
  return c > 3 && r && Object.defineProperty(target, key, r), r;
22
22
  }
23
- var __dxlog_file = "/__w/dxos/dxos/packages/common/context/src/context.ts";
24
23
  var DEBUG_LOG_DISPOSE = false;
25
24
  var MAX_SAFE_DISPOSE_CALLBACKS = 300;
26
25
  var DEFAULT_ERROR_HANDLER = (error, ctx) => {
@@ -43,6 +42,7 @@ var Context = class _Context {
43
42
  #onError;
44
43
  #flags = 0;
45
44
  #disposePromise = void 0;
45
+ #signal = void 0;
46
46
  maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
47
47
  constructor(params = {}, callMeta) {
48
48
  this.#name = getContextName(params, callMeta);
@@ -68,6 +68,15 @@ var Context = class _Context {
68
68
  get disposeCallbacksLength() {
69
69
  return this.#disposeCallbacks.length;
70
70
  }
71
+ get signal() {
72
+ if (this.#signal) {
73
+ return this.#signal;
74
+ }
75
+ const controller = new AbortController();
76
+ this.#signal = controller.signal;
77
+ this.onDispose(() => controller.abort());
78
+ return this.#signal;
79
+ }
71
80
  /**
72
81
  * Schedules a callback to run when the context is disposed.
73
82
  * May be async, in this case the disposer might choose to wait for all resource to released.
@@ -85,11 +94,6 @@ var Context = class _Context {
85
94
  } catch (error) {
86
95
  log.catch(error, {
87
96
  context: this.#name
88
- }, {
89
- F: __dxlog_file,
90
- L: 119,
91
- S: this,
92
- C: (f, a) => f(...a)
93
97
  });
94
98
  }
95
99
  })();
@@ -102,11 +106,6 @@ var Context = class _Context {
102
106
  context: this.#name,
103
107
  callSite,
104
108
  count: this.#disposeCallbacks.length
105
- }, {
106
- F: __dxlog_file,
107
- L: 128,
108
- S: this,
109
- C: (f, a) => f(...a)
110
109
  });
111
110
  }
112
111
  return () => {
@@ -140,11 +139,6 @@ var Context = class _Context {
140
139
  log("disposing", {
141
140
  context: this.#name,
142
141
  count: callbacks.length
143
- }, {
144
- F: __dxlog_file,
145
- L: 173,
146
- S: this,
147
- C: (f, a) => f(...a)
148
142
  });
149
143
  }
150
144
  let i = 0;
@@ -163,11 +157,6 @@ var Context = class _Context {
163
157
  context: this.#name,
164
158
  callback: i,
165
159
  count: callbacks.length
166
- }, {
167
- F: __dxlog_file,
168
- L: 188,
169
- S: this,
170
- C: (f, a) => f(...a)
171
160
  });
172
161
  }
173
162
  }
@@ -179,11 +168,6 @@ var Context = class _Context {
179
168
  if (DEBUG_LOG_DISPOSE) {
180
169
  log("disposed", {
181
170
  context: this.#name
182
- }, {
183
- F: __dxlog_file,
184
- L: 199,
185
- S: this,
186
- C: (f, a) => f(...a)
187
171
  });
188
172
  }
189
173
  return clean;
@@ -205,6 +189,7 @@ var Context = class _Context {
205
189
  }
206
190
  derive({ onError, attributes } = {}) {
207
191
  const newCtx = new _Context({
192
+ parent: this,
208
193
  // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.
209
194
  onError: async (error) => {
210
195
  if (!onError) {
@@ -270,13 +255,14 @@ var cancelWithContext = (ctx, promise) => {
270
255
  };
271
256
 
272
257
  // src/resource.ts
258
+ import "@hazae41/symbol-dispose-polyfill";
273
259
  import { throwUnhandledError } from "@dxos/util";
274
- var LifecycleState = /* @__PURE__ */ function(LifecycleState2) {
260
+ var LifecycleState = /* @__PURE__ */ (function(LifecycleState2) {
275
261
  LifecycleState2["CLOSED"] = "CLOSED";
276
262
  LifecycleState2["OPEN"] = "OPEN";
277
263
  LifecycleState2["ERROR"] = "ERROR";
278
264
  return LifecycleState2;
279
- }({});
265
+ })({});
280
266
  var CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;
281
267
  var Resource = class {
282
268
  #lifecycleState = "CLOSED";
@@ -293,6 +279,16 @@ var Resource = class {
293
279
  * Provided in the open method.
294
280
  */
295
281
  #parentCtx = this.#createParentContext();
282
+ /**
283
+ * ```ts
284
+ * await using resource = new Resource();
285
+ * await resource.open();
286
+ * ```
287
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
288
+ */
289
+ async [Symbol.asyncDispose]() {
290
+ await this.close();
291
+ }
296
292
  get #name() {
297
293
  return Object.getPrototypeOf(this).constructor.name;
298
294
  }
@@ -308,12 +304,12 @@ var Resource = class {
308
304
  /**
309
305
  * To be overridden by subclasses.
310
306
  */
311
- async _open(ctx) {
307
+ async _open(_ctx) {
312
308
  }
313
309
  /**
314
310
  * To be overridden by subclasses.
315
311
  */
316
- async _close(ctx) {
312
+ async _close(_ctx) {
317
313
  }
318
314
  /**
319
315
  * Error handler for errors that are caught by the context.
@@ -330,6 +326,19 @@ var Resource = class {
330
326
  throw err;
331
327
  }
332
328
  /**
329
+ * Calls the provided function, opening and closing the resource.
330
+ * NOTE: Consider using `using` instead.
331
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
332
+ */
333
+ async use(fn) {
334
+ try {
335
+ await this.open();
336
+ return await fn(this);
337
+ } finally {
338
+ await this.close();
339
+ }
340
+ }
341
+ /**
333
342
  * Opens the resource.
334
343
  * If the resource is already open, it does nothing.
335
344
  * If the resource is in an error state, it throws an error.
@@ -375,14 +384,12 @@ var Resource = class {
375
384
  }
376
385
  await this.#openPromise;
377
386
  }
378
- async [Symbol.asyncDispose]() {
379
- await this.close();
380
- }
381
387
  async #open(ctx) {
382
388
  this.#closePromise = null;
383
389
  this.#parentCtx = ctx?.derive({
384
390
  name: this.#name
385
391
  }) ?? this.#createParentContext();
392
+ this.#internalCtx = this.#createContext(this.#parentCtx);
386
393
  await this._open(this.#parentCtx);
387
394
  this.#lifecycleState = "OPEN";
388
395
  }
@@ -393,9 +400,10 @@ var Resource = class {
393
400
  this.#internalCtx = this.#createContext();
394
401
  this.#lifecycleState = "CLOSED";
395
402
  }
396
- #createContext() {
403
+ #createContext(attributeParent) {
397
404
  return new Context({
398
405
  name: this.#name,
406
+ parent: attributeParent,
399
407
  onError: (error) => queueMicrotask(async () => {
400
408
  try {
401
409
  await this._catch(error);
@@ -417,11 +425,48 @@ var openInContext = async (ctx, resource) => {
417
425
  ctx.onDispose(() => resource.close?.());
418
426
  return resource;
419
427
  };
428
+
429
+ // src/trace-context.ts
430
+ var TRACE_SPAN_ATTRIBUTE = "dxos.trace-span";
431
+ var ContextRpcCodec = class {
432
+ /**
433
+ * Read the W3C trace context from a DXOS `Context` for an outgoing RPC.
434
+ *
435
+ * @returns `TraceContextData` to attach to the wire message, or `undefined`
436
+ * if the context has no active trace.
437
+ */
438
+ static encode(ctx) {
439
+ const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE);
440
+ if (traceCtx == null || typeof traceCtx.traceparent !== "string") {
441
+ return void 0;
442
+ }
443
+ return traceCtx;
444
+ }
445
+ /**
446
+ * Reconstruct a DXOS `Context` from W3C trace context received in an
447
+ * incoming RPC request.
448
+ *
449
+ * @returns A `Context` carrying the trace context, or `Context.default()`
450
+ * if the data is missing/invalid.
451
+ */
452
+ static decode(traceContext) {
453
+ if (typeof traceContext.traceparent !== "string" || traceContext.traceparent.length === 0) {
454
+ return Context.default();
455
+ }
456
+ return new Context({
457
+ attributes: {
458
+ [TRACE_SPAN_ATTRIBUTE]: traceContext
459
+ }
460
+ });
461
+ }
462
+ };
420
463
  export {
421
464
  Context,
422
465
  ContextDisposedError,
466
+ ContextRpcCodec,
423
467
  LifecycleState,
424
468
  Resource,
469
+ TRACE_SPAN_ATTRIBUTE,
425
470
  cancelWithContext,
426
471
  openInContext,
427
472
  rejectOnDispose