@dxos/context 0.5.9-next.a50ff17 → 0.6.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.
@@ -22,6 +22,7 @@ function _ts_decorate(decorators, target, key, desc) {
22
22
  return c > 3 && r && Object.defineProperty(target, key, r), r;
23
23
  }
24
24
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/context/src/context.ts";
25
+ var DEBUG_LOG_DISPOSE = false;
25
26
  var MAX_SAFE_DISPOSE_CALLBACKS = 300;
26
27
  var DEFAULT_ERROR_HANDLER = (error, ctx) => {
27
28
  if (error instanceof ContextDisposedError) {
@@ -32,7 +33,7 @@ var DEFAULT_ERROR_HANDLER = (error, ctx) => {
32
33
  };
33
34
  var _a, _b;
34
35
  var Context = class _Context {
35
- constructor({ name, parent, attributes = {}, onError = DEFAULT_ERROR_HANDLER } = {}) {
36
+ constructor(params = {}, callMeta) {
36
37
  this.#disposeCallbacks = [];
37
38
  this.#name = void 0;
38
39
  this.#parent = void 0;
@@ -41,10 +42,10 @@ var Context = class _Context {
41
42
  this.maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
42
43
  this[_a] = "Context";
43
44
  this[_b] = () => this.toString();
44
- this.#name = name;
45
- this.#parent = parent;
46
- this.#attributes = attributes;
47
- this.#onError = onError;
45
+ this.#name = getContextName(params, callMeta);
46
+ this.#parent = params.parent;
47
+ this.#attributes = params.attributes ?? {};
48
+ this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;
48
49
  }
49
50
  static default() {
50
51
  return new _Context();
@@ -77,9 +78,11 @@ var Context = class _Context {
77
78
  try {
78
79
  await callback();
79
80
  } catch (error) {
80
- log.catch(error, void 0, {
81
+ log.catch(error, {
82
+ context: this.#name
83
+ }, {
81
84
  F: __dxlog_file,
82
- L: 88,
85
+ L: 90,
83
86
  S: this,
84
87
  C: (f, a) => f(...a)
85
88
  });
@@ -89,10 +92,11 @@ var Context = class _Context {
89
92
  this.#disposeCallbacks.push(callback);
90
93
  if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks) {
91
94
  log.warn("Context has a large number of dispose callbacks (this might be a memory leak).", {
95
+ context: this.#name,
92
96
  count: this.#disposeCallbacks.length
93
97
  }, {
94
98
  F: __dxlog_file,
95
- L: 95,
99
+ L: 97,
96
100
  S: this,
97
101
  C: (f, a) => f(...a)
98
102
  });
@@ -124,13 +128,13 @@ var Context = class _Context {
124
128
  this.#disposePromise = promise;
125
129
  const callbacks = Array.from(this.#disposeCallbacks).reverse();
126
130
  this.#disposeCallbacks.length = 0;
127
- if (this.#name) {
131
+ if (DEBUG_LOG_DISPOSE) {
128
132
  log("disposing", {
129
133
  context: this.#name,
130
134
  count: callbacks.length
131
135
  }, {
132
136
  F: __dxlog_file,
133
- L: 138,
137
+ L: 141,
134
138
  S: this,
135
139
  C: (f, a) => f(...a)
136
140
  });
@@ -153,7 +157,7 @@ var Context = class _Context {
153
157
  count: callbacks.length
154
158
  }, {
155
159
  F: __dxlog_file,
156
- L: 153,
160
+ L: 156,
157
161
  S: this,
158
162
  C: (f, a) => f(...a)
159
163
  });
@@ -164,12 +168,12 @@ var Context = class _Context {
164
168
  throw new AggregateError(errors);
165
169
  }
166
170
  resolveDispose(clean);
167
- if (this.#name) {
171
+ if (DEBUG_LOG_DISPOSE) {
168
172
  log("disposed", {
169
173
  context: this.#name
170
174
  }, {
171
175
  F: __dxlog_file,
172
- L: 164,
176
+ L: 167,
173
177
  S: this,
174
178
  C: (f, a) => f(...a)
175
179
  });
@@ -230,6 +234,16 @@ var Context = class _Context {
230
234
  Context = _ts_decorate([
231
235
  safeInstanceof("Context")
232
236
  ], Context);
237
+ var getContextName = (params, callMeta) => {
238
+ if (params.name) {
239
+ return params.name;
240
+ }
241
+ if (callMeta?.F?.length) {
242
+ const pathSegments = callMeta?.F.split("/");
243
+ return `${pathSegments[pathSegments.length - 1]}#${callMeta?.L ?? 0}`;
244
+ }
245
+ return void 0;
246
+ };
233
247
 
234
248
  // packages/common/context/src/promise-utils.ts
235
249
  var rejectOnDispose = (ctx, error = new ContextDisposedError()) => new Promise((resolve, reject) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 { 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\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\n@safeInstanceof('Context')\nexport class Context {\n static default() {\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 #isDisposed = false;\n #disposePromise?: Promise<boolean> = undefined;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor({ name, parent, attributes = {}, onError = DEFAULT_ERROR_HANDLER }: CreateContextParams = {}) {\n this.#name = name;\n this.#parent = parent;\n this.#attributes = attributes;\n this.#onError = onError;\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);\n }\n })();\n }\n\n this.#disposeCallbacks.push(callback);\n if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks) {\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\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 (this.#name) {\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 (this.#name) {\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() {\n return `Context(${this.#isDisposed ? 'disposed' : 'active'})`;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\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 = new Context({ name: this.#name });\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\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 async [Symbol.asyncDispose]() {\n await this.close();\n }\n\n async #open(ctx?: Context) {\n this.#closePromise = null;\n if (ctx) {\n this.#parentCtx = ctx.derive({ name: this.#name });\n }\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()) {\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() {\n return new Context({\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\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n\n return resource;\n};\n"],
5
- "mappings": "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",
6
- "names": ["inspect", "log", "safeInstanceof", "ContextDisposedError", "Error", "constructor", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "ContextDisposedError", "dispose", "Context", "constructor", "name", "parent", "attributes", "onError", "undefined", "maxSafeDisposeCallbacks", "Symbol", "inspect", "toString", "default", "disposed", "disposeCallbacksLength", "length", "onDispose", "callback", "log", "catch", "push", "warn", "count", "index", "indexOf", "splice", "throwOnError", "resolveDispose", "promise", "Promise", "resolve", "callbacks", "Array", "from", "reverse", "context", "i", "clean", "errors", "err", "AggregateError", "raise", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "toStringTag", "custom", "asyncDispose", "safeInstanceof", "rejectOnDispose", "ctx", "error", "ContextDisposedError", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally", "throwUnhandledError", "LifecycleState", "CLOSE_RESOURCE_ON_UNHANDLED_ERROR", "Resource", "Context", "name", "Object", "getPrototypeOf", "constructor", "_lifecycleState", "_ctx", "_open", "ctx", "_close", "_catch", "err", "close", "doubleErr", "throwUnhandledError", "open", "Error", "Symbol", "asyncDispose", "derive", "default", "dispose", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose"]
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { inspect } from 'node:util';\n\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\n@safeInstanceof('Context')\nexport class Context {\n static default() {\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 #isDisposed = false;\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 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) {\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\n context: this.#name,\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() {\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 = new Context({ name: this.#name });\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\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 async [Symbol.asyncDispose]() {\n await this.close();\n }\n\n async #open(ctx?: Context) {\n this.#closePromise = null;\n if (ctx) {\n this.#parentCtx = ctx.derive({ name: this.#name });\n }\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()) {\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() {\n return new Context({\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\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n\n return resource;\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["inspect", "log", "safeInstanceof", "ContextDisposedError", "Error", "constructor", "DEBUG_LOG_DISPOSE", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "ContextDisposedError", "dispose", "Context", "constructor", "params", "callMeta", "undefined", "maxSafeDisposeCallbacks", "Symbol", "inspect", "toString", "getContextName", "parent", "attributes", "onError", "default", "disposed", "disposeCallbacksLength", "length", "onDispose", "callback", "log", "catch", "context", "push", "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", "toStringTag", "custom", "asyncDispose", "safeInstanceof", "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", "Context", "name", "Object", "getPrototypeOf", "constructor", "_lifecycleState", "_ctx", "_open", "ctx", "_close", "_catch", "err", "close", "doubleErr", "throwUnhandledError", "open", "Error", "Symbol", "asyncDispose", "derive", "default", "dispose", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/common/context/src/context-disposed-error.ts":{"bytes":853,"imports":[],"format":"esm"},"packages/common/context/src/context.ts":{"bytes":23328,"imports":[{"path":"@dxos/node-std/util","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":3096,"imports":[{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/resource.ts":{"bytes":13662,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":809,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"},{"path":"packages/common/context/src/resource.ts","kind":"import-statement","original":"./resource"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18404},"packages/common/context/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/util","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":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/context.ts":{"bytesInOutput":6400},"packages/common/context/src/context-disposed-error.ts":{"bytesInOutput":106},"packages/common/context/src/index.ts":{"bytesInOutput":0},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":410},"packages/common/context/src/resource.ts":{"bytesInOutput":3372}},"bytes":10692}}}
1
+ {"inputs":{"packages/common/context/src/context-disposed-error.ts":{"bytes":853,"imports":[],"format":"esm"},"packages/common/context/src/context.ts":{"bytes":25119,"imports":[{"path":"@dxos/node-std/util","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":3096,"imports":[{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/resource.ts":{"bytes":13662,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":809,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"},{"path":"packages/common/context/src/resource.ts","kind":"import-statement","original":"./resource"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":19359},"packages/common/context/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/util","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":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/context.ts":{"bytesInOutput":6814},"packages/common/context/src/context-disposed-error.ts":{"bytesInOutput":106},"packages/common/context/src/index.ts":{"bytesInOutput":0},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":410},"packages/common/context/src/resource.ts":{"bytesInOutput":3372}},"bytes":11106}}}
@@ -47,6 +47,7 @@ function _ts_decorate(decorators, target, key, desc) {
47
47
  return c > 3 && r && Object.defineProperty(target, key, r), r;
48
48
  }
49
49
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/context/src/context.ts";
50
+ var DEBUG_LOG_DISPOSE = false;
50
51
  var MAX_SAFE_DISPOSE_CALLBACKS = 300;
51
52
  var DEFAULT_ERROR_HANDLER = (error, ctx) => {
52
53
  if (error instanceof ContextDisposedError) {
@@ -57,7 +58,7 @@ var DEFAULT_ERROR_HANDLER = (error, ctx) => {
57
58
  };
58
59
  var _a, _b;
59
60
  var Context = class _Context {
60
- constructor({ name, parent, attributes = {}, onError = DEFAULT_ERROR_HANDLER } = {}) {
61
+ constructor(params = {}, callMeta) {
61
62
  this.#disposeCallbacks = [];
62
63
  this.#name = void 0;
63
64
  this.#parent = void 0;
@@ -66,10 +67,10 @@ var Context = class _Context {
66
67
  this.maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
67
68
  this[_a] = "Context";
68
69
  this[_b] = () => this.toString();
69
- this.#name = name;
70
- this.#parent = parent;
71
- this.#attributes = attributes;
72
- this.#onError = onError;
70
+ this.#name = getContextName(params, callMeta);
71
+ this.#parent = params.parent;
72
+ this.#attributes = params.attributes ?? {};
73
+ this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;
73
74
  }
74
75
  static default() {
75
76
  return new _Context();
@@ -102,9 +103,11 @@ var Context = class _Context {
102
103
  try {
103
104
  await callback();
104
105
  } catch (error) {
105
- import_log.log.catch(error, void 0, {
106
+ import_log.log.catch(error, {
107
+ context: this.#name
108
+ }, {
106
109
  F: __dxlog_file,
107
- L: 88,
110
+ L: 90,
108
111
  S: this,
109
112
  C: (f, a) => f(...a)
110
113
  });
@@ -114,10 +117,11 @@ var Context = class _Context {
114
117
  this.#disposeCallbacks.push(callback);
115
118
  if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks) {
116
119
  import_log.log.warn("Context has a large number of dispose callbacks (this might be a memory leak).", {
120
+ context: this.#name,
117
121
  count: this.#disposeCallbacks.length
118
122
  }, {
119
123
  F: __dxlog_file,
120
- L: 95,
124
+ L: 97,
121
125
  S: this,
122
126
  C: (f, a) => f(...a)
123
127
  });
@@ -149,13 +153,13 @@ var Context = class _Context {
149
153
  this.#disposePromise = promise;
150
154
  const callbacks = Array.from(this.#disposeCallbacks).reverse();
151
155
  this.#disposeCallbacks.length = 0;
152
- if (this.#name) {
156
+ if (DEBUG_LOG_DISPOSE) {
153
157
  (0, import_log.log)("disposing", {
154
158
  context: this.#name,
155
159
  count: callbacks.length
156
160
  }, {
157
161
  F: __dxlog_file,
158
- L: 138,
162
+ L: 141,
159
163
  S: this,
160
164
  C: (f, a) => f(...a)
161
165
  });
@@ -178,7 +182,7 @@ var Context = class _Context {
178
182
  count: callbacks.length
179
183
  }, {
180
184
  F: __dxlog_file,
181
- L: 153,
185
+ L: 156,
182
186
  S: this,
183
187
  C: (f, a) => f(...a)
184
188
  });
@@ -189,12 +193,12 @@ var Context = class _Context {
189
193
  throw new AggregateError(errors);
190
194
  }
191
195
  resolveDispose(clean);
192
- if (this.#name) {
196
+ if (DEBUG_LOG_DISPOSE) {
193
197
  (0, import_log.log)("disposed", {
194
198
  context: this.#name
195
199
  }, {
196
200
  F: __dxlog_file,
197
- L: 164,
201
+ L: 167,
198
202
  S: this,
199
203
  C: (f, a) => f(...a)
200
204
  });
@@ -255,6 +259,16 @@ var Context = class _Context {
255
259
  Context = _ts_decorate([
256
260
  (0, import_util.safeInstanceof)("Context")
257
261
  ], Context);
262
+ var getContextName = (params, callMeta) => {
263
+ if (params.name) {
264
+ return params.name;
265
+ }
266
+ if (callMeta?.F?.length) {
267
+ const pathSegments = callMeta?.F.split("/");
268
+ return `${pathSegments[pathSegments.length - 1]}#${callMeta?.L ?? 0}`;
269
+ }
270
+ return void 0;
271
+ };
258
272
  var rejectOnDispose = (ctx, error = new ContextDisposedError()) => new Promise((resolve, reject) => {
259
273
  ctx.onDispose(() => reject(error));
260
274
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 { 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\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\n@safeInstanceof('Context')\nexport class Context {\n static default() {\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 #isDisposed = false;\n #disposePromise?: Promise<boolean> = undefined;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor({ name, parent, attributes = {}, onError = DEFAULT_ERROR_HANDLER }: CreateContextParams = {}) {\n this.#name = name;\n this.#parent = parent;\n this.#attributes = attributes;\n this.#onError = onError;\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);\n }\n })();\n }\n\n this.#disposeCallbacks.push(callback);\n if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks) {\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\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 (this.#name) {\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 (this.#name) {\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() {\n return `Context(${this.#isDisposed ? 'disposed' : 'active'})`;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\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 = new Context({ name: this.#name });\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\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 async [Symbol.asyncDispose]() {\n await this.close();\n }\n\n async #open(ctx?: Context) {\n this.#closePromise = null;\n if (ctx) {\n this.#parentCtx = ctx.derive({ name: this.#name });\n }\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()) {\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() {\n return new Context({\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\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n\n return resource;\n};\n"],
5
- "mappings": "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",
6
- "names": ["import_util", "ContextDisposedError", "Error", "constructor", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "dispose", "Context", "name", "parent", "attributes", "onError", "undefined", "maxSafeDisposeCallbacks", "Symbol", "inspect", "toString", "default", "disposed", "disposeCallbacksLength", "length", "onDispose", "callback", "log", "catch", "push", "warn", "count", "index", "indexOf", "splice", "throwOnError", "resolveDispose", "promise", "Promise", "resolve", "callbacks", "Array", "from", "reverse", "context", "i", "clean", "errors", "err", "AggregateError", "raise", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "toStringTag", "custom", "asyncDispose", "safeInstanceof", "rejectOnDispose", "cancelWithContext", "race", "finally", "LifecycleState", "CLOSE_RESOURCE_ON_UNHANDLED_ERROR", "Resource", "Object", "getPrototypeOf", "_lifecycleState", "_ctx", "_open", "_close", "_catch", "close", "doubleErr", "throwUnhandledError", "open", "queueMicrotask", "openInContext", "resource"]
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { inspect } from 'node:util';\n\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\n@safeInstanceof('Context')\nexport class Context {\n static default() {\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 #isDisposed = false;\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 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) {\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\n context: this.#name,\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() {\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 = new Context({ name: this.#name });\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\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 async [Symbol.asyncDispose]() {\n await this.close();\n }\n\n async #open(ctx?: Context) {\n this.#closePromise = null;\n if (ctx) {\n this.#parentCtx = ctx.derive({ name: this.#name });\n }\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()) {\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() {\n return new Context({\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\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n\n return resource;\n};\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,uBAAwB;AAExB,iBAAuC;AACvC,kBAA+B;AGH/B,IAAAA,eAAoC;AFA7B,IAAMC,uBAAN,cAAmCC,MAAAA;EACxCC,cAAc;AACZ,UAAM,mBAAA;EACR;AACF;;;;;;;;;;;;ADcA,IAAMC,oBAAoB;AAK1B,IAAMC,6BAA6B;AAEnC,IAAMC,wBAA6C,CAACC,OAAOC,QAAAA;AACzD,MAAID,iBAAiBN,sBAAsB;AACzC;EACF;AAEA,OAAKO,IAAIC,QAAO;AAGhB,QAAMF;AACR;AAtCA,IAAA,IAAA;AAyCO,IAAMG,UAAN,MAAMA,SAAAA;EAiBXP,YAAYQ,SAA8B,CAAC,GAAGC,UAAkC;AAZvE,SAAA,oBAAuC,CAAA;AAEvC,SAAA,QAAiBC;AACjB,SAAA,UAAoBA;AAI7B,SAAA,cAAc;AACd,SAAA,kBAAqCA;AAE9BC,SAAAA,0BAA0BT;AAyKjC,SAACU,EAAAA,IAAsB;AACvB,SAACC,EAAAA,IAAkB,MAAM,KAAKC,SAAQ;AAvKpC,SAAK,QAAQC,eAAeP,QAAQC,QAAAA;AACpC,SAAK,UAAUD,OAAOQ;AACtB,SAAK,cAAcR,OAAOS,cAAc,CAAC;AACzC,SAAK,WAAWT,OAAOU,WAAWf;EACpC;EArBA,OAAOgB,UAAU;AACf,WAAO,IAAIZ,SAAAA;EACb;EAES;EAEA;EACA;EACA;EACA;EAET;EACA;EAWA,IAAIa,WAAW;AACb,WAAO,KAAK;EACd;EAEA,IAAIC,yBAAyB;AAC3B,WAAO,KAAK,kBAAkBC;EAChC;;;;;;;;;;EAWAC,UAAUC,UAAuC;AAC/C,QAAI,KAAK,aAAa;AAEpB,YAAM,YAAA;AACJ,YAAI;AACF,gBAAMA,SAAAA;QACR,SAASpB,OAAY;AACnBqB,yBAAIC,MAAMtB,OAAO;YAAEuB,SAAS,KAAK;UAAM,GAAA;;;;;;QACzC;MACF,GAAA;IACF;AAEA,SAAK,kBAAkBC,KAAKJ,QAAAA;AAC5B,QAAI,KAAK,kBAAkBF,SAAS,KAAKX,yBAAyB;AAChEc,qBAAII,KAAK,kFAAkF;QACzFF,SAAS,KAAK;QACdG,OAAO,KAAK,kBAAkBR;MAChC,GAAA;;;;;;IACF;AAGA,WAAO,MAAA;AACL,YAAMS,QAAQ,KAAK,kBAAkBC,QAAQR,QAAAA;AAC7C,UAAIO,UAAU,IAAI;AAChB,aAAK,kBAAkBE,OAAOF,OAAO,CAAA;MACvC;IACF;EACF;;;;;;;;;EAUA,MAAMzB,QAAQ4B,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,kBAAkBpB,SAAS;AAEhC,QAAIrB,mBAAmB;AACrBwB,0BAAI,aAAa;QAAEE,SAAS,KAAK;QAAOG,OAAOS,UAAUjB;MAAO,GAAA;;;;;;IAClE;AAEA,QAAIqB,IAAI;AACR,QAAIC,QAAQ;AACZ,UAAMC,SAAkB,CAAA;AACxB,eAAWrB,YAAYe,WAAW;AAChC,UAAI;AACF,cAAMf,SAAAA;AACNmB;MACF,SAASG,KAAU;AACjBF,gBAAQ;AACR,YAAIV,cAAc;AAChBW,iBAAOjB,KAAKkB,GAAAA;QACd,OAAO;AACLrB,yBAAIC,MAAMoB,KAAK;YAAEnB,SAAS,KAAK;YAAOH,UAAUmB;YAAGb,OAAOS,UAAUjB;UAAO,GAAA;;;;;;QAC7E;MACF;IACF;AAEA,QAAIuB,OAAOvB,SAAS,GAAG;AACrB,YAAM,IAAIyB,eAAeF,MAAAA;IAC3B;AAEAV,mBAAeS,KAAAA;AACf,QAAI3C,mBAAmB;AACrBwB,0BAAI,YAAY;QAAEE,SAAS,KAAK;MAAM,GAAA;;;;;;IACxC;AAEA,WAAOiB;EACT;;;;;;EAOAI,MAAM5C,OAAoB;AACxB,QAAI,KAAK,aAAa;AAGpB;IACF;AAEA,QAAI;AACF,WAAK,SAASA,OAAO,IAAI;IAC3B,SAAS0C,KAAK;AAEZ,WAAKT,QAAQY,OAAOH,GAAAA;IACtB;EACF;EAEAI,OAAO,EAAEhC,SAASD,WAAU,IAA0B,CAAC,GAAY;AACjE,UAAMkC,SAAS,IAAI5C,SAAQ;;MAEzBW,SAAS,OAAOd,UAAAA;AACd,YAAI,CAACc,SAAS;AACZ,eAAK8B,MAAM5C,KAAAA;QACb,OAAO;AACL,cAAI;AACF,kBAAMc,QAAQd,OAAO,IAAI;UAC3B,QAAQ;AACN,iBAAK4C,MAAM5C,KAAAA;UACb;QACF;MACF;MACAa;IACF,CAAA;AAEA,UAAMmC,eAAe,KAAK7B,UAAU,MAAM4B,OAAO7C,QAAO,CAAA;AACxD6C,WAAO5B,UAAU6B,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,WAAO5C;EACT;EAKAI,WAAW;AACT,WAAO,WAAW,KAAK,cAAc,aAAa,QAAA;EACpD;EAEA,QAPCF,KAAAA,OAAO2C,aACP1C,KAAAA,yBAAQ2C,QAMF5C,OAAO6C,aAAY,IAAmB;AAC3C,UAAM,KAAKnD,QAAO;EACpB;AACF;AAlMaC,UAAAA,aAAAA;MADZmD,4BAAe,SAAA;GACHnD,OAAAA;AAoMb,IAAMQ,iBAAiB,CAACP,QAA6BC,aAAAA;AACnD,MAAID,OAAOmD,MAAM;AACf,WAAOnD,OAAOmD;EAChB;AACA,MAAIlD,UAAUmD,GAAGtC,QAAQ;AACvB,UAAMuC,eAAepD,UAAUmD,EAAEE,MAAM,GAAA;AACvC,WAAO,GAAGD,aAAaA,aAAavC,SAAS,CAAA,CAAE,IAAIb,UAAUsD,KAAK,CAAA;EACpE;AACA,SAAOrD;AACT;AE3OO,IAAMsD,kBAAkB,CAAC3D,KAAcD,QAAQ,IAAIN,qBAAAA,MACxD,IAAIuC,QAAQ,CAACC,SAASW,WAAAA;AACpB5C,MAAIkB,UAAU,MAAM0B,OAAO7C,KAAAA,CAAAA;AAC7B,CAAA;AAKK,IAAM6D,oBAAoB,CAAI5D,KAAc+B,YAAAA;AACjD,MAAIgB;AACJ,SAAOf,QAAQ6B,KAAK;IAClB9B;IACA,IAAIC,QAAe,CAACC,SAASW,WAAAA;AAE3BG,qBAAe/C,IAAIkB,UAAU,MAAM0B,OAAO,IAAInD,qBAAAA,CAAAA,CAAAA;IAChD,CAAA;GACD,EAAEqE,QAAQ,MAAMf,eAAAA,CAAAA;AACnB;;UCpBYgB,iBAAAA;;;;GAAAA,mBAAAA,iBAAAA,CAAAA,EAAAA;AAYZ,IAAMC,oCAAoC;AAKnC,IAAeC,WAAf,MAAeA;EACpB,kBAAe;EACf,eAAqC;EACrC,gBAAsC;;;;;;EAOtC,eAAwB,KAAK,eAAc;;;;;EAM3C,aAAsB,IAAI/D,QAAQ;IAAEoD,MAAM,KAAK;EAAM,CAAA;EAErD,IAAI,QAAK;AACP,WAAOY,OAAOC,eAAe,IAAI,EAAExE,YAAY2D;EACjD;EAEA,IAAcc,kBAAkB;AAC9B,WAAO,KAAK;EACd;EAEA,IAAcC,OAAO;AACnB,WAAO,KAAK;EACd;;;;EAKA,MAAgBC,MAAMtE,KAA6B;EAAC;;;;EAKpD,MAAgBuE,OAAOvE,KAA6B;EAAC;;;;;EAMrD,MAAgBwE,OAAO/B,KAA2B;AAChD,QAAIuB,mCAAmC;AACrC,UAAI;AACF,cAAM,KAAKS,MAAK;MAClB,SAASC,WAAgB;AACvBC,8CAAoBD,SAAAA;MACtB;IACF;AACA,UAAMjC;EACR;;;;;;;;EASA,MAAMmC,KAAK5E,KAA8B;AACvC,YAAQ,KAAK,iBAAe;MAC1B,KAAA;AACE,eAAO;MACT,KAAA;AACE,cAAM,IAAIN,MAAM,kBAAkB,KAAK,eAAe,EAAE;MAC1D;IACF;AAEA,UAAM,KAAK;AACX,WAAO,KAAK,iBAAiB,KAAK,MAAMM,GAAAA;AAExC,WAAO;EACT;;;;;EAMA,MAAMyE,MAAMzE,KAA8B;AACxC,QAAI,KAAK,oBAAe,UAA4B;AAClD,aAAO;IACT;AACA,UAAM,KAAK;AACX,WAAO,KAAK,kBAAkB,KAAK,OAAOA,GAAAA;AAE1C,WAAO;EACT;EAEA,OAAOO,OAAO6C,YAAY,IAAI;AAC5B,UAAM,KAAKqB,MAAK;EAClB;EAEA,MAAM,MAAMzE,KAAa;AACvB,SAAK,gBAAgB;AACrB,QAAIA,KAAK;AACP,WAAK,aAAaA,IAAI6C,OAAO;QAAES,MAAM,KAAK;MAAM,CAAA;IAClD;AACA,UAAM,KAAKgB,MAAM,KAAK,UAAU;AAChC,SAAK,kBAAe;EACtB;EAEA,MAAM,OAAOtE,MAAME,QAAQY,QAAO,GAAE;AAClC,SAAK,eAAe;AACpB,UAAM,KAAK,aAAab,QAAO;AAC/B,UAAM,KAAKsE,OAAOvE,GAAAA;AAClB,SAAK,eAAe,KAAK,eAAc;AACvC,SAAK,kBAAe;EACtB;EAEA,iBAAc;AACZ,WAAO,IAAIE,QAAQ;MACjBW,SAAS,CAACd,UACR8E,eAAe,YAAA;AACb,YAAI;AACF,gBAAM,KAAKL,OAAOzE,KAAAA;QACpB,SAAS0C,KAAU;AACjB,eAAK,kBAAe;AACpB,eAAK,WAAWE,MAAMF,GAAAA;QACxB;MACF,CAAA;IACJ,CAAA;EACF;AACF;AAEO,IAAMqC,gBAAgB,OAA4B9E,KAAc+E,aAAAA;AACrE,QAAMA,SAASH,OAAO5E,GAAAA;AACtBA,MAAIkB,UAAU,MAAM6D,SAASN,QAAK,CAAA;AAElC,SAAOM;AACT;",
6
+ "names": ["import_util", "ContextDisposedError", "Error", "constructor", "DEBUG_LOG_DISPOSE", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "dispose", "Context", "params", "callMeta", "undefined", "maxSafeDisposeCallbacks", "Symbol", "inspect", "toString", "getContextName", "parent", "attributes", "onError", "default", "disposed", "disposeCallbacksLength", "length", "onDispose", "callback", "log", "catch", "context", "push", "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", "toStringTag", "custom", "asyncDispose", "safeInstanceof", "name", "F", "pathSegments", "split", "L", "rejectOnDispose", "cancelWithContext", "race", "finally", "LifecycleState", "CLOSE_RESOURCE_ON_UNHANDLED_ERROR", "Resource", "Object", "getPrototypeOf", "_lifecycleState", "_ctx", "_open", "_close", "_catch", "close", "doubleErr", "throwUnhandledError", "open", "queueMicrotask", "openInContext", "resource"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/common/context/src/context-disposed-error.ts":{"bytes":853,"imports":[],"format":"esm"},"packages/common/context/src/context.ts":{"bytes":23328,"imports":[{"path":"node:util","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":3096,"imports":[{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/resource.ts":{"bytes":13662,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":809,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"},{"path":"packages/common/context/src/resource.ts","kind":"import-statement","original":"./resource"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18404},"packages/common/context/dist/lib/node/index.cjs":{"imports":[{"path":"node:util","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":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/context.ts":{"bytesInOutput":6390},"packages/common/context/src/context-disposed-error.ts":{"bytesInOutput":106},"packages/common/context/src/index.ts":{"bytesInOutput":0},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":410},"packages/common/context/src/resource.ts":{"bytesInOutput":3372}},"bytes":10682}}}
1
+ {"inputs":{"packages/common/context/src/context-disposed-error.ts":{"bytes":853,"imports":[],"format":"esm"},"packages/common/context/src/context.ts":{"bytes":25119,"imports":[{"path":"node:util","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":3096,"imports":[{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"}],"format":"esm"},"packages/common/context/src/resource.ts":{"bytes":13662,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"}],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":809,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"packages/common/context/src/context-disposed-error.ts","kind":"import-statement","original":"./context-disposed-error"},{"path":"packages/common/context/src/resource.ts","kind":"import-statement","original":"./resource"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":19359},"packages/common/context/dist/lib/node/index.cjs":{"imports":[{"path":"node:util","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":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/context.ts":{"bytesInOutput":6804},"packages/common/context/src/context-disposed-error.ts":{"bytesInOutput":106},"packages/common/context/src/index.ts":{"bytesInOutput":0},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":410},"packages/common/context/src/resource.ts":{"bytesInOutput":3372}},"bytes":11096}}}
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'node:util';
3
+ import { type CallMetadata } from '@dxos/log';
3
4
  export type ContextErrorHandler = (error: Error, ctx: Context) => void;
4
5
  export type DisposeCallback = () => any | Promise<any>;
5
6
  export type CreateContextParams = {
@@ -12,7 +13,7 @@ export declare class Context {
12
13
  #private;
13
14
  static default(): Context;
14
15
  maxSafeDisposeCallbacks: number;
15
- constructor({ name, parent, attributes, onError }?: CreateContextParams);
16
+ constructor(params?: CreateContextParams, callMeta?: Partial<CallMetadata>);
16
17
  get disposed(): boolean;
17
18
  get disposeCallbacksLength(): number;
18
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/context.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;AAEvE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B,CAAC;AAkBF,qBACa,OAAO;;IAClB,MAAM,CAAC,OAAO;IAcP,uBAAuB,SAA8B;gBAEhD,EAAE,IAAI,EAAE,MAAM,EAAE,UAAe,EAAE,OAA+B,EAAE,GAAE,mBAAwB;IAOxG,IAAI,QAAQ,YAEX;IAED,IAAI,sBAAsB,WAEzB;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IA4BhD;;;;;;;OAOG;IACG,OAAO,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAqDrD;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAezB,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAE,mBAAwB,GAAG,OAAO;IAsBlE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IAW9B,CAAC,MAAM,CAAC,WAAW,CAAC,SAAa;IACjC,CAAC,OAAO,CAAC,MAAM,CAAC,eAAyB;IAEzC,QAAQ;IAIF,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7C"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/context.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,YAAY,EAAO,MAAM,WAAW,CAAC;AAKnD,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;AAEvE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B,CAAC;AAoBF,qBACa,OAAO;;IAClB,MAAM,CAAC,OAAO;IAcP,uBAAuB,SAA8B;gBAEhD,MAAM,GAAE,mBAAwB,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAO9E,IAAI,QAAQ,YAEX;IAED,IAAI,sBAAsB,WAEzB;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IA6BhD;;;;;;;OAOG;IACG,OAAO,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAqDrD;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAezB,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAE,mBAAwB,GAAG,OAAO;IAsBlE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IAW9B,CAAC,MAAM,CAAC,WAAW,CAAC,SAAa;IACjC,CAAC,OAAO,CAAC,MAAM,CAAC,eAAyB;IAEzC,QAAQ;IAIF,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/context",
3
- "version": "0.5.9-next.a50ff17",
3
+ "version": "0.6.0",
4
4
  "description": "Async utils.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,9 +16,9 @@
16
16
  "src"
17
17
  ],
18
18
  "dependencies": {
19
- "@dxos/log": "0.5.9-next.a50ff17",
20
- "@dxos/node-std": "0.5.9-next.a50ff17",
21
- "@dxos/util": "0.5.9-next.a50ff17"
19
+ "@dxos/log": "0.6.0",
20
+ "@dxos/util": "0.6.0",
21
+ "@dxos/node-std": "0.6.0"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public"
package/src/context.ts CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { inspect } from 'node:util';
6
6
 
7
- import { log } from '@dxos/log';
7
+ import { type CallMetadata, log } from '@dxos/log';
8
8
  import { safeInstanceof } from '@dxos/util';
9
9
 
10
10
  import { ContextDisposedError } from './context-disposed-error';
@@ -20,6 +20,8 @@ export type CreateContextParams = {
20
20
  onError?: ContextErrorHandler;
21
21
  };
22
22
 
23
+ const DEBUG_LOG_DISPOSE = false;
24
+
23
25
  /**
24
26
  * Maximum number of dispose callbacks before we start logging warnings.
25
27
  */
@@ -54,11 +56,11 @@ export class Context {
54
56
 
55
57
  public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
56
58
 
57
- constructor({ name, parent, attributes = {}, onError = DEFAULT_ERROR_HANDLER }: CreateContextParams = {}) {
58
- this.#name = name;
59
- this.#parent = parent;
60
- this.#attributes = attributes;
61
- this.#onError = onError;
59
+ constructor(params: CreateContextParams = {}, callMeta?: Partial<CallMetadata>) {
60
+ this.#name = getContextName(params, callMeta);
61
+ this.#parent = params.parent;
62
+ this.#attributes = params.attributes ?? {};
63
+ this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;
62
64
  }
63
65
 
64
66
  get disposed() {
@@ -85,7 +87,7 @@ export class Context {
85
87
  try {
86
88
  await callback();
87
89
  } catch (error: any) {
88
- log.catch(error);
90
+ log.catch(error, { context: this.#name });
89
91
  }
90
92
  })();
91
93
  }
@@ -93,6 +95,7 @@ export class Context {
93
95
  this.#disposeCallbacks.push(callback);
94
96
  if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks) {
95
97
  log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {
98
+ context: this.#name,
96
99
  count: this.#disposeCallbacks.length,
97
100
  });
98
101
  }
@@ -134,7 +137,7 @@ export class Context {
134
137
  const callbacks = Array.from(this.#disposeCallbacks).reverse();
135
138
  this.#disposeCallbacks.length = 0;
136
139
 
137
- if (this.#name) {
140
+ if (DEBUG_LOG_DISPOSE) {
138
141
  log('disposing', { context: this.#name, count: callbacks.length });
139
142
  }
140
143
 
@@ -160,7 +163,7 @@ export class Context {
160
163
  }
161
164
 
162
165
  resolveDispose(clean);
163
- if (this.#name) {
166
+ if (DEBUG_LOG_DISPOSE) {
164
167
  log('disposed', { context: this.#name });
165
168
  }
166
169
 
@@ -231,3 +234,14 @@ export class Context {
231
234
  await this.dispose();
232
235
  }
233
236
  }
237
+
238
+ const getContextName = (params: CreateContextParams, callMeta?: Partial<CallMetadata>): string | undefined => {
239
+ if (params.name) {
240
+ return params.name;
241
+ }
242
+ if (callMeta?.F?.length) {
243
+ const pathSegments = callMeta?.F.split('/');
244
+ return `${pathSegments[pathSegments.length - 1]}#${callMeta?.L ?? 0}`;
245
+ }
246
+ return undefined;
247
+ };