@dxos/context 0.3.1 → 0.3.2-main.0f6af28

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.
@@ -1,6 +1,15 @@
1
1
  // packages/common/context/src/context.ts
2
2
  import { log } from "@dxos/log";
3
3
  import { safeInstanceof } from "@dxos/util";
4
+
5
+ // packages/common/context/src/context-disposed.ts
6
+ var ContextDisposedError = class extends Error {
7
+ constructor() {
8
+ super("Context disposed.");
9
+ }
10
+ };
11
+
12
+ // packages/common/context/src/context.ts
4
13
  function _ts_decorate(decorators, target, key, desc) {
5
14
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6
15
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -11,10 +20,13 @@ function _ts_decorate(decorators, target, key, desc) {
11
20
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
12
21
  return c > 3 && r && Object.defineProperty(target, key, r), r;
13
22
  }
14
- var __dxlog_file = "/mnt/ramdisk/work/packages/common/context/src/context.ts";
23
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/context/src/context.ts";
15
24
  var MAX_SAFE_DISPOSE_CALLBACKS = 300;
16
25
  var Context = class Context1 {
17
26
  constructor({ onError = (error) => {
27
+ if (error instanceof ContextDisposedError) {
28
+ return;
29
+ }
18
30
  void this.dispose();
19
31
  throw error;
20
32
  }, attributes = {}, parent } = {}) {
@@ -49,7 +61,7 @@ var Context = class Context1 {
49
61
  } catch (error) {
50
62
  log.catch(error, void 0, {
51
63
  F: __dxlog_file,
52
- L: 72,
64
+ L: 78,
53
65
  S: this,
54
66
  C: (f, a) => f(...a)
55
67
  });
@@ -63,7 +75,7 @@ var Context = class Context1 {
63
75
  safeThreshold: this.maxSafeDisposeCallbacks
64
76
  }, {
65
77
  F: __dxlog_file,
66
- L: 79,
78
+ L: 85,
67
79
  S: this,
68
80
  C: (f, a) => f(...a)
69
81
  });
@@ -96,7 +108,7 @@ var Context = class Context1 {
96
108
  } catch (error) {
97
109
  log.catch(error, void 0, {
98
110
  F: __dxlog_file,
99
- L: 114,
111
+ L: 120,
100
112
  S: this,
101
113
  C: (f, a) => f(...a)
102
114
  });
@@ -157,7 +169,7 @@ Context = _ts_decorate([
157
169
  ], Context);
158
170
 
159
171
  // packages/common/context/src/promise-utils.ts
160
- var rejectOnDispose = (ctx, error = new Error("CANCELLED")) => new Promise((resolve, reject) => {
172
+ var rejectOnDispose = (ctx, error = new ContextDisposedError()) => new Promise((resolve, reject) => {
161
173
  ctx.onDispose(() => reject(error));
162
174
  });
163
175
  var cancelWithContext = (ctx, promise) => {
@@ -165,12 +177,13 @@ var cancelWithContext = (ctx, promise) => {
165
177
  return Promise.race([
166
178
  promise,
167
179
  new Promise((resolve, reject) => {
168
- clearDispose = ctx.onDispose(() => reject(new Error("CANCELLED")));
180
+ clearDispose = ctx.onDispose(() => reject(new ContextDisposedError()));
169
181
  })
170
182
  ]).finally(() => clearDispose?.());
171
183
  };
172
184
  export {
173
185
  Context,
186
+ ContextDisposedError,
174
187
  cancelWithContext,
175
188
  rejectOnDispose
176
189
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/context.ts", "../../../src/promise-utils.ts"],
4
- "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { log } from '@dxos/log';\nimport { safeInstanceof } from '@dxos/util';\n\nexport type ContextErrorHandler = (error: Error) => void;\n\nexport type DisposeCallback = () => void | Promise<void>;\n\nexport type CreateContextParams = {\n onError?: ContextErrorHandler;\n attributes?: Record<string, any>;\n parent?: Context;\n};\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\n@safeInstanceof('Context')\nexport class Context {\n private readonly _onError: ContextErrorHandler;\n private readonly _disposeCallbacks: DisposeCallback[] = [];\n private _isDisposed = false;\n private _disposePromise?: Promise<void> = undefined;\n private _parent: Context | null = null;\n\n private _attributes: Record<string, any>;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor({\n onError = (error) => {\n void this.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n },\n attributes = {},\n parent,\n }: CreateContextParams = {}) {\n this._onError = onError;\n this._attributes = attributes;\n if (parent !== undefined) {\n this._parent = parent;\n }\n }\n\n get disposed() {\n return this._isDisposed;\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) {\n if (this._isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })();\n }\n\n this._disposeCallbacks.push(callback);\n if (this._disposeCallbacks.length > this.maxSafeDisposeCallbacks) {\n log.warn('Context has a large number of dispose callbacks. This might be a memory leak.', {\n count: this._disposeCallbacks.length,\n safeThreshold: this.maxSafeDisposeCallbacks,\n });\n }\n\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 * Sync callbacks are run in the reverse order they were added.\n * Async callbacks are run in parallel.\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 */\n dispose(): Promise<void> {\n if (this._disposePromise) {\n return this._disposePromise;\n }\n this._isDisposed = true;\n\n const promises = [];\n for (const callback of this._disposeCallbacks.reverse()) {\n promises.push(\n (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })(),\n );\n }\n this._disposeCallbacks.length = 0;\n\n return (this._disposePromise = Promise.all(promises).then(() => {}));\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);\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);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\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 !== null) {\n return this._parent.getAttribute(key);\n }\n return undefined;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { Context } from './context';\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 Error('CANCELLED')): 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 Error('CANCELLED')));\n }),\n ]).finally(() => clearDispose?.());\n};\n"],
5
- "mappings": "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",
6
- "names": ["log", "safeInstanceof", "MAX_SAFE_DISPOSE_CALLBACKS", "Context", "constructor", "onError", "error", "dispose", "attributes", "parent", "_disposeCallbacks", "_isDisposed", "_disposePromise", "undefined", "_parent", "maxSafeDisposeCallbacks", "_onError", "_attributes", "disposed", "onDispose", "callback", "catch", "push", "length", "warn", "count", "safeThreshold", "index", "indexOf", "splice", "promises", "reverse", "Promise", "all", "then", "raise", "err", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "rejectOnDispose", "ctx", "error", "Error", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally"]
3
+ "sources": ["../../../src/context.ts", "../../../src/context-disposed.ts", "../../../src/promise-utils.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { log } from '@dxos/log';\nimport { safeInstanceof } from '@dxos/util';\n\nimport { ContextDisposedError } from './context-disposed';\n\nexport type ContextErrorHandler = (error: Error) => void;\n\nexport type DisposeCallback = () => void | Promise<void>;\n\nexport type CreateContextParams = {\n onError?: ContextErrorHandler;\n attributes?: Record<string, any>;\n parent?: Context;\n};\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\n@safeInstanceof('Context')\nexport class Context {\n private readonly _onError: ContextErrorHandler;\n private readonly _disposeCallbacks: DisposeCallback[] = [];\n private _isDisposed = false;\n private _disposePromise?: Promise<void> = undefined;\n private _parent: Context | null = null;\n\n private _attributes: Record<string, any>;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor({\n onError = (error) => {\n if (error instanceof ContextDisposedError) {\n return;\n }\n\n void this.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n },\n attributes = {},\n parent,\n }: CreateContextParams = {}) {\n this._onError = onError;\n this._attributes = attributes;\n if (parent !== undefined) {\n this._parent = parent;\n }\n }\n\n get disposed() {\n return this._isDisposed;\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) {\n if (this._isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })();\n }\n\n this._disposeCallbacks.push(callback);\n if (this._disposeCallbacks.length > this.maxSafeDisposeCallbacks) {\n log.warn('Context has a large number of dispose callbacks. This might be a memory leak.', {\n count: this._disposeCallbacks.length,\n safeThreshold: this.maxSafeDisposeCallbacks,\n });\n }\n\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 * Sync callbacks are run in the reverse order they were added.\n * Async callbacks are run in parallel.\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 */\n dispose(): Promise<void> {\n if (this._disposePromise) {\n return this._disposePromise;\n }\n this._isDisposed = true;\n\n const promises = [];\n for (const callback of this._disposeCallbacks.reverse()) {\n promises.push(\n (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })(),\n );\n }\n this._disposeCallbacks.length = 0;\n\n return (this._disposePromise = Promise.all(promises).then(() => {}));\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);\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);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\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 !== null) {\n return this._parent.getAttribute(key);\n }\n return undefined;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nexport class ContextDisposedError extends Error {\n constructor() {\n super('Context disposed.');\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context } from './context';\nimport { ContextDisposedError } from './context-disposed';\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"],
5
+ "mappings": "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",
6
+ "names": ["log", "safeInstanceof", "ContextDisposedError", "Error", "constructor", "MAX_SAFE_DISPOSE_CALLBACKS", "Context", "constructor", "onError", "error", "ContextDisposedError", "dispose", "attributes", "parent", "_disposeCallbacks", "_isDisposed", "_disposePromise", "undefined", "_parent", "maxSafeDisposeCallbacks", "_onError", "_attributes", "disposed", "onDispose", "callback", "log", "catch", "push", "length", "warn", "count", "safeThreshold", "index", "indexOf", "splice", "promises", "reverse", "Promise", "all", "then", "raise", "err", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "safeInstanceof", "rejectOnDispose", "ctx", "error", "ContextDisposedError", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/common/context/src/context.ts":{"bytes":16765,"imports":[{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":2801,"imports":[],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":553,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":9050},"packages/common/context/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Context","cancelWithContext","rejectOnDispose"],"entryPoint":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/context.ts":{"bytesInOutput":4863},"packages/common/context/src/index.ts":{"bytesInOutput":0},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":402}},"bytes":5453}}}
1
+ {"inputs":{"packages/common/context/src/context-disposed.ts":{"bytes":803,"imports":[],"format":"esm"},"packages/common/context/src/context.ts":{"bytes":17229,"imports":[{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context-disposed.ts","kind":"import-statement","original":"./context-disposed"}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":3026,"imports":[{"path":"packages/common/context/src/context-disposed.ts","kind":"import-statement","original":"./context-disposed"}],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":675,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"packages/common/context/src/context-disposed.ts","kind":"import-statement","original":"./context-disposed"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":9676},"packages/common/context/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Context","ContextDisposedError","cancelWithContext","rejectOnDispose"],"entryPoint":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/context.ts":{"bytesInOutput":4942},"packages/common/context/src/context-disposed.ts":{"bytesInOutput":106},"packages/common/context/src/index.ts":{"bytesInOutput":0},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":410}},"bytes":5765}}}
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Context: () => Context,
24
+ ContextDisposedError: () => ContextDisposedError,
24
25
  cancelWithContext: () => cancelWithContext,
25
26
  rejectOnDispose: () => rejectOnDispose
26
27
  });
@@ -29,6 +30,15 @@ module.exports = __toCommonJS(src_exports);
29
30
  // packages/common/context/src/context.ts
30
31
  var import_log = require("@dxos/log");
31
32
  var import_util = require("@dxos/util");
33
+
34
+ // packages/common/context/src/context-disposed.ts
35
+ var ContextDisposedError = class extends Error {
36
+ constructor() {
37
+ super("Context disposed.");
38
+ }
39
+ };
40
+
41
+ // packages/common/context/src/context.ts
32
42
  function _ts_decorate(decorators, target, key, desc) {
33
43
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
34
44
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -39,10 +49,13 @@ function _ts_decorate(decorators, target, key, desc) {
39
49
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
40
50
  return c > 3 && r && Object.defineProperty(target, key, r), r;
41
51
  }
42
- var __dxlog_file = "/mnt/ramdisk/work/packages/common/context/src/context.ts";
52
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/context/src/context.ts";
43
53
  var MAX_SAFE_DISPOSE_CALLBACKS = 300;
44
54
  var Context = class Context1 {
45
55
  constructor({ onError = (error) => {
56
+ if (error instanceof ContextDisposedError) {
57
+ return;
58
+ }
46
59
  void this.dispose();
47
60
  throw error;
48
61
  }, attributes = {}, parent } = {}) {
@@ -77,7 +90,7 @@ var Context = class Context1 {
77
90
  } catch (error) {
78
91
  import_log.log.catch(error, void 0, {
79
92
  F: __dxlog_file,
80
- L: 72,
93
+ L: 78,
81
94
  S: this,
82
95
  C: (f, a) => f(...a)
83
96
  });
@@ -91,7 +104,7 @@ var Context = class Context1 {
91
104
  safeThreshold: this.maxSafeDisposeCallbacks
92
105
  }, {
93
106
  F: __dxlog_file,
94
- L: 79,
107
+ L: 85,
95
108
  S: this,
96
109
  C: (f, a) => f(...a)
97
110
  });
@@ -124,7 +137,7 @@ var Context = class Context1 {
124
137
  } catch (error) {
125
138
  import_log.log.catch(error, void 0, {
126
139
  F: __dxlog_file,
127
- L: 114,
140
+ L: 120,
128
141
  S: this,
129
142
  C: (f, a) => f(...a)
130
143
  });
@@ -185,7 +198,7 @@ Context = _ts_decorate([
185
198
  ], Context);
186
199
 
187
200
  // packages/common/context/src/promise-utils.ts
188
- var rejectOnDispose = (ctx, error = new Error("CANCELLED")) => new Promise((resolve, reject) => {
201
+ var rejectOnDispose = (ctx, error = new ContextDisposedError()) => new Promise((resolve, reject) => {
189
202
  ctx.onDispose(() => reject(error));
190
203
  });
191
204
  var cancelWithContext = (ctx, promise) => {
@@ -193,13 +206,14 @@ var cancelWithContext = (ctx, promise) => {
193
206
  return Promise.race([
194
207
  promise,
195
208
  new Promise((resolve, reject) => {
196
- clearDispose = ctx.onDispose(() => reject(new Error("CANCELLED")));
209
+ clearDispose = ctx.onDispose(() => reject(new ContextDisposedError()));
197
210
  })
198
211
  ]).finally(() => clearDispose?.());
199
212
  };
200
213
  // Annotate the CommonJS export names for ESM import in node:
201
214
  0 && (module.exports = {
202
215
  Context,
216
+ ContextDisposedError,
203
217
  cancelWithContext,
204
218
  rejectOnDispose
205
219
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/index.ts", "../../../src/context.ts", "../../../src/promise-utils.ts"],
4
- "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nexport * from './context';\nexport * from './promise-utils';\n", "//\n// Copyright 2022 DXOS.org\n//\n\nimport { log } from '@dxos/log';\nimport { safeInstanceof } from '@dxos/util';\n\nexport type ContextErrorHandler = (error: Error) => void;\n\nexport type DisposeCallback = () => void | Promise<void>;\n\nexport type CreateContextParams = {\n onError?: ContextErrorHandler;\n attributes?: Record<string, any>;\n parent?: Context;\n};\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\n@safeInstanceof('Context')\nexport class Context {\n private readonly _onError: ContextErrorHandler;\n private readonly _disposeCallbacks: DisposeCallback[] = [];\n private _isDisposed = false;\n private _disposePromise?: Promise<void> = undefined;\n private _parent: Context | null = null;\n\n private _attributes: Record<string, any>;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor({\n onError = (error) => {\n void this.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n },\n attributes = {},\n parent,\n }: CreateContextParams = {}) {\n this._onError = onError;\n this._attributes = attributes;\n if (parent !== undefined) {\n this._parent = parent;\n }\n }\n\n get disposed() {\n return this._isDisposed;\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) {\n if (this._isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })();\n }\n\n this._disposeCallbacks.push(callback);\n if (this._disposeCallbacks.length > this.maxSafeDisposeCallbacks) {\n log.warn('Context has a large number of dispose callbacks. This might be a memory leak.', {\n count: this._disposeCallbacks.length,\n safeThreshold: this.maxSafeDisposeCallbacks,\n });\n }\n\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 * Sync callbacks are run in the reverse order they were added.\n * Async callbacks are run in parallel.\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 */\n dispose(): Promise<void> {\n if (this._disposePromise) {\n return this._disposePromise;\n }\n this._isDisposed = true;\n\n const promises = [];\n for (const callback of this._disposeCallbacks.reverse()) {\n promises.push(\n (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })(),\n );\n }\n this._disposeCallbacks.length = 0;\n\n return (this._disposePromise = Promise.all(promises).then(() => {}));\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);\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);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\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 !== null) {\n return this._parent.getAttribute(key);\n }\n return undefined;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { Context } from './context';\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 Error('CANCELLED')): 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 Error('CANCELLED')));\n }),\n ]).finally(() => clearDispose?.());\n};\n"],
5
- "mappings": "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",
6
- "names": ["MAX_SAFE_DISPOSE_CALLBACKS", "Context", "constructor", "onError", "error", "dispose", "attributes", "parent", "_disposeCallbacks", "_isDisposed", "_disposePromise", "undefined", "_parent", "maxSafeDisposeCallbacks", "_onError", "_attributes", "disposed", "onDispose", "callback", "log", "catch", "push", "length", "warn", "count", "safeThreshold", "index", "indexOf", "splice", "promises", "reverse", "Promise", "all", "then", "raise", "err", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "safeInstanceof", "rejectOnDispose", "ctx", "error", "Error", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally"]
3
+ "sources": ["../../../src/index.ts", "../../../src/context.ts", "../../../src/context-disposed.ts", "../../../src/promise-utils.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nexport * from './context';\nexport * from './promise-utils';\nexport * from './context-disposed';\n", "//\n// Copyright 2022 DXOS.org\n//\n\nimport { log } from '@dxos/log';\nimport { safeInstanceof } from '@dxos/util';\n\nimport { ContextDisposedError } from './context-disposed';\n\nexport type ContextErrorHandler = (error: Error) => void;\n\nexport type DisposeCallback = () => void | Promise<void>;\n\nexport type CreateContextParams = {\n onError?: ContextErrorHandler;\n attributes?: Record<string, any>;\n parent?: Context;\n};\n\n/**\n * Maximum number of dispose callbacks before we start logging warnings.\n */\nconst MAX_SAFE_DISPOSE_CALLBACKS = 300;\n\n@safeInstanceof('Context')\nexport class Context {\n private readonly _onError: ContextErrorHandler;\n private readonly _disposeCallbacks: DisposeCallback[] = [];\n private _isDisposed = false;\n private _disposePromise?: Promise<void> = undefined;\n private _parent: Context | null = null;\n\n private _attributes: Record<string, any>;\n\n public maxSafeDisposeCallbacks = MAX_SAFE_DISPOSE_CALLBACKS;\n\n constructor({\n onError = (error) => {\n if (error instanceof ContextDisposedError) {\n return;\n }\n\n void this.dispose();\n\n // Will generate an unhandled rejection.\n throw error;\n },\n attributes = {},\n parent,\n }: CreateContextParams = {}) {\n this._onError = onError;\n this._attributes = attributes;\n if (parent !== undefined) {\n this._parent = parent;\n }\n }\n\n get disposed() {\n return this._isDisposed;\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) {\n if (this._isDisposed) {\n // Call the callback immediately if the context is already disposed.\n void (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })();\n }\n\n this._disposeCallbacks.push(callback);\n if (this._disposeCallbacks.length > this.maxSafeDisposeCallbacks) {\n log.warn('Context has a large number of dispose callbacks. This might be a memory leak.', {\n count: this._disposeCallbacks.length,\n safeThreshold: this.maxSafeDisposeCallbacks,\n });\n }\n\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 * Sync callbacks are run in the reverse order they were added.\n * Async callbacks are run in parallel.\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 */\n dispose(): Promise<void> {\n if (this._disposePromise) {\n return this._disposePromise;\n }\n this._isDisposed = true;\n\n const promises = [];\n for (const callback of this._disposeCallbacks.reverse()) {\n promises.push(\n (async () => {\n try {\n await callback();\n } catch (error: any) {\n log.catch(error);\n }\n })(),\n );\n }\n this._disposeCallbacks.length = 0;\n\n return (this._disposePromise = Promise.all(promises).then(() => {}));\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);\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);\n } catch {\n this.raise(error);\n }\n }\n },\n attributes,\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 !== null) {\n return this._parent.getAttribute(key);\n }\n return undefined;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nexport class ContextDisposedError extends Error {\n constructor() {\n super('Context disposed.');\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context } from './context';\nimport { ContextDisposedError } from './context-disposed';\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"],
5
+ "mappings": "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",
6
+ "names": ["ContextDisposedError", "Error", "constructor", "MAX_SAFE_DISPOSE_CALLBACKS", "Context", "constructor", "onError", "error", "ContextDisposedError", "dispose", "attributes", "parent", "_disposeCallbacks", "_isDisposed", "_disposePromise", "undefined", "_parent", "maxSafeDisposeCallbacks", "_onError", "_attributes", "disposed", "onDispose", "callback", "log", "catch", "push", "length", "warn", "count", "safeThreshold", "index", "indexOf", "splice", "promises", "reverse", "Promise", "all", "then", "raise", "err", "reject", "derive", "newCtx", "clearDispose", "getAttribute", "key", "safeInstanceof", "rejectOnDispose", "ctx", "error", "ContextDisposedError", "Promise", "resolve", "reject", "onDispose", "cancelWithContext", "promise", "clearDispose", "race", "finally"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/common/context/src/context.ts":{"bytes":16765,"imports":[{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":2801,"imports":[],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":553,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":9207},"packages/common/context/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/log","kind":"require-call","external":true},{"path":"@dxos/util","kind":"require-call","external":true}],"exports":[],"entryPoint":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/index.ts":{"bytesInOutput":207},"packages/common/context/src/context.ts":{"bytesInOutput":4915},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":402}},"bytes":6632}}}
1
+ {"inputs":{"packages/common/context/src/context-disposed.ts":{"bytes":803,"imports":[],"format":"esm"},"packages/common/context/src/context.ts":{"bytes":17229,"imports":[{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/common/context/src/context-disposed.ts","kind":"import-statement","original":"./context-disposed"}],"format":"esm"},"packages/common/context/src/promise-utils.ts":{"bytes":3026,"imports":[{"path":"packages/common/context/src/context-disposed.ts","kind":"import-statement","original":"./context-disposed"}],"format":"esm"},"packages/common/context/src/index.ts":{"bytes":675,"imports":[{"path":"packages/common/context/src/context.ts","kind":"import-statement","original":"./context"},{"path":"packages/common/context/src/promise-utils.ts","kind":"import-statement","original":"./promise-utils"},{"path":"packages/common/context/src/context-disposed.ts","kind":"import-statement","original":"./context-disposed"}],"format":"esm"}},"outputs":{"packages/common/context/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":9844},"packages/common/context/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/log","kind":"require-call","external":true},{"path":"@dxos/util","kind":"require-call","external":true}],"exports":[],"entryPoint":"packages/common/context/src/index.ts","inputs":{"packages/common/context/src/index.ts":{"bytesInOutput":259},"packages/common/context/src/context.ts":{"bytesInOutput":4994},"packages/common/context/src/context-disposed.ts":{"bytesInOutput":106},"packages/common/context/src/promise-utils.ts":{"bytesInOutput":410}},"bytes":6996}}}
@@ -0,0 +1,4 @@
1
+ export declare class ContextDisposedError extends Error {
2
+ constructor();
3
+ }
4
+ //# sourceMappingURL=context-disposed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-disposed.d.ts","sourceRoot":"","sources":["../../../src/context-disposed.ts"],"names":[],"mappings":"AAIA,qBAAa,oBAAqB,SAAQ,KAAK;;CAI9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/context.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAOF,qBACa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAC,CAA4B;IACpD,OAAO,CAAC,OAAO,CAAwB;IAEvC,OAAO,CAAC,WAAW,CAAsB;IAElC,uBAAuB,SAA8B;gBAEhD,EACV,OAKC,EACD,UAAe,EACf,MAAM,GACP,GAAE,mBAAwB;IAQ3B,IAAI,QAAQ,YAEX;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,eAAe;IA4BnC;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBxB;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAezB,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAE,mBAAwB,GAAG,OAAO;IAqBlE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;CAS/B"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/context.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAOF,qBACa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAC,CAA4B;IACpD,OAAO,CAAC,OAAO,CAAwB;IAEvC,OAAO,CAAC,WAAW,CAAsB;IAElC,uBAAuB,SAA8B;gBAEhD,EACV,OASC,EACD,UAAe,EACf,MAAM,GACP,GAAE,mBAAwB;IAQ3B,IAAI,QAAQ,YAEX;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,eAAe;IA4BnC;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBxB;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAezB,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAE,mBAAwB,GAAG,OAAO;IAqBlE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;CAS/B"}
@@ -1,3 +1,4 @@
1
1
  export * from './context';
2
2
  export * from './promise-utils';
3
+ export * from './context-disposed';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
@@ -1,8 +1,9 @@
1
- import { Context } from './context';
1
+ import { type Context } from './context';
2
+ import { ContextDisposedError } from './context-disposed';
2
3
  /**
3
4
  * @returns A promise that rejects when the context is disposed.
4
5
  */
5
- export declare const rejectOnDispose: (ctx: Context, error?: Error) => Promise<never>;
6
+ export declare const rejectOnDispose: (ctx: Context, error?: ContextDisposedError) => Promise<never>;
6
7
  /**
7
8
  * Rejects the promise if the context is disposed.
8
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"promise-utils.d.ts","sourceRoot":"","sources":["../../../src/promise-utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;GAEG;AAEH,eAAO,MAAM,eAAe,QAAS,OAAO,oBAAmC,QAAQ,KAAK,CAGxF,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,iBAAiB,WAAY,OAAO,oCAShD,CAAC"}
1
+ {"version":3,"file":"promise-utils.d.ts","sourceRoot":"","sources":["../../../src/promise-utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;GAEG;AAEH,eAAO,MAAM,eAAe,QAAS,OAAO,mCAAuC,QAAQ,KAAK,CAG5F,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,iBAAiB,WAAY,OAAO,oCAShD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/context",
3
- "version": "0.3.1",
3
+ "version": "0.3.2-main.0f6af28",
4
4
  "description": "Async utils.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,8 +16,8 @@
16
16
  "src"
17
17
  ],
18
18
  "dependencies": {
19
- "@dxos/log": "0.3.1",
20
- "@dxos/util": "0.3.1"
19
+ "@dxos/log": "0.3.2-main.0f6af28",
20
+ "@dxos/util": "0.3.2-main.0f6af28"
21
21
  },
22
22
  "publishConfig": {
23
23
  "access": "public"
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export class ContextDisposedError extends Error {
6
+ constructor() {
7
+ super('Context disposed.');
8
+ }
9
+ }
package/src/context.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  import { log } from '@dxos/log';
6
6
  import { safeInstanceof } from '@dxos/util';
7
7
 
8
+ import { ContextDisposedError } from './context-disposed';
9
+
8
10
  export type ContextErrorHandler = (error: Error) => void;
9
11
 
10
12
  export type DisposeCallback = () => void | Promise<void>;
@@ -34,6 +36,10 @@ export class Context {
34
36
 
35
37
  constructor({
36
38
  onError = (error) => {
39
+ if (error instanceof ContextDisposedError) {
40
+ return;
41
+ }
42
+
37
43
  void this.dispose();
38
44
 
39
45
  // Will generate an unhandled rejection.
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@
4
4
 
5
5
  export * from './context';
6
6
  export * from './promise-utils';
7
+ export * from './context-disposed';
@@ -2,13 +2,14 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Context } from './context';
5
+ import { type Context } from './context';
6
+ import { ContextDisposedError } from './context-disposed';
6
7
 
7
8
  /**
8
9
  * @returns A promise that rejects when the context is disposed.
9
10
  */
10
11
  // TODO(dmaretskyi): Memory leak.
11
- export const rejectOnDispose = (ctx: Context, error = new Error('CANCELLED')): Promise<never> =>
12
+ export const rejectOnDispose = (ctx: Context, error = new ContextDisposedError()): Promise<never> =>
12
13
  new Promise((resolve, reject) => {
13
14
  ctx.onDispose(() => reject(error));
14
15
  });
@@ -22,7 +23,7 @@ export const cancelWithContext = <T>(ctx: Context, promise: Promise<T>): Promise
22
23
  promise,
23
24
  new Promise<never>((resolve, reject) => {
24
25
  // Will be called before .finally() handlers.
25
- clearDispose = ctx.onDispose(() => reject(new Error('CANCELLED')));
26
+ clearDispose = ctx.onDispose(() => reject(new ContextDisposedError()));
26
27
  }),
27
28
  ]).finally(() => clearDispose?.());
28
29
  };