@standardserver/shared 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Standard Server
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,132 @@
1
+ declare function toArray<T>(value: T): T extends readonly any[] ? T : Exclude<T, undefined | null>[];
2
+
3
+ declare const PACKAGE_NAME = "@standardserver/shared";
4
+ declare const PACKAGE_VERSION = "0.0.0";
5
+ /**
6
+ * Generates a unique symbol for the specified name within the package scope.
7
+ * The symbol is globally registered using `Symbol.for` to ensure consistency across modules.
8
+ *
9
+ * @remarks
10
+ * - Ensure names are unique to avoid symbol collisions within the package.
11
+ * - Ensure package versions are synchronized (e.g., when the core package is updated, this package should also be updated).
12
+ */
13
+ declare function getPackageSymbol(name: string): symbol;
14
+
15
+ /**
16
+ * Error thrown when an operation is aborted.
17
+ * Uses the standardized 'AbortError' name for consistency with JavaScript APIs.
18
+ */
19
+ declare class AbortError extends Error {
20
+ constructor(...rest: ConstructorParameters<typeof Error>);
21
+ }
22
+
23
+ /**
24
+ * Sequentially execute the function, if the previous execution is not completed, the next execution will be blocked
25
+ */
26
+ declare function sequential<A extends any[], R>(fn: (...args: A) => Promise<R>): (...args: A) => Promise<R>;
27
+
28
+ declare class SequentialIdGenerator {
29
+ private index;
30
+ generate(): string;
31
+ }
32
+
33
+ interface AsyncCleanupFn {
34
+ (isCompleted: boolean): Promise<void>;
35
+ }
36
+
37
+ declare function isAsyncIteratorObject(maybe: unknown): maybe is AsyncIteratorObject<any, any, any>;
38
+ interface AsyncIteratorClassNextFn<T, TReturn> {
39
+ (): Promise<IteratorResult<T, TReturn>>;
40
+ }
41
+ declare const fallbackAsyncDisposeSymbol: unique symbol;
42
+ declare const asyncDisposeSymbol: typeof Symbol extends {
43
+ asyncDispose: infer T;
44
+ } ? T : typeof fallbackAsyncDisposeSymbol;
45
+ declare class AsyncIteratorClass<T, TReturn = unknown, TNext = unknown> implements AsyncIteratorObject<T, TReturn, TNext>, AsyncGenerator<T, TReturn, TNext> {
46
+ private isDone;
47
+ private isExecuteComplete;
48
+ private readonly cleanup;
49
+ readonly next: AsyncIteratorClassNextFn<T, TReturn>;
50
+ constructor(next: AsyncIteratorClassNextFn<T, TReturn>, cleanup: AsyncCleanupFn);
51
+ return(value?: any): Promise<IteratorResult<T, TReturn>>;
52
+ throw(err: any): Promise<IteratorResult<T, TReturn>>;
53
+ /**
54
+ * asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
55
+ */
56
+ [asyncDisposeSymbol](): Promise<void>;
57
+ [Symbol.asyncIterator](): this;
58
+ }
59
+
60
+ declare function parseEmptyableJSON(text: string | null | undefined): unknown;
61
+ declare function stringifyJSON<T>(value: T | {
62
+ toJSON(): T;
63
+ }): undefined extends T ? undefined | string : string;
64
+
65
+ /**
66
+ * Checks whether the provided container is a typescript object (object or function).
67
+ */
68
+ declare function isTypescriptObject(maybeObject: unknown): maybeObject is object;
69
+
70
+ /**
71
+ * A ProxyHandler whose `get` trap receives an additional `fallback` function.
72
+ *
73
+ * The `fallback` returns the default property value as if no proxy
74
+ * interception occurred, and automatically binds methods to the target.
75
+ */
76
+ interface EnhancedProxyHandler<T extends object> extends Omit<ProxyHandler<T>, 'get'> {
77
+ get(target: T, p: PropertyKey, receiver: any, fallback: () => any): any;
78
+ }
79
+ /**
80
+ * Creates a Proxy that enhances the standard `get` trap by providing:
81
+ *
82
+ * - A `fallback` function that:
83
+ * - Reads the property directly from the target
84
+ * - Automatically binds methods to the target
85
+ * - Caches bound methods for stable identity
86
+ * - An internal symbol that allows accessing the original target
87
+ */
88
+ declare function createEnhancedProxy<T extends object>(target: T, handler: EnhancedProxyHandler<T>): T;
89
+ /**
90
+ * Returns the underlying target of an enhanced proxy.
91
+ * If the given value is not an enhanced proxy, it is returned as-is.
92
+ */
93
+ declare function getEnhancedProxyTarget<T extends object>(value: T): T;
94
+
95
+ interface AsyncIdQueueCloseOptions {
96
+ id?: string;
97
+ reason?: unknown;
98
+ }
99
+ interface AsyncIdQueueOptions {
100
+ /**
101
+ * Maximum number of buffered items per queue.
102
+ *
103
+ * @default Infinity
104
+ */
105
+ maxBufferedSize?: number;
106
+ }
107
+ declare class AsyncIdQueue<T> {
108
+ private readonly maxBufferedSize;
109
+ private readonly openIds;
110
+ private readonly queues;
111
+ private readonly waiters;
112
+ constructor(options?: AsyncIdQueueOptions);
113
+ get length(): number;
114
+ get waiterIds(): string[];
115
+ hasBufferedItems(id: string): boolean;
116
+ open(id: string): void;
117
+ isOpen(id: string): boolean;
118
+ push(id: string, item: T): void;
119
+ pull(id: string): Promise<T>;
120
+ close({ id, reason }?: AsyncIdQueueCloseOptions): void;
121
+ assertOpen(id: string): void;
122
+ }
123
+
124
+ /**
125
+ * sleep for a specified amount of time
126
+ */
127
+ declare function sleep(ms: number): Promise<void>;
128
+
129
+ declare function tryDecodeURIComponent(value: string): string;
130
+
131
+ export { AbortError, AsyncIdQueue, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, SequentialIdGenerator, createEnhancedProxy, getEnhancedProxyTarget, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
132
+ export type { AsyncCleanupFn, AsyncIdQueueCloseOptions, AsyncIdQueueOptions, AsyncIteratorClassNextFn, EnhancedProxyHandler };
@@ -0,0 +1,132 @@
1
+ declare function toArray<T>(value: T): T extends readonly any[] ? T : Exclude<T, undefined | null>[];
2
+
3
+ declare const PACKAGE_NAME = "@standardserver/shared";
4
+ declare const PACKAGE_VERSION = "0.0.0";
5
+ /**
6
+ * Generates a unique symbol for the specified name within the package scope.
7
+ * The symbol is globally registered using `Symbol.for` to ensure consistency across modules.
8
+ *
9
+ * @remarks
10
+ * - Ensure names are unique to avoid symbol collisions within the package.
11
+ * - Ensure package versions are synchronized (e.g., when the core package is updated, this package should also be updated).
12
+ */
13
+ declare function getPackageSymbol(name: string): symbol;
14
+
15
+ /**
16
+ * Error thrown when an operation is aborted.
17
+ * Uses the standardized 'AbortError' name for consistency with JavaScript APIs.
18
+ */
19
+ declare class AbortError extends Error {
20
+ constructor(...rest: ConstructorParameters<typeof Error>);
21
+ }
22
+
23
+ /**
24
+ * Sequentially execute the function, if the previous execution is not completed, the next execution will be blocked
25
+ */
26
+ declare function sequential<A extends any[], R>(fn: (...args: A) => Promise<R>): (...args: A) => Promise<R>;
27
+
28
+ declare class SequentialIdGenerator {
29
+ private index;
30
+ generate(): string;
31
+ }
32
+
33
+ interface AsyncCleanupFn {
34
+ (isCompleted: boolean): Promise<void>;
35
+ }
36
+
37
+ declare function isAsyncIteratorObject(maybe: unknown): maybe is AsyncIteratorObject<any, any, any>;
38
+ interface AsyncIteratorClassNextFn<T, TReturn> {
39
+ (): Promise<IteratorResult<T, TReturn>>;
40
+ }
41
+ declare const fallbackAsyncDisposeSymbol: unique symbol;
42
+ declare const asyncDisposeSymbol: typeof Symbol extends {
43
+ asyncDispose: infer T;
44
+ } ? T : typeof fallbackAsyncDisposeSymbol;
45
+ declare class AsyncIteratorClass<T, TReturn = unknown, TNext = unknown> implements AsyncIteratorObject<T, TReturn, TNext>, AsyncGenerator<T, TReturn, TNext> {
46
+ private isDone;
47
+ private isExecuteComplete;
48
+ private readonly cleanup;
49
+ readonly next: AsyncIteratorClassNextFn<T, TReturn>;
50
+ constructor(next: AsyncIteratorClassNextFn<T, TReturn>, cleanup: AsyncCleanupFn);
51
+ return(value?: any): Promise<IteratorResult<T, TReturn>>;
52
+ throw(err: any): Promise<IteratorResult<T, TReturn>>;
53
+ /**
54
+ * asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
55
+ */
56
+ [asyncDisposeSymbol](): Promise<void>;
57
+ [Symbol.asyncIterator](): this;
58
+ }
59
+
60
+ declare function parseEmptyableJSON(text: string | null | undefined): unknown;
61
+ declare function stringifyJSON<T>(value: T | {
62
+ toJSON(): T;
63
+ }): undefined extends T ? undefined | string : string;
64
+
65
+ /**
66
+ * Checks whether the provided container is a typescript object (object or function).
67
+ */
68
+ declare function isTypescriptObject(maybeObject: unknown): maybeObject is object;
69
+
70
+ /**
71
+ * A ProxyHandler whose `get` trap receives an additional `fallback` function.
72
+ *
73
+ * The `fallback` returns the default property value as if no proxy
74
+ * interception occurred, and automatically binds methods to the target.
75
+ */
76
+ interface EnhancedProxyHandler<T extends object> extends Omit<ProxyHandler<T>, 'get'> {
77
+ get(target: T, p: PropertyKey, receiver: any, fallback: () => any): any;
78
+ }
79
+ /**
80
+ * Creates a Proxy that enhances the standard `get` trap by providing:
81
+ *
82
+ * - A `fallback` function that:
83
+ * - Reads the property directly from the target
84
+ * - Automatically binds methods to the target
85
+ * - Caches bound methods for stable identity
86
+ * - An internal symbol that allows accessing the original target
87
+ */
88
+ declare function createEnhancedProxy<T extends object>(target: T, handler: EnhancedProxyHandler<T>): T;
89
+ /**
90
+ * Returns the underlying target of an enhanced proxy.
91
+ * If the given value is not an enhanced proxy, it is returned as-is.
92
+ */
93
+ declare function getEnhancedProxyTarget<T extends object>(value: T): T;
94
+
95
+ interface AsyncIdQueueCloseOptions {
96
+ id?: string;
97
+ reason?: unknown;
98
+ }
99
+ interface AsyncIdQueueOptions {
100
+ /**
101
+ * Maximum number of buffered items per queue.
102
+ *
103
+ * @default Infinity
104
+ */
105
+ maxBufferedSize?: number;
106
+ }
107
+ declare class AsyncIdQueue<T> {
108
+ private readonly maxBufferedSize;
109
+ private readonly openIds;
110
+ private readonly queues;
111
+ private readonly waiters;
112
+ constructor(options?: AsyncIdQueueOptions);
113
+ get length(): number;
114
+ get waiterIds(): string[];
115
+ hasBufferedItems(id: string): boolean;
116
+ open(id: string): void;
117
+ isOpen(id: string): boolean;
118
+ push(id: string, item: T): void;
119
+ pull(id: string): Promise<T>;
120
+ close({ id, reason }?: AsyncIdQueueCloseOptions): void;
121
+ assertOpen(id: string): void;
122
+ }
123
+
124
+ /**
125
+ * sleep for a specified amount of time
126
+ */
127
+ declare function sleep(ms: number): Promise<void>;
128
+
129
+ declare function tryDecodeURIComponent(value: string): string;
130
+
131
+ export { AbortError, AsyncIdQueue, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, SequentialIdGenerator, createEnhancedProxy, getEnhancedProxyTarget, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
132
+ export type { AsyncCleanupFn, AsyncIdQueueCloseOptions, AsyncIdQueueOptions, AsyncIteratorClassNextFn, EnhancedProxyHandler };
package/dist/index.mjs ADDED
@@ -0,0 +1,253 @@
1
+ function toArray(value) {
2
+ return Array.isArray(value) ? value : value === void 0 || value === null ? [] : [value];
3
+ }
4
+
5
+ const PACKAGE_NAME = "@standardserver/shared";
6
+ const PACKAGE_VERSION = "0.0.0";
7
+ function getPackageSymbol(name) {
8
+ return Symbol.for(`${PACKAGE_NAME}@${PACKAGE_VERSION}/${name}`);
9
+ }
10
+
11
+ class AbortError extends Error {
12
+ constructor(...rest) {
13
+ super(...rest);
14
+ this.name = "AbortError";
15
+ }
16
+ }
17
+
18
+ function sequential(fn) {
19
+ let lastOperationPromise = Promise.resolve();
20
+ return (...args) => {
21
+ return lastOperationPromise = lastOperationPromise.catch(() => {
22
+ }).then(() => {
23
+ return fn(...args);
24
+ });
25
+ };
26
+ }
27
+
28
+ class SequentialIdGenerator {
29
+ index = BigInt(1);
30
+ generate() {
31
+ const id = this.index.toString(36);
32
+ this.index++;
33
+ return id;
34
+ }
35
+ }
36
+
37
+ function isAsyncIteratorObject(maybe) {
38
+ if (!maybe || typeof maybe !== "object") {
39
+ return false;
40
+ }
41
+ return "next" in maybe && typeof maybe.next === "function" && Symbol.asyncIterator in maybe && typeof maybe[Symbol.asyncIterator] === "function";
42
+ }
43
+ const fallbackAsyncDisposeSymbol = Symbol.for("asyncDispose");
44
+ const asyncDisposeSymbol = Symbol.asyncDispose ?? fallbackAsyncDisposeSymbol;
45
+ class AsyncIteratorClass {
46
+ isDone = false;
47
+ isExecuteComplete = false;
48
+ cleanup;
49
+ next;
50
+ constructor(next, cleanup) {
51
+ this.cleanup = cleanup;
52
+ this.next = sequential(async () => {
53
+ if (this.isDone) {
54
+ return { done: true, value: void 0 };
55
+ }
56
+ try {
57
+ const result = await next();
58
+ if (result.done) {
59
+ this.isDone = true;
60
+ }
61
+ return result;
62
+ } catch (err) {
63
+ this.isDone = true;
64
+ throw err;
65
+ } finally {
66
+ if (this.isDone && !this.isExecuteComplete) {
67
+ this.isExecuteComplete = true;
68
+ await this.cleanup(true);
69
+ }
70
+ }
71
+ });
72
+ }
73
+ async return(value) {
74
+ this.isDone = true;
75
+ if (!this.isExecuteComplete) {
76
+ this.isExecuteComplete = true;
77
+ await this.cleanup(false);
78
+ }
79
+ return { done: true, value };
80
+ }
81
+ async throw(err) {
82
+ this.isDone = true;
83
+ if (!this.isExecuteComplete) {
84
+ this.isExecuteComplete = true;
85
+ await this.cleanup(false);
86
+ }
87
+ throw err;
88
+ }
89
+ /**
90
+ * asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
91
+ */
92
+ async [asyncDisposeSymbol]() {
93
+ this.isDone = true;
94
+ if (!this.isExecuteComplete) {
95
+ this.isExecuteComplete = true;
96
+ await this.cleanup(false);
97
+ }
98
+ }
99
+ [Symbol.asyncIterator]() {
100
+ return this;
101
+ }
102
+ }
103
+
104
+ function parseEmptyableJSON(text) {
105
+ if (!text) {
106
+ return void 0;
107
+ }
108
+ return JSON.parse(text);
109
+ }
110
+ function stringifyJSON(value) {
111
+ return JSON.stringify(value);
112
+ }
113
+
114
+ function isTypescriptObject(maybeObject) {
115
+ if (!maybeObject) {
116
+ return false;
117
+ }
118
+ const type = typeof maybeObject;
119
+ return type === "object" || type === "function";
120
+ }
121
+
122
+ const ENHANCED_PROXY_TARGET_SYMBOL = getPackageSymbol("ENHANCED_PROXY_TARGET");
123
+ function createEnhancedProxy(target, handler) {
124
+ const boundMethodCache = /* @__PURE__ */ new WeakMap();
125
+ return new Proxy(target, {
126
+ ...handler,
127
+ get(target2, p, receiver) {
128
+ if (p === ENHANCED_PROXY_TARGET_SYMBOL) {
129
+ return target2;
130
+ }
131
+ return handler.get(target2, p, receiver, () => {
132
+ const value = Reflect.get(target2, p);
133
+ if (typeof value !== "function") {
134
+ return value;
135
+ }
136
+ const cached = boundMethodCache.get(value);
137
+ if (cached) {
138
+ return cached;
139
+ }
140
+ const bound = value.bind(target2);
141
+ boundMethodCache.set(value, bound);
142
+ return bound;
143
+ });
144
+ }
145
+ });
146
+ }
147
+ function getEnhancedProxyTarget(value) {
148
+ return value[ENHANCED_PROXY_TARGET_SYMBOL] ?? value;
149
+ }
150
+
151
+ class AsyncIdQueue {
152
+ maxBufferedSize;
153
+ openIds = /* @__PURE__ */ new Set();
154
+ queues = /* @__PURE__ */ new Map();
155
+ waiters = /* @__PURE__ */ new Map();
156
+ constructor(options = {}) {
157
+ this.maxBufferedSize = options.maxBufferedSize ?? Infinity;
158
+ }
159
+ get length() {
160
+ return this.openIds.size;
161
+ }
162
+ get waiterIds() {
163
+ return Array.from(this.waiters.keys());
164
+ }
165
+ hasBufferedItems(id) {
166
+ return Boolean(this.queues.get(id)?.length);
167
+ }
168
+ open(id) {
169
+ this.openIds.add(id);
170
+ }
171
+ isOpen(id) {
172
+ return this.openIds.has(id);
173
+ }
174
+ push(id, item) {
175
+ this.assertOpen(id);
176
+ const pending = this.waiters.get(id);
177
+ if (pending?.length) {
178
+ pending.shift()[0](item);
179
+ if (pending.length === 0) {
180
+ this.waiters.delete(id);
181
+ }
182
+ } else {
183
+ let items = this.queues.get(id);
184
+ if (!items) {
185
+ items = [];
186
+ this.queues.set(id, items);
187
+ }
188
+ items.push(item);
189
+ if (items.length > this.maxBufferedSize) {
190
+ items.shift();
191
+ }
192
+ if (items.length === 0) {
193
+ this.queues.delete(id);
194
+ }
195
+ }
196
+ }
197
+ async pull(id) {
198
+ this.assertOpen(id);
199
+ const items = this.queues.get(id);
200
+ if (items?.length) {
201
+ const item = items.shift();
202
+ if (items.length === 0) {
203
+ this.queues.delete(id);
204
+ }
205
+ return item;
206
+ }
207
+ return new Promise((resolve, reject) => {
208
+ const waitingPulls = this.waiters.get(id);
209
+ const pending = [resolve, reject];
210
+ if (waitingPulls) {
211
+ waitingPulls.push(pending);
212
+ } else {
213
+ this.waiters.set(id, [pending]);
214
+ }
215
+ });
216
+ }
217
+ close({ id, reason } = {}) {
218
+ if (id === void 0) {
219
+ this.waiters.forEach((pendingPulls, id2) => {
220
+ const error2 = reason ?? new AbortError(`[AsyncIdQueue] Queue[${id2}] was closed or aborted while waiting for pulling.`);
221
+ pendingPulls.forEach(([, reject]) => reject(error2));
222
+ });
223
+ this.waiters.clear();
224
+ this.openIds.clear();
225
+ this.queues.clear();
226
+ return;
227
+ }
228
+ const error = reason ?? new AbortError(`[AsyncIdQueue] Queue[${id}] was closed or aborted while waiting for pulling.`);
229
+ this.waiters.get(id)?.forEach(([, reject]) => reject(error));
230
+ this.waiters.delete(id);
231
+ this.openIds.delete(id);
232
+ this.queues.delete(id);
233
+ }
234
+ assertOpen(id) {
235
+ if (!this.isOpen(id)) {
236
+ throw new Error(`[AsyncIdQueue] Cannot access queue[${id}] because it is not open or aborted.`);
237
+ }
238
+ }
239
+ }
240
+
241
+ function sleep(ms) {
242
+ return new Promise((resolve) => setTimeout(resolve, ms));
243
+ }
244
+
245
+ function tryDecodeURIComponent(value) {
246
+ try {
247
+ return decodeURIComponent(value);
248
+ } catch {
249
+ return value;
250
+ }
251
+ }
252
+
253
+ export { AbortError, AsyncIdQueue, AsyncIteratorClass, PACKAGE_NAME, PACKAGE_VERSION, SequentialIdGenerator, createEnhancedProxy, getEnhancedProxyTarget, getPackageSymbol, isAsyncIteratorObject, isTypescriptObject, parseEmptyableJSON, sequential, sleep, stringifyJSON, toArray, tryDecodeURIComponent };
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@standardserver/shared",
3
+ "type": "module",
4
+ "version": "0.0.0",
5
+ "license": "MIT",
6
+ "homepage": "https://standardserver.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/standardserver/standardserver.git",
10
+ "directory": "packages/shared"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.mts",
15
+ "import": "./dist/index.mjs",
16
+ "default": "./dist/index.mjs"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "scripts": {
23
+ "build": "unbuild",
24
+ "type:check": "tsc -b"
25
+ }
26
+ }