@dxos/context 0.8.4-main.b97322e → 0.8.4-main.bd9b33e6c8

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.
@@ -30,32 +30,25 @@ var DEFAULT_ERROR_HANDLER = (error, ctx) => {
30
30
  };
31
31
  var CONTEXT_FLAG_IS_DISPOSED = 1 << 0;
32
32
  var CONTEXT_FLAG_LEAK_DETECTED = 1 << 1;
33
- var _a, _b;
34
33
  var Context = class _Context {
34
+ static default() {
35
+ return new _Context();
36
+ }
37
+ #disposeCallbacks = [];
38
+ #name = void 0;
39
+ #parent = void 0;
40
+ #attributes;
41
+ #onError;
42
+ #flags = 0;
43
+ #disposePromise = void 0;
44
+ #signal = void 0;
45
+ maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
35
46
  constructor(params = {}, callMeta) {
36
- this.#disposeCallbacks = [];
37
- this.#name = void 0;
38
- this.#parent = void 0;
39
- this.#flags = 0;
40
- this.#disposePromise = void 0;
41
- this.maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
42
- this[_b] = "Context";
43
- this[_a] = () => this.toString();
44
47
  this.#name = getContextName(params, callMeta);
45
48
  this.#parent = params.parent;
46
49
  this.#attributes = params.attributes ?? {};
47
50
  this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;
48
51
  }
49
- static default() {
50
- return new _Context();
51
- }
52
- #disposeCallbacks;
53
- #name;
54
- #parent;
55
- #attributes;
56
- #onError;
57
- #flags;
58
- #disposePromise;
59
52
  get #isDisposed() {
60
53
  return !!(this.#flags & CONTEXT_FLAG_IS_DISPOSED);
61
54
  }
@@ -74,6 +67,15 @@ var Context = class _Context {
74
67
  get disposeCallbacksLength() {
75
68
  return this.#disposeCallbacks.length;
76
69
  }
70
+ get signal() {
71
+ if (this.#signal) {
72
+ return this.#signal;
73
+ }
74
+ const controller = new AbortController();
75
+ this.#signal = controller.signal;
76
+ this.onDispose(() => controller.abort());
77
+ return this.#signal;
78
+ }
77
79
  /**
78
80
  * Schedules a callback to run when the context is disposed.
79
81
  * May be async, in this case the disposer might choose to wait for all resource to released.
@@ -93,7 +95,7 @@ var Context = class _Context {
93
95
  context: this.#name
94
96
  }, {
95
97
  F: __dxlog_file,
96
- L: 119,
98
+ L: 131,
97
99
  S: this,
98
100
  C: (f, a) => f(...a)
99
101
  });
@@ -110,7 +112,7 @@ var Context = class _Context {
110
112
  count: this.#disposeCallbacks.length
111
113
  }, {
112
114
  F: __dxlog_file,
113
- L: 128,
115
+ L: 140,
114
116
  S: this,
115
117
  C: (f, a) => f(...a)
116
118
  });
@@ -148,7 +150,7 @@ var Context = class _Context {
148
150
  count: callbacks.length
149
151
  }, {
150
152
  F: __dxlog_file,
151
- L: 173,
153
+ L: 185,
152
154
  S: this,
153
155
  C: (f, a) => f(...a)
154
156
  });
@@ -171,7 +173,7 @@ var Context = class _Context {
171
173
  count: callbacks.length
172
174
  }, {
173
175
  F: __dxlog_file,
174
- L: 188,
176
+ L: 200,
175
177
  S: this,
176
178
  C: (f, a) => f(...a)
177
179
  });
@@ -187,7 +189,7 @@ var Context = class _Context {
187
189
  context: this.#name
188
190
  }, {
189
191
  F: __dxlog_file,
190
- L: 199,
192
+ L: 211,
191
193
  S: this,
192
194
  C: (f, a) => f(...a)
193
195
  });
@@ -211,6 +213,7 @@ var Context = class _Context {
211
213
  }
212
214
  derive({ onError, attributes } = {}) {
213
215
  const newCtx = new _Context({
216
+ parent: this,
214
217
  // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.
215
218
  onError: async (error) => {
216
219
  if (!onError) {
@@ -238,10 +241,12 @@ var Context = class _Context {
238
241
  }
239
242
  return void 0;
240
243
  }
244
+ [Symbol.toStringTag] = "Context";
245
+ [inspect.custom] = () => this.toString();
241
246
  toString() {
242
247
  return `Context(${this.#isDisposed ? "disposed" : "active"})`;
243
248
  }
244
- async [(_b = Symbol.toStringTag, _a = inspect.custom, Symbol.asyncDispose)]() {
249
+ async [Symbol.asyncDispose]() {
245
250
  await this.dispose();
246
251
  }
247
252
  };
@@ -275,12 +280,13 @@ var cancelWithContext = (ctx, promise) => {
275
280
 
276
281
  // src/resource.ts
277
282
  import { throwUnhandledError } from "@dxos/util";
278
- var LifecycleState = /* @__PURE__ */ function(LifecycleState2) {
283
+ import "@hazae41/symbol-dispose-polyfill";
284
+ var LifecycleState = /* @__PURE__ */ (function(LifecycleState2) {
279
285
  LifecycleState2["CLOSED"] = "CLOSED";
280
286
  LifecycleState2["OPEN"] = "OPEN";
281
287
  LifecycleState2["ERROR"] = "ERROR";
282
288
  return LifecycleState2;
283
- }({});
289
+ })({});
284
290
  var CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;
285
291
  var Resource = class {
286
292
  #lifecycleState = "CLOSED";
@@ -297,6 +303,16 @@ var Resource = class {
297
303
  * Provided in the open method.
298
304
  */
299
305
  #parentCtx = this.#createParentContext();
306
+ /**
307
+ * ```ts
308
+ * await using resource = new Resource();
309
+ * await resource.open();
310
+ * ```
311
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
312
+ */
313
+ async [Symbol.asyncDispose]() {
314
+ await this.close();
315
+ }
300
316
  get #name() {
301
317
  return Object.getPrototypeOf(this).constructor.name;
302
318
  }
@@ -312,12 +328,12 @@ var Resource = class {
312
328
  /**
313
329
  * To be overridden by subclasses.
314
330
  */
315
- async _open(ctx) {
331
+ async _open(_ctx) {
316
332
  }
317
333
  /**
318
334
  * To be overridden by subclasses.
319
335
  */
320
- async _close(ctx) {
336
+ async _close(_ctx) {
321
337
  }
322
338
  /**
323
339
  * Error handler for errors that are caught by the context.
@@ -334,6 +350,19 @@ var Resource = class {
334
350
  throw err;
335
351
  }
336
352
  /**
353
+ * Calls the provided function, opening and closing the resource.
354
+ * NOTE: Consider using `using` instead.
355
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
356
+ */
357
+ async use(fn) {
358
+ try {
359
+ await this.open();
360
+ return await fn(this);
361
+ } finally {
362
+ await this.close();
363
+ }
364
+ }
365
+ /**
337
366
  * Opens the resource.
338
367
  * If the resource is already open, it does nothing.
339
368
  * If the resource is in an error state, it throws an error.
@@ -379,14 +408,12 @@ var Resource = class {
379
408
  }
380
409
  await this.#openPromise;
381
410
  }
382
- async [Symbol.asyncDispose]() {
383
- await this.close();
384
- }
385
411
  async #open(ctx) {
386
412
  this.#closePromise = null;
387
413
  this.#parentCtx = ctx?.derive({
388
414
  name: this.#name
389
415
  }) ?? this.#createParentContext();
416
+ this.#internalCtx = this.#createContext(this.#parentCtx);
390
417
  await this._open(this.#parentCtx);
391
418
  this.#lifecycleState = "OPEN";
392
419
  }
@@ -397,9 +424,10 @@ var Resource = class {
397
424
  this.#internalCtx = this.#createContext();
398
425
  this.#lifecycleState = "CLOSED";
399
426
  }
400
- #createContext() {
427
+ #createContext(attributeParent) {
401
428
  return new Context({
402
429
  name: this.#name,
430
+ parent: attributeParent,
403
431
  onError: (error) => queueMicrotask(async () => {
404
432
  try {
405
433
  await this._catch(error);
@@ -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", "params", "callMeta", "undefined", "maxSafeDisposeCallbacks", "Symbol", "inspect", "toString", "getContextName", "parent", "attributes", "onError", "default", "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", "toStringTag", "custom", "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 CreateContextProps = {\n name?: string;\n parent?: Context;\n attributes?: Record<string, any>;\n onError?: ContextErrorHandler;\n};\n\nconst DEBUG_LOG_DISPOSE = false;\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\nconst DEFAULT_ERROR_HANDLER: ContextErrorHandler = (error, ctx) => {\n if (error instanceof ContextDisposedError) {\n return;\n }\n\n void ctx.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n};\n\ntype ContextFlags = number;\n\nconst CONTEXT_FLAG_IS_DISPOSED: ContextFlags = 1 << 0;\n\n/**\n * Whether the dispose callback leak was detected.\n */\nconst CONTEXT_FLAG_LEAK_DETECTED: ContextFlags = 1 << 1;\n\n/**\n * NOTE: Context is not reusable after it is disposed.\n */\n@safeInstanceof('Context')\nexport class Context {\n static default(): Context {\n return new Context();\n }\n\n readonly #disposeCallbacks: DisposeCallback[] = [];\n\n readonly #name?: string = undefined;\n readonly #parent?: Context = undefined;\n readonly #attributes: Record<string, any>;\n readonly #onError: ContextErrorHandler;\n\n #flags: ContextFlags = 0;\n #disposePromise?: Promise<boolean> = undefined;\n\n #signal: AbortSignal | undefined = undefined;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor(params: CreateContextProps = {}, callMeta?: Partial<CallMetadata>) {\n this.#name = getContextName(params, callMeta);\n this.#parent = params.parent;\n this.#attributes = params.attributes ?? {};\n this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;\n }\n\n get #isDisposed() {\n return !!(this.#flags & CONTEXT_FLAG_IS_DISPOSED);\n }\n\n set #isDisposed(value: boolean) {\n this.#flags = value ? this.#flags | CONTEXT_FLAG_IS_DISPOSED : this.#flags & ~CONTEXT_FLAG_IS_DISPOSED;\n }\n\n get #leakDetected() {\n return !!(this.#flags & CONTEXT_FLAG_LEAK_DETECTED);\n }\n\n set #leakDetected(value: boolean) {\n this.#flags = value ? this.#flags | CONTEXT_FLAG_LEAK_DETECTED : this.#flags & ~CONTEXT_FLAG_LEAK_DETECTED;\n }\n\n get disposed() {\n return this.#isDisposed;\n }\n\n get disposeCallbacksLength() {\n return this.#disposeCallbacks.length;\n }\n\n get signal(): AbortSignal {\n if (this.#signal) {\n return this.#signal;\n }\n const controller = new AbortController();\n this.#signal = controller.signal;\n this.onDispose(() => controller.abort());\n return this.#signal;\n }\n\n /**\n * Schedules a callback to run when the context is disposed.\n * May be async, in this case the disposer might choose to wait for all resource to released.\n * Throwing an error inside the callback will result in the error being logged, but not re-thrown.\n *\n * NOTE: Will call the callback immediately if the context is already disposed.\n *\n * @returns A function that can be used to remove the callback from the dispose list.\n */\n onDispose(callback: DisposeCallback): () => void {\n if (this.#isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error, { context: this.#name });\n }\n })();\n }\n\n this.#disposeCallbacks.push(callback);\n if (this.#disposeCallbacks.length > this.maxSafeDisposeCallbacks && !this.#leakDetected) {\n this.#leakDetected = true;\n const callSite = new StackTrace().getStackArray(1)[0].trim();\n log.warn('Context has a large number of dispose callbacks (this might be a memory leak).', {\n context: this.#name,\n callSite,\n count: this.#disposeCallbacks.length,\n });\n }\n\n // Remove handler.\n return () => {\n const index = this.#disposeCallbacks.indexOf(callback);\n if (index !== -1) {\n this.#disposeCallbacks.splice(index, 1);\n }\n };\n }\n\n /**\n * Runs all dispose callbacks.\n * Callbacks are run in the reverse order they were added.\n * This function never throws.\n * It is safe to ignore the returned promise if the caller does not wish to wait for callbacks to complete.\n * Disposing context means that onDispose will throw an error and any errors raised will be logged and not propagated.\n * @returns true if there were no errors during the dispose process.\n */\n async dispose(throwOnError = false): Promise<boolean> {\n if (this.#disposePromise) {\n return this.#disposePromise;\n }\n\n // TODO(burdon): Probably should not be set until the dispose is complete, but causes tests to fail if moved.\n this.#isDisposed = true;\n\n // Set the promise before running the callbacks.\n let resolveDispose!: (value: boolean) => void;\n const promise = new Promise<boolean>((resolve) => {\n resolveDispose = resolve;\n });\n this.#disposePromise = promise;\n\n // Process last first.\n // Clone the array so that any mutations to the original array don't affect the dispose process.\n const callbacks = Array.from(this.#disposeCallbacks).reverse();\n this.#disposeCallbacks.length = 0;\n\n if (DEBUG_LOG_DISPOSE) {\n log('disposing', { context: this.#name, count: callbacks.length });\n }\n\n let i = 0;\n let clean = true;\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n await callback();\n i++;\n } catch (err: any) {\n clean = false;\n if (throwOnError) {\n errors.push(err);\n } else {\n log.catch(err, { context: this.#name, callback: i, count: callbacks.length });\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors);\n }\n\n resolveDispose(clean);\n if (DEBUG_LOG_DISPOSE) {\n log('disposed', { context: this.#name });\n }\n\n return clean;\n }\n\n /**\n * Raise the error inside the context.\n * The error will be propagated to the error handler.\n * IF the error handler is not set, the error will dispose the context and cause an unhandled rejection.\n */\n raise(error: Error): void {\n if (this.#isDisposed) {\n // TODO(dmaretskyi): Don't log those.\n // log.warn('Error in disposed context', error);\n return;\n }\n\n try {\n this.#onError(error, this);\n } catch (err) {\n // Generate an unhandled rejection and stop the error propagation.\n void Promise.reject(err);\n }\n }\n\n derive({ onError, attributes }: CreateContextProps = {}): Context {\n const newCtx = new Context({\n parent: this,\n // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.\n onError: async (error) => {\n if (!onError) {\n this.raise(error);\n } else {\n try {\n await onError(error, this);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\n });\n\n const clearDispose = this.onDispose(() => newCtx.dispose());\n newCtx.onDispose(clearDispose);\n return newCtx;\n }\n\n getAttribute(key: string): any {\n if (key in this.#attributes) {\n return this.#attributes[key];\n }\n if (this.#parent) {\n return this.#parent.getAttribute(key);\n }\n\n return undefined;\n }\n\n [Symbol.toStringTag] = 'Context';\n [inspect.custom] = () => this.toString();\n\n toString(): string {\n return `Context(${this.#isDisposed ? 'disposed' : 'active'})`;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\nconst getContextName = (params: CreateContextProps, callMeta?: Partial<CallMetadata>): string | undefined => {\n if (params.name) {\n return params.name;\n }\n if (callMeta?.F?.length) {\n const pathSegments = callMeta?.F.split('/');\n return `${pathSegments[pathSegments.length - 1]}#${callMeta?.L ?? 0}`;\n }\n return undefined;\n};\n", "//\n// Copyright 2023 DXOS.org\n//\n\nexport class ContextDisposedError extends Error {\n constructor() {\n super('Context disposed.');\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context } from './context';\nimport { ContextDisposedError } from './context-disposed-error';\n\n/**\n * @returns A promise that rejects when the context is disposed.\n */\n// TODO(dmaretskyi): Memory leak.\nexport const rejectOnDispose = (ctx: Context, error = new ContextDisposedError()): Promise<never> =>\n new Promise((resolve, reject) => {\n ctx.onDispose(() => reject(error));\n });\n\n/**\n * Rejects the promise if the context is disposed.\n */\nexport const cancelWithContext = <T>(ctx: Context, promise: Promise<T>): Promise<T> => {\n let clearDispose: () => void;\n return Promise.race([\n promise,\n new Promise<never>((resolve, reject) => {\n // Will be called before .finally() handlers.\n clearDispose = ctx.onDispose(() => reject(new ContextDisposedError()));\n }),\n ]).finally(() => clearDispose?.());\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { throwUnhandledError } from '@dxos/util';\n\nimport { Context } from './context';\n\nimport '@hazae41/symbol-dispose-polyfill';\n\nexport enum LifecycleState {\n CLOSED = 'CLOSED',\n OPEN = 'OPEN',\n ERROR = 'ERROR',\n}\n\nexport interface Lifecycle {\n open?(ctx?: Context): Promise<any> | any;\n close?(): Promise<any> | any;\n}\n\n// Feature flag to be enabled later.\nconst CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;\n\n/**\n * Base class for resources that need to be opened and closed.\n */\nexport abstract class Resource implements Lifecycle {\n #lifecycleState = LifecycleState.CLOSED;\n\n #openPromise: Promise<void> | null = null;\n #closePromise: Promise<void> | null = null;\n\n /**\n * Managed internally by the resource.\n * Recreated on close.\n * Errors are propagated to the `_catch` method and the parent context.\n */\n #internalCtx: Context = this.#createContext();\n\n /**\n * Context that is used to bubble up errors that are not handled by the resource.\n * Provided in the open method.\n */\n #parentCtx: Context = this.#createParentContext();\n\n /**\n * ```ts\n * await using resource = new Resource();\n * await resource.open();\n * ```\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n get #name() {\n return Object.getPrototypeOf(this).constructor.name;\n }\n\n get isOpen() {\n return this.#lifecycleState === LifecycleState.OPEN && this.#closePromise == null;\n }\n\n protected get _lifecycleState() {\n return this.#lifecycleState;\n }\n\n protected get _ctx() {\n return this.#internalCtx;\n }\n\n /**\n * To be overridden by subclasses.\n */\n protected async _open(_ctx: Context): Promise<void> {}\n\n /**\n * To be overridden by subclasses.\n */\n protected async _close(_ctx: Context): Promise<void> {}\n\n /**\n * Error handler for errors that are caught by the context.\n * By default, errors are bubbled up to the parent context which is passed to the open method.\n */\n protected async _catch(err: Error): Promise<void> {\n if (CLOSE_RESOURCE_ON_UNHANDLED_ERROR) {\n try {\n await this.close();\n } catch (doubleErr: any) {\n throwUnhandledError(doubleErr);\n }\n }\n throw err;\n }\n\n /**\n * Calls the provided function, opening and closing the resource.\n * NOTE: Consider using `using` instead.\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using\n */\n async use<T>(fn: (resource: this) => Promise<T>): Promise<T> {\n try {\n await this.open();\n return await fn(this);\n } finally {\n await this.close();\n }\n }\n\n /**\n * Opens the resource.\n * If the resource is already open, it does nothing.\n * If the resource is in an error state, it throws an error.\n * If the resource is closed, it waits for it to close and then opens it.\n * @param ctx - Context to use for opening the resource. This context will receive errors that are not handled in `_catch`.\n */\n async open(ctx?: Context): Promise<this> {\n switch (this.#lifecycleState) {\n case LifecycleState.OPEN:\n return this;\n case LifecycleState.ERROR:\n throw new Error(`Invalid state: ${this.#lifecycleState}`);\n default:\n }\n\n await this.#closePromise;\n await (this.#openPromise ??= this.#open(ctx));\n return this;\n }\n\n /**\n * Closes the resource.\n * If the resource is already closed, it does nothing.\n */\n async close(ctx?: Context): Promise<this> {\n if (this.#lifecycleState === LifecycleState.CLOSED) {\n return this;\n }\n await this.#openPromise;\n await (this.#closePromise ??= this.#close(ctx));\n return this;\n }\n\n /**\n * Waits until the resource is open.\n */\n async waitUntilOpen(): Promise<void> {\n switch (this.#lifecycleState) {\n case LifecycleState.OPEN:\n return;\n case LifecycleState.ERROR:\n throw new Error(`Invalid state: ${this.#lifecycleState}`);\n }\n\n if (!this.#openPromise) {\n throw new Error('Resource is not being opened');\n }\n await this.#openPromise;\n }\n\n async #open(ctx?: Context): Promise<void> {\n this.#closePromise = null;\n this.#parentCtx = ctx?.derive({ name: this.#name }) ?? this.#createParentContext();\n this.#internalCtx = this.#createContext(this.#parentCtx);\n await this._open(this.#parentCtx);\n this.#lifecycleState = LifecycleState.OPEN;\n }\n\n async #close(ctx = Context.default()): Promise<void> {\n this.#openPromise = null;\n await this.#internalCtx.dispose();\n await this._close(ctx);\n this.#internalCtx = this.#createContext();\n this.#lifecycleState = LifecycleState.CLOSED;\n }\n\n #createContext(attributeParent?: Context): Context {\n return new Context({\n name: this.#name,\n parent: attributeParent,\n onError: (error) =>\n queueMicrotask(async () => {\n try {\n await this._catch(error);\n } catch (err: any) {\n this.#lifecycleState = LifecycleState.ERROR;\n this.#parentCtx.raise(err);\n }\n }),\n });\n }\n\n #createParentContext(): Context {\n return new Context({ name: this.#name });\n }\n}\n\nexport const openInContext = async <T extends Lifecycle>(ctx: Context, resource: T): Promise<T> => {\n await resource.open?.(ctx);\n ctx.onDispose(() => resource.close?.());\n return resource;\n};\n"],
5
+ "mappings": ";AAIA,SAASA,eAAe;AAExB,SAASC,kBAAkB;AAC3B,SAA4BC,WAAW;AACvC,SAASC,sBAAsB;;;ACJxB,IAAMC,uBAAN,cAAmCC,MAAAA;EACxC,cAAc;AACZ,UAAM,mBAAA;EACR;AACF;;;;;;;;;;ADeA,IAAMC,oBAAoB;AAK1B,IAAMC,6BAA6B;AAEnC,IAAMC,wBAA6C,CAACC,OAAOC,QAAAA;AACzD,MAAID,iBAAiBE,sBAAsB;AACzC;EACF;AAEA,OAAKD,IAAIE,QAAO;AAGhB,QAAMH;AACR;AAIA,IAAMI,2BAAyC,KAAK;AAKpD,IAAMC,6BAA2C,KAAK;AAM/C,IAAMC,UAAN,MAAMA,SAAAA;EACX,OAAOC,UAAmB;AACxB,WAAO,IAAID,SAAAA;EACb;EAES,oBAAuC,CAAA;EAEvC,QAAiBE;EACjB,UAAoBA;EACpB;EACA;EAET,SAAuB;EACvB,kBAAqCA;EAErC,UAAmCA;EAE5BC,0BAA0BX;EAEjC,YAAYY,SAA6B,CAAC,GAAGC,UAAkC;AAC7E,SAAK,QAAQC,eAAeF,QAAQC,QAAAA;AACpC,SAAK,UAAUD,OAAOG;AACtB,SAAK,cAAcH,OAAOI,cAAc,CAAC;AACzC,SAAK,WAAWJ,OAAOK,WAAWhB;EACpC;EAEA,IAAI,cAAW;AACb,WAAO,CAAC,EAAE,KAAK,SAASK;EAC1B;EAEA,IAAI,YAAYY,OAAc;AAC5B,SAAK,SAASA,QAAQ,KAAK,SAASZ,2BAA2B,KAAK,SAAS,CAACA;EAChF;EAEA,IAAI,gBAAa;AACf,WAAO,CAAC,EAAE,KAAK,SAASC;EAC1B;EAEA,IAAI,cAAcW,OAAc;AAC9B,SAAK,SAASA,QAAQ,KAAK,SAASX,6BAA6B,KAAK,SAAS,CAACA;EAClF;EAEA,IAAIY,WAAW;AACb,WAAO,KAAK;EACd;EAEA,IAAIC,yBAAyB;AAC3B,WAAO,KAAK,kBAAkBC;EAChC;EAEA,IAAIC,SAAsB;AACxB,QAAI,KAAK,SAAS;AAChB,aAAO,KAAK;IACd;AACA,UAAMC,aAAa,IAAIC,gBAAAA;AACvB,SAAK,UAAUD,WAAWD;AAC1B,SAAKG,UAAU,MAAMF,WAAWG,MAAK,CAAA;AACrC,WAAO,KAAK;EACd;;;;;;;;;;EAWAD,UAAUE,UAAuC;AAC/C,QAAI,KAAK,aAAa;AAEpB,YAAM,YAAA;AACJ,YAAI;AACF,gBAAMA,SAAAA;QACR,SAASzB,OAAY;AACnB0B,cAAIC,MAAM3B,OAAO;YAAE4B,SAAS,KAAK;UAAM,GAAA;;;;;;QACzC;MACF,GAAA;IACF;AAEA,SAAK,kBAAkBC,KAAKJ,QAAAA;AAC5B,QAAI,KAAK,kBAAkBN,SAAS,KAAKV,2BAA2B,CAAC,KAAK,eAAe;AACvF,WAAK,gBAAgB;AACrB,YAAMqB,WAAW,IAAIC,WAAAA,EAAaC,cAAc,CAAA,EAAG,CAAA,EAAGC,KAAI;AAC1DP,UAAIQ,KAAK,kFAAkF;QACzFN,SAAS,KAAK;QACdE;QACAK,OAAO,KAAK,kBAAkBhB;MAChC,GAAA;;;;;;IACF;AAGA,WAAO,MAAA;AACL,YAAMiB,QAAQ,KAAK,kBAAkBC,QAAQZ,QAAAA;AAC7C,UAAIW,UAAU,IAAI;AAChB,aAAK,kBAAkBE,OAAOF,OAAO,CAAA;MACvC;IACF;EACF;;;;;;;;;EAUA,MAAMjC,QAAQoC,eAAe,OAAyB;AACpD,QAAI,KAAK,iBAAiB;AACxB,aAAO,KAAK;IACd;AAGA,SAAK,cAAc;AAGnB,QAAIC;AACJ,UAAMC,UAAU,IAAIC,QAAiB,CAACC,YAAAA;AACpCH,uBAAiBG;IACnB,CAAA;AACA,SAAK,kBAAkBF;AAIvB,UAAMG,YAAYC,MAAMC,KAAK,KAAK,iBAAiB,EAAEC,QAAO;AAC5D,SAAK,kBAAkB5B,SAAS;AAEhC,QAAItB,mBAAmB;AACrB6B,UAAI,aAAa;QAAEE,SAAS,KAAK;QAAOO,OAAOS,UAAUzB;MAAO,GAAA;;;;;;IAClE;AAEA,QAAI6B,IAAI;AACR,QAAIC,QAAQ;AACZ,UAAMC,SAAkB,CAAA;AACxB,eAAWzB,YAAYmB,WAAW;AAChC,UAAI;AACF,cAAMnB,SAAAA;AACNuB;MACF,SAASG,KAAU;AACjBF,gBAAQ;AACR,YAAIV,cAAc;AAChBW,iBAAOrB,KAAKsB,GAAAA;QACd,OAAO;AACLzB,cAAIC,MAAMwB,KAAK;YAAEvB,SAAS,KAAK;YAAOH,UAAUuB;YAAGb,OAAOS,UAAUzB;UAAO,GAAA;;;;;;QAC7E;MACF;IACF;AAEA,QAAI+B,OAAO/B,SAAS,GAAG;AACrB,YAAM,IAAIiC,eAAeF,MAAAA;IAC3B;AAEAV,mBAAeS,KAAAA;AACf,QAAIpD,mBAAmB;AACrB6B,UAAI,YAAY;QAAEE,SAAS,KAAK;MAAM,GAAA;;;;;;IACxC;AAEA,WAAOqB;EACT;;;;;;EAOAI,MAAMrD,OAAoB;AACxB,QAAI,KAAK,aAAa;AAGpB;IACF;AAEA,QAAI;AACF,WAAK,SAASA,OAAO,IAAI;IAC3B,SAASmD,KAAK;AAEZ,WAAKT,QAAQY,OAAOH,GAAAA;IACtB;EACF;EAEAI,OAAO,EAAExC,SAASD,WAAU,IAAyB,CAAC,GAAY;AAChE,UAAM0C,SAAS,IAAIlD,SAAQ;MACzBO,QAAQ;;MAERE,SAAS,OAAOf,UAAAA;AACd,YAAI,CAACe,SAAS;AACZ,eAAKsC,MAAMrD,KAAAA;QACb,OAAO;AACL,cAAI;AACF,kBAAMe,QAAQf,OAAO,IAAI;UAC3B,QAAQ;AACN,iBAAKqD,MAAMrD,KAAAA;UACb;QACF;MACF;MACAc;IACF,CAAA;AAEA,UAAM2C,eAAe,KAAKlC,UAAU,MAAMiC,OAAOrD,QAAO,CAAA;AACxDqD,WAAOjC,UAAUkC,YAAAA;AACjB,WAAOD;EACT;EAEAE,aAAaC,KAAkB;AAC7B,QAAIA,OAAO,KAAK,aAAa;AAC3B,aAAO,KAAK,YAAYA,GAAAA;IAC1B;AACA,QAAI,KAAK,SAAS;AAChB,aAAO,KAAK,QAAQD,aAAaC,GAAAA;IACnC;AAEA,WAAOnD;EACT;EAEA,CAACoD,OAAOC,WAAW,IAAI;EACvB,CAACC,QAAQC,MAAM,IAAI,MAAM,KAAKC,SAAQ;EAEtCA,WAAmB;AACjB,WAAO,WAAW,KAAK,cAAc,aAAa,QAAA;EACpD;EAEA,OAAOJ,OAAOK,YAAY,IAAmB;AAC3C,UAAM,KAAK9D,QAAO;EACpB;AACF;;;;AAEA,IAAMS,iBAAiB,CAACF,QAA4BC,aAAAA;AAClD,MAAID,OAAOwD,MAAM;AACf,WAAOxD,OAAOwD;EAChB;AACA,MAAIvD,UAAUwD,GAAGhD,QAAQ;AACvB,UAAMiD,eAAezD,UAAUwD,EAAEE,MAAM,GAAA;AACvC,WAAO,GAAGD,aAAaA,aAAajD,SAAS,CAAA,CAAE,IAAIR,UAAU2D,KAAK,CAAA;EACpE;AACA,SAAO9D;AACT;;;AExRO,IAAM+D,kBAAkB,CAACC,KAAcC,QAAQ,IAAIC,qBAAAA,MACxD,IAAIC,QAAQ,CAACC,SAASC,WAAAA;AACpBL,MAAIM,UAAU,MAAMD,OAAOJ,KAAAA,CAAAA;AAC7B,CAAA;AAKK,IAAMM,oBAAoB,CAAIP,KAAcQ,YAAAA;AACjD,MAAIC;AACJ,SAAON,QAAQO,KAAK;IAClBF;IACA,IAAIL,QAAe,CAACC,SAASC,WAAAA;AAE3BI,qBAAeT,IAAIM,UAAU,MAAMD,OAAO,IAAIH,qBAAAA,CAAAA,CAAAA;IAChD,CAAA;GACD,EAAES,QAAQ,MAAMF,eAAAA,CAAAA;AACnB;;;ACxBA,SAASG,2BAA2B;AAIpC,OAAO;AAEA,IAAKC,iBAAAA,0BAAAA,iBAAAA;;;;SAAAA;;AAYZ,IAAMC,oCAAoC;AAKnC,IAAeC,WAAf,MAAeA;EACpB,kBAAe;EAEf,eAAqC;EACrC,gBAAsC;;;;;;EAOtC,eAAwB,KAAK,eAAc;;;;;EAM3C,aAAsB,KAAK,qBAAoB;;;;;;;;EAS/C,OAAOC,OAAOC,YAAY,IAAmB;AAC3C,UAAM,KAAKC,MAAK;EAClB;EAEA,IAAI,QAAK;AACP,WAAOC,OAAOC,eAAe,IAAI,EAAE,YAAYC;EACjD;EAEA,IAAIC,SAAS;AACX,WAAO,KAAK,oBAAe,UAA4B,KAAK,iBAAiB;EAC/E;EAEA,IAAcC,kBAAkB;AAC9B,WAAO,KAAK;EACd;EAEA,IAAcC,OAAO;AACnB,WAAO,KAAK;EACd;;;;EAKA,MAAgBC,MAAMD,MAA8B;EAAC;;;;EAKrD,MAAgBE,OAAOF,MAA8B;EAAC;;;;;EAMtD,MAAgBG,OAAOC,KAA2B;AAChD,QAAId,mCAAmC;AACrC,UAAI;AACF,cAAM,KAAKI,MAAK;MAClB,SAASW,WAAgB;AACvBC,4BAAoBD,SAAAA;MACtB;IACF;AACA,UAAMD;EACR;;;;;;EAOA,MAAMG,IAAOC,IAAgD;AAC3D,QAAI;AACF,YAAM,KAAKC,KAAI;AACf,aAAO,MAAMD,GAAG,IAAI;IACtB,UAAA;AACE,YAAM,KAAKd,MAAK;IAClB;EACF;;;;;;;;EASA,MAAMe,KAAKC,KAA8B;AACvC,YAAQ,KAAK,iBAAe;MAC1B,KAAA;AACE,eAAO;MACT,KAAA;AACE,cAAM,IAAIC,MAAM,kBAAkB,KAAK,eAAe,EAAE;MAC1D;IACF;AAEA,UAAM,KAAK;AACX,WAAO,KAAK,iBAAiB,KAAK,MAAMD,GAAAA;AACxC,WAAO;EACT;;;;;EAMA,MAAMhB,MAAMgB,KAA8B;AACxC,QAAI,KAAK,oBAAe,UAA4B;AAClD,aAAO;IACT;AACA,UAAM,KAAK;AACX,WAAO,KAAK,kBAAkB,KAAK,OAAOA,GAAAA;AAC1C,WAAO;EACT;;;;EAKA,MAAME,gBAA+B;AACnC,YAAQ,KAAK,iBAAe;MAC1B,KAAA;AACE;MACF,KAAA;AACE,cAAM,IAAID,MAAM,kBAAkB,KAAK,eAAe,EAAE;IAC5D;AAEA,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAIA,MAAM,8BAAA;IAClB;AACA,UAAM,KAAK;EACb;EAEA,MAAM,MAAMD,KAAa;AACvB,SAAK,gBAAgB;AACrB,SAAK,aAAaA,KAAKG,OAAO;MAAEhB,MAAM,KAAK;IAAM,CAAA,KAAM,KAAK,qBAAoB;AAChF,SAAK,eAAe,KAAK,eAAe,KAAK,UAAU;AACvD,UAAM,KAAKI,MAAM,KAAK,UAAU;AAChC,SAAK,kBAAe;EACtB;EAEA,MAAM,OAAOS,MAAMI,QAAQC,QAAO,GAAE;AAClC,SAAK,eAAe;AACpB,UAAM,KAAK,aAAaC,QAAO;AAC/B,UAAM,KAAKd,OAAOQ,GAAAA;AAClB,SAAK,eAAe,KAAK,eAAc;AACvC,SAAK,kBAAe;EACtB;EAEA,eAAeO,iBAAyB;AACtC,WAAO,IAAIH,QAAQ;MACjBjB,MAAM,KAAK;MACXqB,QAAQD;MACRE,SAAS,CAACC,UACRC,eAAe,YAAA;AACb,YAAI;AACF,gBAAM,KAAKlB,OAAOiB,KAAAA;QACpB,SAAShB,KAAU;AACjB,eAAK,kBAAe;AACpB,eAAK,WAAWkB,MAAMlB,GAAAA;QACxB;MACF,CAAA;IACJ,CAAA;EACF;EAEA,uBAAoB;AAClB,WAAO,IAAIU,QAAQ;MAAEjB,MAAM,KAAK;IAAM,CAAA;EACxC;AACF;AAEO,IAAM0B,gBAAgB,OAA4Bb,KAAcc,aAAAA;AACrE,QAAMA,SAASf,OAAOC,GAAAA;AACtBA,MAAIe,UAAU,MAAMD,SAAS9B,QAAK,CAAA;AAClC,SAAO8B;AACT;",
6
+ "names": ["inspect", "StackTrace", "log", "safeInstanceof", "ContextDisposedError", "Error", "DEBUG_LOG_DISPOSE", "MAX_SAFE_DISPOSE_CALLBACKS", "DEFAULT_ERROR_HANDLER", "error", "ctx", "ContextDisposedError", "dispose", "CONTEXT_FLAG_IS_DISPOSED", "CONTEXT_FLAG_LEAK_DETECTED", "Context", "default", "undefined", "maxSafeDisposeCallbacks", "params", "callMeta", "getContextName", "parent", "attributes", "onError", "value", "disposed", "disposeCallbacksLength", "length", "signal", "controller", "AbortController", "onDispose", "abort", "callback", "log", "catch", "context", "push", "callSite", "StackTrace", "getStackArray", "trim", "warn", "count", "index", "indexOf", "splice", "throwOnError", "resolveDispose", "promise", "Promise", "resolve", "callbacks", "Array", "from", "reverse", "i", "clean", "errors", "err", "AggregateError", "raise", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "Symbol", "toStringTag", "inspect", "custom", "toString", "asyncDispose", "name", "F", "pathSegments", "split", "L", "rejectOnDispose", "ctx", "error", "ContextDisposedError", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally", "throwUnhandledError", "LifecycleState", "CLOSE_RESOURCE_ON_UNHANDLED_ERROR", "Resource", "Symbol", "asyncDispose", "close", "Object", "getPrototypeOf", "name", "isOpen", "_lifecycleState", "_ctx", "_open", "_close", "_catch", "err", "doubleErr", "throwUnhandledError", "use", "fn", "open", "ctx", "Error", "waitUntilOpen", "derive", "Context", "default", "dispose", "attributeParent", "parent", "onError", "error", "queueMicrotask", "raise", "openInContext", "resource", "onDispose"]
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":21848},"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":7481},"src/context-disposed-error.ts":{"bytesInOutput":106},"src/index.ts":{"bytesInOutput":0},"src/promise-utils.ts":{"bytesInOutput":410},"src/resource.ts":{"bytesInOutput":3938}},"bytes":12219}}}
1
+ {"inputs":{"src/context-disposed-error.ts":{"bytes":784,"imports":[],"format":"esm"},"src/context.ts":{"bytes":28908,"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":17647,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"@hazae41/symbol-dispose-polyfill","kind":"import-statement","external":true}],"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":23386},"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},{"path":"@hazae41/symbol-dispose-polyfill","kind":"import-statement","external":true}],"exports":["Context","ContextDisposedError","LifecycleState","Resource","cancelWithContext","openInContext","rejectOnDispose"],"entryPoint":"src/index.ts","inputs":{"src/context.ts":{"bytesInOutput":7598},"src/context-disposed-error.ts":{"bytesInOutput":106},"src/index.ts":{"bytesInOutput":0},"src/promise-utils.ts":{"bytesInOutput":410},"src/resource.ts":{"bytesInOutput":4620}},"bytes":13018}}}
@@ -32,32 +32,25 @@ var DEFAULT_ERROR_HANDLER = (error, ctx) => {
32
32
  };
33
33
  var CONTEXT_FLAG_IS_DISPOSED = 1 << 0;
34
34
  var CONTEXT_FLAG_LEAK_DETECTED = 1 << 1;
35
- var _a, _b;
36
35
  var Context = class _Context {
36
+ static default() {
37
+ return new _Context();
38
+ }
39
+ #disposeCallbacks = [];
40
+ #name = void 0;
41
+ #parent = void 0;
42
+ #attributes;
43
+ #onError;
44
+ #flags = 0;
45
+ #disposePromise = void 0;
46
+ #signal = void 0;
47
+ maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
37
48
  constructor(params = {}, callMeta) {
38
- this.#disposeCallbacks = [];
39
- this.#name = void 0;
40
- this.#parent = void 0;
41
- this.#flags = 0;
42
- this.#disposePromise = void 0;
43
- this.maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;
44
- this[_b] = "Context";
45
- this[_a] = () => this.toString();
46
49
  this.#name = getContextName(params, callMeta);
47
50
  this.#parent = params.parent;
48
51
  this.#attributes = params.attributes ?? {};
49
52
  this.#onError = params.onError ?? DEFAULT_ERROR_HANDLER;
50
53
  }
51
- static default() {
52
- return new _Context();
53
- }
54
- #disposeCallbacks;
55
- #name;
56
- #parent;
57
- #attributes;
58
- #onError;
59
- #flags;
60
- #disposePromise;
61
54
  get #isDisposed() {
62
55
  return !!(this.#flags & CONTEXT_FLAG_IS_DISPOSED);
63
56
  }
@@ -76,6 +69,15 @@ var Context = class _Context {
76
69
  get disposeCallbacksLength() {
77
70
  return this.#disposeCallbacks.length;
78
71
  }
72
+ get signal() {
73
+ if (this.#signal) {
74
+ return this.#signal;
75
+ }
76
+ const controller = new AbortController();
77
+ this.#signal = controller.signal;
78
+ this.onDispose(() => controller.abort());
79
+ return this.#signal;
80
+ }
79
81
  /**
80
82
  * Schedules a callback to run when the context is disposed.
81
83
  * May be async, in this case the disposer might choose to wait for all resource to released.
@@ -95,7 +97,7 @@ var Context = class _Context {
95
97
  context: this.#name
96
98
  }, {
97
99
  F: __dxlog_file,
98
- L: 119,
100
+ L: 131,
99
101
  S: this,
100
102
  C: (f, a) => f(...a)
101
103
  });
@@ -112,7 +114,7 @@ var Context = class _Context {
112
114
  count: this.#disposeCallbacks.length
113
115
  }, {
114
116
  F: __dxlog_file,
115
- L: 128,
117
+ L: 140,
116
118
  S: this,
117
119
  C: (f, a) => f(...a)
118
120
  });
@@ -150,7 +152,7 @@ var Context = class _Context {
150
152
  count: callbacks.length
151
153
  }, {
152
154
  F: __dxlog_file,
153
- L: 173,
155
+ L: 185,
154
156
  S: this,
155
157
  C: (f, a) => f(...a)
156
158
  });
@@ -173,7 +175,7 @@ var Context = class _Context {
173
175
  count: callbacks.length
174
176
  }, {
175
177
  F: __dxlog_file,
176
- L: 188,
178
+ L: 200,
177
179
  S: this,
178
180
  C: (f, a) => f(...a)
179
181
  });
@@ -189,7 +191,7 @@ var Context = class _Context {
189
191
  context: this.#name
190
192
  }, {
191
193
  F: __dxlog_file,
192
- L: 199,
194
+ L: 211,
193
195
  S: this,
194
196
  C: (f, a) => f(...a)
195
197
  });
@@ -213,6 +215,7 @@ var Context = class _Context {
213
215
  }
214
216
  derive({ onError, attributes } = {}) {
215
217
  const newCtx = new _Context({
218
+ parent: this,
216
219
  // TODO(dmaretskyi): Optimize to not require allocating a new closure for every context.
217
220
  onError: async (error) => {
218
221
  if (!onError) {
@@ -240,10 +243,12 @@ var Context = class _Context {
240
243
  }
241
244
  return void 0;
242
245
  }
246
+ [Symbol.toStringTag] = "Context";
247
+ [inspect.custom] = () => this.toString();
243
248
  toString() {
244
249
  return `Context(${this.#isDisposed ? "disposed" : "active"})`;
245
250
  }
246
- async [(_b = Symbol.toStringTag, _a = inspect.custom, Symbol.asyncDispose)]() {
251
+ async [Symbol.asyncDispose]() {
247
252
  await this.dispose();
248
253
  }
249
254
  };
@@ -277,12 +282,13 @@ var cancelWithContext = (ctx, promise) => {
277
282
 
278
283
  // src/resource.ts
279
284
  import { throwUnhandledError } from "@dxos/util";
280
- var LifecycleState = /* @__PURE__ */ function(LifecycleState2) {
285
+ import "@hazae41/symbol-dispose-polyfill";
286
+ var LifecycleState = /* @__PURE__ */ (function(LifecycleState2) {
281
287
  LifecycleState2["CLOSED"] = "CLOSED";
282
288
  LifecycleState2["OPEN"] = "OPEN";
283
289
  LifecycleState2["ERROR"] = "ERROR";
284
290
  return LifecycleState2;
285
- }({});
291
+ })({});
286
292
  var CLOSE_RESOURCE_ON_UNHANDLED_ERROR = false;
287
293
  var Resource = class {
288
294
  #lifecycleState = "CLOSED";
@@ -299,6 +305,16 @@ var Resource = class {
299
305
  * Provided in the open method.
300
306
  */
301
307
  #parentCtx = this.#createParentContext();
308
+ /**
309
+ * ```ts
310
+ * await using resource = new Resource();
311
+ * await resource.open();
312
+ * ```
313
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
314
+ */
315
+ async [Symbol.asyncDispose]() {
316
+ await this.close();
317
+ }
302
318
  get #name() {
303
319
  return Object.getPrototypeOf(this).constructor.name;
304
320
  }
@@ -314,12 +330,12 @@ var Resource = class {
314
330
  /**
315
331
  * To be overridden by subclasses.
316
332
  */
317
- async _open(ctx) {
333
+ async _open(_ctx) {
318
334
  }
319
335
  /**
320
336
  * To be overridden by subclasses.
321
337
  */
322
- async _close(ctx) {
338
+ async _close(_ctx) {
323
339
  }
324
340
  /**
325
341
  * Error handler for errors that are caught by the context.
@@ -336,6 +352,19 @@ var Resource = class {
336
352
  throw err;
337
353
  }
338
354
  /**
355
+ * Calls the provided function, opening and closing the resource.
356
+ * NOTE: Consider using `using` instead.
357
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
358
+ */
359
+ async use(fn) {
360
+ try {
361
+ await this.open();
362
+ return await fn(this);
363
+ } finally {
364
+ await this.close();
365
+ }
366
+ }
367
+ /**
339
368
  * Opens the resource.
340
369
  * If the resource is already open, it does nothing.
341
370
  * If the resource is in an error state, it throws an error.
@@ -381,14 +410,12 @@ var Resource = class {
381
410
  }
382
411
  await this.#openPromise;
383
412
  }
384
- async [Symbol.asyncDispose]() {
385
- await this.close();
386
- }
387
413
  async #open(ctx) {
388
414
  this.#closePromise = null;
389
415
  this.#parentCtx = ctx?.derive({
390
416
  name: this.#name
391
417
  }) ?? this.#createParentContext();
418
+ this.#internalCtx = this.#createContext(this.#parentCtx);
392
419
  await this._open(this.#parentCtx);
393
420
  this.#lifecycleState = "OPEN";
394
421
  }
@@ -399,9 +426,10 @@ var Resource = class {
399
426
  this.#internalCtx = this.#createContext();
400
427
  this.#lifecycleState = "CLOSED";
401
428
  }
402
- #createContext() {
429
+ #createContext(attributeParent) {
403
430
  return new Context({
404
431
  name: this.#name,
432
+ parent: attributeParent,
405
433
  onError: (error) => queueMicrotask(async () => {
406
434
  try {
407
435
  await this._catch(error);