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

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.
@@ -269,12 +269,12 @@ var cancelWithContext = (ctx, promise) => {
269
269
 
270
270
  // src/resource.ts
271
271
  import { throwUnhandledError } from "@dxos/util";
272
- var LifecycleState = /* @__PURE__ */ function(LifecycleState2) {
272
+ var LifecycleState = /* @__PURE__ */ (function(LifecycleState2) {
273
273
  LifecycleState2["CLOSED"] = "CLOSED";
274
274
  LifecycleState2["OPEN"] = "OPEN";
275
275
  LifecycleState2["ERROR"] = "ERROR";
276
276
  return LifecycleState2;
277
- }({});
277
+ })({});
278
278
  var CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;
279
279
  var Resource = class {
280
280
  #lifecycleState = "CLOSED";
@@ -291,6 +291,16 @@ var Resource = class {
291
291
  * Provided in the open method.
292
292
  */
293
293
  #parentCtx = this.#createParentContext();
294
+ /**
295
+ * ```ts
296
+ * await using resource = new Resource();
297
+ * await resource.open();
298
+ * ```
299
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
300
+ */
301
+ async [Symbol.asyncDispose]() {
302
+ await this.close();
303
+ }
294
304
  get #name() {
295
305
  return Object.getPrototypeOf(this).constructor.name;
296
306
  }
@@ -306,12 +316,12 @@ var Resource = class {
306
316
  /**
307
317
  * To be overridden by subclasses.
308
318
  */
309
- async _open(ctx) {
319
+ async _open(_ctx) {
310
320
  }
311
321
  /**
312
322
  * To be overridden by subclasses.
313
323
  */
314
- async _close(ctx) {
324
+ async _close(_ctx) {
315
325
  }
316
326
  /**
317
327
  * Error handler for errors that are caught by the context.
@@ -328,6 +338,19 @@ var Resource = class {
328
338
  throw err;
329
339
  }
330
340
  /**
341
+ * Calls the provided function, opening and closing the resource.
342
+ * NOTE: Consider using `using` instead.
343
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
344
+ */
345
+ async use(fn) {
346
+ try {
347
+ await this.open();
348
+ return await fn(this);
349
+ } finally {
350
+ await this.close();
351
+ }
352
+ }
353
+ /**
331
354
  * Opens the resource.
332
355
  * If the resource is already open, it does nothing.
333
356
  * If the resource is in an error state, it throws an error.
@@ -373,9 +396,6 @@ var Resource = class {
373
396
  }
374
397
  await this.#openPromise;
375
398
  }
376
- async [Symbol.asyncDispose]() {
377
- await this.close();
378
- }
379
399
  async #open(ctx) {
380
400
  this.#closePromise = null;
381
401
  this.#parentCtx = ctx?.derive({
@@ -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 { 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"]
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 /**\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 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", "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", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose"]
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":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":17062,"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":22547},"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":4469}},"bytes":12594}}}
@@ -271,12 +271,12 @@ var cancelWithContext = (ctx, promise) => {
271
271
 
272
272
  // src/resource.ts
273
273
  import { throwUnhandledError } from "@dxos/util";
274
- var LifecycleState = /* @__PURE__ */ function(LifecycleState2) {
274
+ var LifecycleState = /* @__PURE__ */ (function(LifecycleState2) {
275
275
  LifecycleState2["CLOSED"] = "CLOSED";
276
276
  LifecycleState2["OPEN"] = "OPEN";
277
277
  LifecycleState2["ERROR"] = "ERROR";
278
278
  return LifecycleState2;
279
- }({});
279
+ })({});
280
280
  var CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;
281
281
  var Resource = class {
282
282
  #lifecycleState = "CLOSED";
@@ -293,6 +293,16 @@ var Resource = class {
293
293
  * Provided in the open method.
294
294
  */
295
295
  #parentCtx = this.#createParentContext();
296
+ /**
297
+ * ```ts
298
+ * await using resource = new Resource();
299
+ * await resource.open();
300
+ * ```
301
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
302
+ */
303
+ async [Symbol.asyncDispose]() {
304
+ await this.close();
305
+ }
296
306
  get #name() {
297
307
  return Object.getPrototypeOf(this).constructor.name;
298
308
  }
@@ -308,12 +318,12 @@ var Resource = class {
308
318
  /**
309
319
  * To be overridden by subclasses.
310
320
  */
311
- async _open(ctx) {
321
+ async _open(_ctx) {
312
322
  }
313
323
  /**
314
324
  * To be overridden by subclasses.
315
325
  */
316
- async _close(ctx) {
326
+ async _close(_ctx) {
317
327
  }
318
328
  /**
319
329
  * Error handler for errors that are caught by the context.
@@ -330,6 +340,19 @@ var Resource = class {
330
340
  throw err;
331
341
  }
332
342
  /**
343
+ * Calls the provided function, opening and closing the resource.
344
+ * NOTE: Consider using `using` instead.
345
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
346
+ */
347
+ async use(fn) {
348
+ try {
349
+ await this.open();
350
+ return await fn(this);
351
+ } finally {
352
+ await this.close();
353
+ }
354
+ }
355
+ /**
333
356
  * Opens the resource.
334
357
  * If the resource is already open, it does nothing.
335
358
  * If the resource is in an error state, it throws an error.
@@ -375,9 +398,6 @@ var Resource = class {
375
398
  }
376
399
  await this.#openPromise;
377
400
  }
378
- async [Symbol.asyncDispose]() {
379
- await this.close();
380
- }
381
401
  async #open(ctx) {
382
402
  this.#closePromise = null;
383
403
  this.#parentCtx = ctx?.derive({
@@ -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 { 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"]
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 /**\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 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", "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", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"src/context-disposed-error.ts":{"bytes":784,"imports":[],"format":"esm"},"src/context.ts":{"bytes":27720,"imports":[{"path":"node: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/node-esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":21797},"dist/lib/node-esm/index.mjs":{"imports":[{"path":"node: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":7315},"src/context-disposed-error.ts":{"bytesInOutput":106},"src/index.ts":{"bytesInOutput":0},"src/promise-utils.ts":{"bytesInOutput":410},"src/resource.ts":{"bytesInOutput":3938}},"bytes":12146}}}
1
+ {"inputs":{"src/context-disposed-error.ts":{"bytes":784,"imports":[],"format":"esm"},"src/context.ts":{"bytes":27720,"imports":[{"path":"node: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":17062,"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/node-esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":22549},"dist/lib/node-esm/index.mjs":{"imports":[{"path":"node: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":7315},"src/context-disposed-error.ts":{"bytesInOutput":106},"src/index.ts":{"bytesInOutput":0},"src/promise-utils.ts":{"bytesInOutput":410},"src/resource.ts":{"bytesInOutput":4469}},"bytes":12677}}}
@@ -13,22 +13,36 @@ export interface Lifecycle {
13
13
  */
14
14
  export declare abstract class Resource implements Lifecycle {
15
15
  #private;
16
+ /**
17
+ * ```ts
18
+ * await using resource = new Resource();
19
+ * await resource.open();
20
+ * ```
21
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
22
+ */
23
+ [Symbol.asyncDispose](): Promise<void>;
16
24
  get isOpen(): boolean;
17
25
  protected get _lifecycleState(): LifecycleState;
18
26
  protected get _ctx(): Context;
19
27
  /**
20
28
  * To be overridden by subclasses.
21
29
  */
22
- protected _open(ctx: Context): Promise<void>;
30
+ protected _open(_ctx: Context): Promise<void>;
23
31
  /**
24
32
  * To be overridden by subclasses.
25
33
  */
26
- protected _close(ctx: Context): Promise<void>;
34
+ protected _close(_ctx: Context): Promise<void>;
27
35
  /**
28
36
  * Error handler for errors that are caught by the context.
29
37
  * By default, errors are bubbled up to the parent context which is passed to the open method.
30
38
  */
31
39
  protected _catch(err: Error): Promise<void>;
40
+ /**
41
+ * Calls the provided function, opening and closing the resource.
42
+ * NOTE: Consider using `using` instead.
43
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
44
+ */
45
+ use<T>(fn: (resource: this) => Promise<T>): Promise<T>;
32
46
  /**
33
47
  * Opens the resource.
34
48
  * If the resource is already open, it does nothing.
@@ -46,7 +60,6 @@ export declare abstract class Resource implements Lifecycle {
46
60
  * Waits until the resource is open.
47
61
  */
48
62
  waitUntilOpen(): Promise<void>;
49
- [Symbol.asyncDispose](): Promise<void>;
50
63
  }
51
64
  export declare const openInContext: <T extends Lifecycle>(ctx: Context, resource: T) => Promise<T>;
52
65
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/resource.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,oBAAY,cAAc;IACxB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACzC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC9B;AAKD;;GAEG;AACH,8BAAsB,QAAS,YAAW,SAAS;;IAsBjD,IAAI,MAAM,YAET;IAED,SAAS,KAAK,eAAe,mBAE5B;IAED,SAAS,KAAK,IAAI,YAEjB;IAED;;OAEG;cACa,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAElD;;OAEG;cACa,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnD;;;OAGG;cACa,MAAM,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjD;;;;;;OAMG;IACG,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAexC;;;OAGG;IACG,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAc9B,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAqC7C;AAED,eAAO,MAAM,aAAa,GAAU,CAAC,SAAS,SAAS,EAAE,KAAK,OAAO,EAAE,UAAU,CAAC,KAAG,OAAO,CAAC,CAAC,CAI7F,CAAC"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/resource.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,oBAAY,cAAc;IACxB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACzC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC9B;AAKD;;GAEG;AACH,8BAAsB,QAAS,YAAW,SAAS;;IAkBjD;;;;;;OAMG;IACG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5C,IAAI,MAAM,YAET;IAED,SAAS,KAAK,eAAe,mBAE5B;IAED,SAAS,KAAK,IAAI,YAEjB;IAED;;OAEG;cACa,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnD;;OAEG;cACa,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpD;;;OAGG;cACa,MAAM,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjD;;;;OAIG;IACG,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAS5D;;;;;;OAMG;IACG,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxC;;;OAGG;IACG,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASzC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CA+CrC;AAED,eAAO,MAAM,aAAa,GAAU,CAAC,SAAS,SAAS,EAAE,KAAK,OAAO,EAAE,UAAU,CAAC,KAAG,OAAO,CAAC,CAAC,CAI7F,CAAC"}