@standardserver/shared 0.0.6 → 0.0.8

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.
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- declare function toArray<T>(value: T): T extends readonly any[] ? T : Exclude<T, undefined | null>[];
1
+ declare function toArray<T>(value: T): T extends readonly any[] ? T : (T extends undefined | null ? never : T[]);
2
2
 
3
3
  declare const PACKAGE_NAME = "@standardserver/shared";
4
- declare const PACKAGE_VERSION = "0.0.6";
4
+ declare const PACKAGE_VERSION = "0.0.8";
5
5
  /**
6
6
  * Generates a unique symbol for the specified name within the package scope.
7
7
  * The symbol is globally registered using `Symbol.for` to ensure consistency across modules.
@@ -19,6 +19,11 @@ declare function getPackageSymbol(name: string): symbol;
19
19
  declare class AbortError extends Error {
20
20
  constructor(...rest: ConstructorParameters<typeof Error>);
21
21
  }
22
+ /**
23
+ * Forwards an error to the global unhandled rejection handler via a rejected Promise.
24
+ * Useful for routing errors from sync contexts into async error pipelines.
25
+ */
26
+ declare function emitUnhandledRejection(error: unknown): void;
22
27
 
23
28
  /**
24
29
  * Sequentially execute the function, if the previous execution is not completed, the next execution will be blocked
@@ -31,7 +36,10 @@ declare class SequentialIdGenerator {
31
36
  }
32
37
 
33
38
  interface AsyncCleanupFn {
34
- (isCompleted: boolean): Promise<void>;
39
+ (state: {
40
+ isCancelled: boolean;
41
+ error?: unknown;
42
+ }): Promise<void>;
35
43
  }
36
44
 
37
45
  declare function isAsyncIteratorObject(maybe: unknown): maybe is AsyncIteratorObject<any, any, any>;
@@ -49,7 +57,7 @@ declare class AsyncIteratorClass<T, TReturn = unknown, TNext = unknown> implemen
49
57
  readonly next: AsyncIteratorClassNextFn<T, TReturn>;
50
58
  constructor(next: AsyncIteratorClassNextFn<T, TReturn>, cleanup: AsyncCleanupFn);
51
59
  return(value?: any): Promise<IteratorResult<T, TReturn>>;
52
- throw(err: any): Promise<IteratorResult<T, TReturn>>;
60
+ throw(error: any): Promise<IteratorResult<T, TReturn>>;
53
61
  /**
54
62
  * asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
55
63
  */
@@ -66,6 +74,10 @@ declare function stringifyJSON<T>(value: T | {
66
74
  * Checks whether the provided container is a typescript object (object or function).
67
75
  */
68
76
  declare function isTypescriptObject(maybeObject: unknown): maybeObject is object & Record<PropertyKey, unknown>;
77
+ /**
78
+ * Creates a new object with the specified keys omitted.
79
+ */
80
+ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
69
81
 
70
82
  interface GetOrBindOptions {
71
83
  /**
@@ -77,41 +89,42 @@ interface GetOrBindOptions {
77
89
  }
78
90
  declare function getOrBind<T extends object, K extends PropertyKey>(target: T, property: K, { bind }?: GetOrBindOptions): K extends keyof T ? T[K] : unknown;
79
91
 
80
- interface AsyncIdQueueCloseOptions {
81
- id?: string;
82
- reason?: unknown;
83
- }
84
- interface AsyncIdQueueOptions {
92
+ declare class Queue<T> {
93
+ private readonly items;
94
+ private readonly pendingPulls;
95
+ private closed;
96
+ /**
97
+ * Pushes an item into the queue.
98
+ * @throws when the queue is closed or aborted
99
+ */
100
+ push(item: T): void;
85
101
  /**
86
- * Maximum number of buffered items per queue.
102
+ * Pulls the next item from the queue.
87
103
  *
88
- * @default Infinity
104
+ * @throws when the queue is closed or aborted. Note that buffered items can still be pulled after close until the buffer is drained.
89
105
  */
90
- maxBufferedSize?: number;
91
- }
92
- declare class AsyncIdQueue<T> {
93
- private readonly maxBufferedSize;
94
- private readonly openIds;
95
- private readonly queues;
96
- private readonly waiters;
97
- constructor(options?: AsyncIdQueueOptions);
98
- get length(): number;
99
- get waiterIds(): string[];
100
- hasBufferedItems(id: string): boolean;
101
- open(id: string): void;
102
- isOpen(id: string): boolean;
103
- push(id: string, item: T): void;
104
- pull(id: string): Promise<T>;
105
- close({ id, reason }?: AsyncIdQueueCloseOptions): void;
106
- assertOpen(id: string): void;
106
+ pull(): Promise<T>;
107
+ /**
108
+ * Closes the queue and rejects any pending pulls.
109
+ * Buffered items remain available to be pulled. Repeated calls are ignored.
110
+ */
111
+ close(reason?: unknown): void;
112
+ /**
113
+ * Aborts the queue.
114
+ * Unlike `close()`, this also discards any buffered items before closing.
115
+ */
116
+ abort(reason?: unknown): void;
107
117
  }
108
118
 
119
+ interface SleepOptions {
120
+ signal?: AbortSignal | undefined;
121
+ }
109
122
  /**
110
123
  * sleep for a specified amount of time
111
124
  */
112
- declare function sleep(ms: number): Promise<void>;
125
+ declare function sleep(ms: number, { signal }?: SleepOptions): Promise<void>;
113
126
 
114
127
  declare function tryDecodeURIComponent(value: string): string;
115
128
 
116
- export { AbortError, AsyncIdQueue, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, SequentialIdGenerator, getOrBind, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
117
- export type { AsyncCleanupFn, AsyncIdQueueCloseOptions, AsyncIdQueueOptions, AsyncIteratorClassNextFn, GetOrBindOptions };
129
+ export { AbortError, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, Queue, SequentialIdGenerator, emitUnhandledRejection, getOrBind, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, omit, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
130
+ export type { AsyncCleanupFn, AsyncIteratorClassNextFn, GetOrBindOptions, SleepOptions };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- declare function toArray<T>(value: T): T extends readonly any[] ? T : Exclude<T, undefined | null>[];
1
+ declare function toArray<T>(value: T): T extends readonly any[] ? T : (T extends undefined | null ? never : T[]);
2
2
 
3
3
  declare const PACKAGE_NAME = "@standardserver/shared";
4
- declare const PACKAGE_VERSION = "0.0.6";
4
+ declare const PACKAGE_VERSION = "0.0.8";
5
5
  /**
6
6
  * Generates a unique symbol for the specified name within the package scope.
7
7
  * The symbol is globally registered using `Symbol.for` to ensure consistency across modules.
@@ -19,6 +19,11 @@ declare function getPackageSymbol(name: string): symbol;
19
19
  declare class AbortError extends Error {
20
20
  constructor(...rest: ConstructorParameters<typeof Error>);
21
21
  }
22
+ /**
23
+ * Forwards an error to the global unhandled rejection handler via a rejected Promise.
24
+ * Useful for routing errors from sync contexts into async error pipelines.
25
+ */
26
+ declare function emitUnhandledRejection(error: unknown): void;
22
27
 
23
28
  /**
24
29
  * Sequentially execute the function, if the previous execution is not completed, the next execution will be blocked
@@ -31,7 +36,10 @@ declare class SequentialIdGenerator {
31
36
  }
32
37
 
33
38
  interface AsyncCleanupFn {
34
- (isCompleted: boolean): Promise<void>;
39
+ (state: {
40
+ isCancelled: boolean;
41
+ error?: unknown;
42
+ }): Promise<void>;
35
43
  }
36
44
 
37
45
  declare function isAsyncIteratorObject(maybe: unknown): maybe is AsyncIteratorObject<any, any, any>;
@@ -49,7 +57,7 @@ declare class AsyncIteratorClass<T, TReturn = unknown, TNext = unknown> implemen
49
57
  readonly next: AsyncIteratorClassNextFn<T, TReturn>;
50
58
  constructor(next: AsyncIteratorClassNextFn<T, TReturn>, cleanup: AsyncCleanupFn);
51
59
  return(value?: any): Promise<IteratorResult<T, TReturn>>;
52
- throw(err: any): Promise<IteratorResult<T, TReturn>>;
60
+ throw(error: any): Promise<IteratorResult<T, TReturn>>;
53
61
  /**
54
62
  * asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
55
63
  */
@@ -66,6 +74,10 @@ declare function stringifyJSON<T>(value: T | {
66
74
  * Checks whether the provided container is a typescript object (object or function).
67
75
  */
68
76
  declare function isTypescriptObject(maybeObject: unknown): maybeObject is object & Record<PropertyKey, unknown>;
77
+ /**
78
+ * Creates a new object with the specified keys omitted.
79
+ */
80
+ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
69
81
 
70
82
  interface GetOrBindOptions {
71
83
  /**
@@ -77,41 +89,42 @@ interface GetOrBindOptions {
77
89
  }
78
90
  declare function getOrBind<T extends object, K extends PropertyKey>(target: T, property: K, { bind }?: GetOrBindOptions): K extends keyof T ? T[K] : unknown;
79
91
 
80
- interface AsyncIdQueueCloseOptions {
81
- id?: string;
82
- reason?: unknown;
83
- }
84
- interface AsyncIdQueueOptions {
92
+ declare class Queue<T> {
93
+ private readonly items;
94
+ private readonly pendingPulls;
95
+ private closed;
96
+ /**
97
+ * Pushes an item into the queue.
98
+ * @throws when the queue is closed or aborted
99
+ */
100
+ push(item: T): void;
85
101
  /**
86
- * Maximum number of buffered items per queue.
102
+ * Pulls the next item from the queue.
87
103
  *
88
- * @default Infinity
104
+ * @throws when the queue is closed or aborted. Note that buffered items can still be pulled after close until the buffer is drained.
89
105
  */
90
- maxBufferedSize?: number;
91
- }
92
- declare class AsyncIdQueue<T> {
93
- private readonly maxBufferedSize;
94
- private readonly openIds;
95
- private readonly queues;
96
- private readonly waiters;
97
- constructor(options?: AsyncIdQueueOptions);
98
- get length(): number;
99
- get waiterIds(): string[];
100
- hasBufferedItems(id: string): boolean;
101
- open(id: string): void;
102
- isOpen(id: string): boolean;
103
- push(id: string, item: T): void;
104
- pull(id: string): Promise<T>;
105
- close({ id, reason }?: AsyncIdQueueCloseOptions): void;
106
- assertOpen(id: string): void;
106
+ pull(): Promise<T>;
107
+ /**
108
+ * Closes the queue and rejects any pending pulls.
109
+ * Buffered items remain available to be pulled. Repeated calls are ignored.
110
+ */
111
+ close(reason?: unknown): void;
112
+ /**
113
+ * Aborts the queue.
114
+ * Unlike `close()`, this also discards any buffered items before closing.
115
+ */
116
+ abort(reason?: unknown): void;
107
117
  }
108
118
 
119
+ interface SleepOptions {
120
+ signal?: AbortSignal | undefined;
121
+ }
109
122
  /**
110
123
  * sleep for a specified amount of time
111
124
  */
112
- declare function sleep(ms: number): Promise<void>;
125
+ declare function sleep(ms: number, { signal }?: SleepOptions): Promise<void>;
113
126
 
114
127
  declare function tryDecodeURIComponent(value: string): string;
115
128
 
116
- export { AbortError, AsyncIdQueue, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, SequentialIdGenerator, getOrBind, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
117
- export type { AsyncCleanupFn, AsyncIdQueueCloseOptions, AsyncIdQueueOptions, AsyncIteratorClassNextFn, GetOrBindOptions };
129
+ export { AbortError, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, Queue, SequentialIdGenerator, emitUnhandledRejection, getOrBind, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, omit, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
130
+ export type { AsyncCleanupFn, AsyncIteratorClassNextFn, GetOrBindOptions, SleepOptions };
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ function toArray(value) {
3
3
  }
4
4
 
5
5
  const PACKAGE_NAME = "@standardserver/shared";
6
- const PACKAGE_VERSION = "0.0.6";
6
+ const PACKAGE_VERSION = "0.0.8";
7
7
  function getPackageSymbol(name) {
8
8
  return Symbol.for(`${PACKAGE_NAME}@${PACKAGE_VERSION}/${name}`);
9
9
  }
@@ -14,6 +14,11 @@ class AbortError extends Error {
14
14
  this.name = "AbortError";
15
15
  }
16
16
  }
17
+ function emitUnhandledRejection(error) {
18
+ Promise.reject(error).catch(() => {
19
+ throw error;
20
+ });
21
+ }
17
22
 
18
23
  function sequential(fn) {
19
24
  let lastOperationPromise = Promise.resolve();
@@ -53,19 +58,21 @@ class AsyncIteratorClass {
53
58
  if (this.isDone) {
54
59
  return { done: true, value: void 0 };
55
60
  }
61
+ let errorRef;
56
62
  try {
57
63
  const result = await next();
58
64
  if (result.done) {
59
65
  this.isDone = true;
60
66
  }
61
67
  return result;
62
- } catch (err) {
68
+ } catch (error) {
69
+ errorRef = { value: error };
63
70
  this.isDone = true;
64
- throw err;
71
+ throw error;
65
72
  } finally {
66
73
  if (this.isDone && !this.isExecuteComplete) {
67
74
  this.isExecuteComplete = true;
68
- await this.cleanup(true);
75
+ await this.cleanup(errorRef ? { isCancelled: false, error: errorRef.value } : { isCancelled: false });
69
76
  }
70
77
  }
71
78
  });
@@ -74,17 +81,17 @@ class AsyncIteratorClass {
74
81
  this.isDone = true;
75
82
  if (!this.isExecuteComplete) {
76
83
  this.isExecuteComplete = true;
77
- await this.cleanup(false);
84
+ await this.cleanup({ isCancelled: true });
78
85
  }
79
86
  return { done: true, value };
80
87
  }
81
- async throw(err) {
88
+ async throw(error) {
82
89
  this.isDone = true;
83
90
  if (!this.isExecuteComplete) {
84
91
  this.isExecuteComplete = true;
85
- await this.cleanup(false);
92
+ await this.cleanup({ isCancelled: true, error });
86
93
  }
87
- throw err;
94
+ throw error;
88
95
  }
89
96
  /**
90
97
  * asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
@@ -93,7 +100,7 @@ class AsyncIteratorClass {
93
100
  this.isDone = true;
94
101
  if (!this.isExecuteComplete) {
95
102
  this.isExecuteComplete = true;
96
- await this.cleanup(false);
103
+ await this.cleanup({ isCancelled: true });
97
104
  }
98
105
  }
99
106
  [Symbol.asyncIterator]() {
@@ -118,6 +125,13 @@ function isTypescriptObject(maybeObject) {
118
125
  const type = typeof maybeObject;
119
126
  return type === "object" || type === "function";
120
127
  }
128
+ function omit(obj, keys) {
129
+ const result = { ...obj };
130
+ for (const key of keys) {
131
+ delete result[key];
132
+ }
133
+ return result;
134
+ }
121
135
 
122
136
  const GET_OR_BIND_CACHE = /* @__PURE__ */ new WeakMap();
123
137
  function getOrBind(target, property, { bind = true } = {}) {
@@ -136,98 +150,86 @@ function getOrBind(target, property, { bind = true } = {}) {
136
150
  return bound;
137
151
  }
138
152
 
139
- class AsyncIdQueue {
140
- maxBufferedSize;
141
- openIds = /* @__PURE__ */ new Set();
142
- queues = /* @__PURE__ */ new Map();
143
- waiters = /* @__PURE__ */ new Map();
144
- constructor(options = {}) {
145
- this.maxBufferedSize = options.maxBufferedSize ?? Infinity;
146
- }
147
- get length() {
148
- return this.openIds.size;
149
- }
150
- get waiterIds() {
151
- return Array.from(this.waiters.keys());
152
- }
153
- hasBufferedItems(id) {
154
- return Boolean(this.queues.get(id)?.length);
155
- }
156
- open(id) {
157
- this.openIds.add(id);
158
- }
159
- isOpen(id) {
160
- return this.openIds.has(id);
161
- }
162
- push(id, item) {
163
- this.assertOpen(id);
164
- const pending = this.waiters.get(id);
165
- if (pending?.length) {
166
- pending.shift()[0](item);
167
- if (pending.length === 0) {
168
- this.waiters.delete(id);
169
- }
153
+ class Queue {
154
+ items = [];
155
+ pendingPulls = [];
156
+ closed;
157
+ /**
158
+ * Pushes an item into the queue.
159
+ * @throws when the queue is closed or aborted
160
+ */
161
+ push(item) {
162
+ if (this.closed) {
163
+ throw this.closed.reason;
164
+ }
165
+ const pendingPull = this.pendingPulls.shift();
166
+ if (pendingPull) {
167
+ pendingPull[0](item);
170
168
  } else {
171
- let items = this.queues.get(id);
172
- if (!items) {
173
- items = [];
174
- this.queues.set(id, items);
175
- }
176
- items.push(item);
177
- if (items.length > this.maxBufferedSize) {
178
- items.shift();
179
- }
180
- if (items.length === 0) {
181
- this.queues.delete(id);
182
- }
169
+ this.items.push(item);
183
170
  }
184
171
  }
185
- async pull(id) {
186
- this.assertOpen(id);
187
- const items = this.queues.get(id);
188
- if (items?.length) {
189
- const item = items.shift();
190
- if (items.length === 0) {
191
- this.queues.delete(id);
192
- }
172
+ /**
173
+ * Pulls the next item from the queue.
174
+ *
175
+ * @throws when the queue is closed or aborted. Note that buffered items can still be pulled after close until the buffer is drained.
176
+ */
177
+ async pull() {
178
+ const item = this.items.shift();
179
+ if (item !== void 0) {
193
180
  return item;
194
181
  }
182
+ if (this.closed) {
183
+ throw this.closed.reason;
184
+ }
195
185
  return new Promise((resolve, reject) => {
196
- const waitingPulls = this.waiters.get(id);
197
- const pending = [resolve, reject];
198
- if (waitingPulls) {
199
- waitingPulls.push(pending);
200
- } else {
201
- this.waiters.set(id, [pending]);
202
- }
186
+ this.pendingPulls.push([resolve, reject]);
203
187
  });
204
188
  }
205
- close({ id, reason } = {}) {
206
- if (id === void 0) {
207
- this.waiters.forEach((pendingPulls, id2) => {
208
- const error2 = reason ?? new AbortError(`[AsyncIdQueue] Queue[${id2}] was closed or aborted while waiting for pulling.`);
209
- pendingPulls.forEach(([, reject]) => reject(error2));
210
- });
211
- this.waiters.clear();
212
- this.openIds.clear();
213
- this.queues.clear();
189
+ /**
190
+ * Closes the queue and rejects any pending pulls.
191
+ * Buffered items remain available to be pulled. Repeated calls are ignored.
192
+ */
193
+ close(reason) {
194
+ if (this.closed) {
214
195
  return;
215
196
  }
216
- const error = reason ?? new AbortError(`[AsyncIdQueue] Queue[${id}] was closed or aborted while waiting for pulling.`);
217
- this.waiters.get(id)?.forEach(([, reject]) => reject(error));
218
- this.waiters.delete(id);
219
- this.openIds.delete(id);
220
- this.queues.delete(id);
221
- }
222
- assertOpen(id) {
223
- if (!this.isOpen(id)) {
224
- throw new Error(`[AsyncIdQueue] Cannot access queue[${id}] because it is not open or aborted.`);
225
- }
197
+ reason ??= new AbortError("Queue was closed.");
198
+ this.closed = { reason };
199
+ this.pendingPulls.forEach(([, reject]) => reject(reason));
200
+ this.pendingPulls.length = 0;
201
+ }
202
+ /**
203
+ * Aborts the queue.
204
+ * Unlike `close()`, this also discards any buffered items before closing.
205
+ */
206
+ abort(reason) {
207
+ reason ??= new AbortError("Queue was aborted.");
208
+ this.items.length = 0;
209
+ this.close(reason);
226
210
  }
227
211
  }
228
212
 
229
- function sleep(ms) {
230
- return new Promise((resolve) => setTimeout(resolve, ms));
213
+ function sleep(ms, { signal } = {}) {
214
+ return new Promise((resolve, reject) => {
215
+ if (signal?.aborted) {
216
+ reject(signal.reason);
217
+ return;
218
+ }
219
+ let abortListener = null;
220
+ const timeout = setTimeout(() => {
221
+ if (abortListener) {
222
+ signal?.removeEventListener("abort", abortListener);
223
+ }
224
+ resolve();
225
+ }, ms);
226
+ if (signal) {
227
+ signal.addEventListener("abort", abortListener = () => {
228
+ clearTimeout(timeout);
229
+ reject(signal?.reason);
230
+ }, { once: true });
231
+ }
232
+ });
231
233
  }
232
234
 
233
235
  function tryDecodeURIComponent(value) {
@@ -238,4 +240,4 @@ function tryDecodeURIComponent(value) {
238
240
  }
239
241
  }
240
242
 
241
- export { AbortError, AsyncIdQueue, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, SequentialIdGenerator, getOrBind, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
243
+ export { AbortError, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, Queue, SequentialIdGenerator, emitUnhandledRejection, getOrBind, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, omit, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@standardserver/shared",
3
3
  "type": "module",
4
- "version": "0.0.6",
4
+ "version": "0.0.8",
5
5
  "license": "MIT",
6
6
  "homepage": "https://standardserver.dev",
7
7
  "repository": {