@develia/commons 0.4.12 → 0.4.14
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/cjs/array.js +4 -5
- package/dist/cjs/array.js.map +1 -1
- package/dist/cjs/async-pool-executor.js +0 -0
- package/dist/cjs/async-pool-executor.js.map +0 -0
- package/dist/cjs/browser/form-utilities.js +12 -12
- package/dist/cjs/browser/form-utilities.js.map +1 -1
- package/dist/cjs/browser/geo-location.js +1 -1
- package/dist/cjs/browser/geo-location.js.map +1 -1
- package/dist/cjs/browser/index.js +30 -2
- package/dist/cjs/browser/index.js.map +1 -1
- package/dist/cjs/browser/native-bridge.js +0 -0
- package/dist/cjs/browser/native-bridge.js.map +0 -0
- package/dist/cjs/cancellable-promise.js +0 -0
- package/dist/cjs/cancellable-promise.js.map +0 -0
- package/dist/cjs/clean-url-search-params.js +0 -0
- package/dist/cjs/clean-url-search-params.js.map +0 -0
- package/dist/cjs/collections/cache-dictionary.js +0 -0
- package/dist/cjs/collections/cache-dictionary.js.map +0 -0
- package/dist/cjs/collections/default-map.js +0 -0
- package/dist/cjs/collections/default-map.js.map +0 -0
- package/dist/cjs/collections/default-weak-map.js +0 -0
- package/dist/cjs/collections/default-weak-map.js.map +0 -0
- package/dist/cjs/collections/index.js +0 -0
- package/dist/cjs/collections/index.js.map +0 -0
- package/dist/cjs/draft.js +153 -0
- package/dist/cjs/draft.js.map +1 -0
- package/dist/cjs/event-bus.js +0 -0
- package/dist/cjs/event-bus.js.map +0 -0
- package/dist/cjs/from.js +1 -1
- package/dist/cjs/from.js.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/key-value-pair.js +0 -0
- package/dist/cjs/key-value-pair.js.map +0 -0
- package/dist/cjs/lazy.js +0 -0
- package/dist/cjs/lazy.js.map +0 -0
- package/dist/cjs/math.js +2 -3
- package/dist/cjs/math.js.map +1 -1
- package/dist/cjs/order.js +0 -0
- package/dist/cjs/order.js.map +0 -0
- package/dist/cjs/parse.js +3 -4
- package/dist/cjs/parse.js.map +1 -1
- package/dist/cjs/patch.js +2 -3
- package/dist/cjs/patch.js.map +1 -1
- package/dist/cjs/stopwatch.js +0 -0
- package/dist/cjs/stopwatch.js.map +0 -0
- package/dist/cjs/string.js +8 -9
- package/dist/cjs/string.js.map +1 -1
- package/dist/cjs/timer.js +0 -0
- package/dist/cjs/timer.js.map +0 -0
- package/dist/cjs/timespan.js +0 -0
- package/dist/cjs/timespan.js.map +0 -0
- package/dist/cjs/type-code.js +0 -0
- package/dist/cjs/type-code.js.map +0 -0
- package/dist/cjs/type.js +0 -0
- package/dist/cjs/type.js.map +0 -0
- package/dist/cjs/types.js +0 -0
- package/dist/cjs/types.js.map +0 -0
- package/dist/cjs/utilities.js +106 -57
- package/dist/cjs/utilities.js.map +1 -1
- package/dist/esm/array.js +0 -0
- package/dist/esm/array.js.map +0 -0
- package/dist/esm/async-pool-executor.js +0 -0
- package/dist/esm/async-pool-executor.js.map +0 -0
- package/dist/esm/browser/form-utilities.js +7 -6
- package/dist/esm/browser/form-utilities.js.map +1 -1
- package/dist/esm/browser/geo-location.js +0 -0
- package/dist/esm/browser/geo-location.js.map +0 -0
- package/dist/esm/browser/index.js +27 -0
- package/dist/esm/browser/index.js.map +1 -1
- package/dist/esm/browser/native-bridge.js +0 -0
- package/dist/esm/browser/native-bridge.js.map +0 -0
- package/dist/esm/cancellable-promise.js +0 -0
- package/dist/esm/cancellable-promise.js.map +0 -0
- package/dist/esm/clean-url-search-params.js +0 -0
- package/dist/esm/clean-url-search-params.js.map +0 -0
- package/dist/esm/collections/cache-dictionary.js +0 -0
- package/dist/esm/collections/cache-dictionary.js.map +0 -0
- package/dist/esm/collections/default-map.js +0 -0
- package/dist/esm/collections/default-map.js.map +0 -0
- package/dist/esm/collections/default-weak-map.js +0 -0
- package/dist/esm/collections/default-weak-map.js.map +0 -0
- package/dist/esm/collections/index.js +0 -0
- package/dist/esm/collections/index.js.map +0 -0
- package/dist/esm/draft.js +150 -0
- package/dist/esm/draft.js.map +1 -0
- package/dist/esm/event-bus.js +0 -0
- package/dist/esm/event-bus.js.map +0 -0
- package/dist/esm/from.js +0 -0
- package/dist/esm/from.js.map +0 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/key-value-pair.js +0 -0
- package/dist/esm/key-value-pair.js.map +0 -0
- package/dist/esm/lazy.js +0 -0
- package/dist/esm/lazy.js.map +0 -0
- package/dist/esm/math.js +0 -0
- package/dist/esm/math.js.map +0 -0
- package/dist/esm/order.js +0 -0
- package/dist/esm/order.js.map +0 -0
- package/dist/esm/parse.js +0 -0
- package/dist/esm/parse.js.map +0 -0
- package/dist/esm/patch.js +0 -0
- package/dist/esm/patch.js.map +0 -0
- package/dist/esm/stopwatch.js +0 -0
- package/dist/esm/stopwatch.js.map +0 -0
- package/dist/esm/string.js +0 -0
- package/dist/esm/string.js.map +0 -0
- package/dist/esm/timer.js +0 -0
- package/dist/esm/timer.js.map +0 -0
- package/dist/esm/timespan.js +0 -0
- package/dist/esm/timespan.js.map +0 -0
- package/dist/esm/type-code.js +0 -0
- package/dist/esm/type-code.js.map +0 -0
- package/dist/esm/type.js +0 -0
- package/dist/esm/type.js.map +0 -0
- package/dist/esm/types.js +0 -0
- package/dist/esm/types.js.map +0 -0
- package/dist/esm/utilities.js +81 -32
- package/dist/esm/utilities.js.map +1 -1
- package/dist/types/array.d.ts +0 -0
- package/dist/types/array.d.ts.map +0 -0
- package/dist/types/async-pool-executor.d.ts +0 -0
- package/dist/types/async-pool-executor.d.ts.map +0 -0
- package/dist/types/browser/form-utilities.d.ts +0 -0
- package/dist/types/browser/form-utilities.d.ts.map +1 -1
- package/dist/types/browser/geo-location.d.ts +0 -0
- package/dist/types/browser/geo-location.d.ts.map +0 -0
- package/dist/types/browser/index.d.ts +3 -0
- package/dist/types/browser/index.d.ts.map +1 -1
- package/dist/types/browser/native-bridge.d.ts +0 -0
- package/dist/types/browser/native-bridge.d.ts.map +0 -0
- package/dist/types/cancellable-promise.d.ts +0 -0
- package/dist/types/cancellable-promise.d.ts.map +0 -0
- package/dist/types/clean-url-search-params.d.ts +0 -0
- package/dist/types/clean-url-search-params.d.ts.map +0 -0
- package/dist/types/collections/cache-dictionary.d.ts +0 -0
- package/dist/types/collections/cache-dictionary.d.ts.map +0 -0
- package/dist/types/collections/default-map.d.ts +0 -0
- package/dist/types/collections/default-map.d.ts.map +0 -0
- package/dist/types/collections/default-weak-map.d.ts +0 -0
- package/dist/types/collections/default-weak-map.d.ts.map +0 -0
- package/dist/types/collections/index.d.ts +0 -0
- package/dist/types/collections/index.d.ts.map +0 -0
- package/dist/types/draft.d.ts +17 -0
- package/dist/types/draft.d.ts.map +1 -0
- package/dist/types/event-bus.d.ts +0 -0
- package/dist/types/event-bus.d.ts.map +0 -0
- package/dist/types/from.d.ts +0 -0
- package/dist/types/from.d.ts.map +0 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key-value-pair.d.ts +0 -0
- package/dist/types/key-value-pair.d.ts.map +0 -0
- package/dist/types/lazy.d.ts +0 -0
- package/dist/types/lazy.d.ts.map +0 -0
- package/dist/types/math.d.ts +0 -0
- package/dist/types/math.d.ts.map +0 -0
- package/dist/types/order.d.ts +0 -0
- package/dist/types/order.d.ts.map +0 -0
- package/dist/types/parse.d.ts +0 -0
- package/dist/types/parse.d.ts.map +0 -0
- package/dist/types/patch.d.ts +0 -0
- package/dist/types/patch.d.ts.map +0 -0
- package/dist/types/stopwatch.d.ts +0 -0
- package/dist/types/stopwatch.d.ts.map +0 -0
- package/dist/types/string.d.ts +0 -0
- package/dist/types/string.d.ts.map +0 -0
- package/dist/types/timer.d.ts +0 -0
- package/dist/types/timer.d.ts.map +0 -0
- package/dist/types/timespan.d.ts +0 -0
- package/dist/types/timespan.d.ts.map +0 -0
- package/dist/types/type-code.d.ts +0 -0
- package/dist/types/type-code.d.ts.map +0 -0
- package/dist/types/type.d.ts +0 -0
- package/dist/types/type.d.ts.map +0 -0
- package/dist/types/types.d.ts +0 -0
- package/dist/types/types.d.ts.map +0 -0
- package/dist/types/utilities.d.ts +3 -2
- package/dist/types/utilities.d.ts.map +1 -1
- package/dist/web/array.js +0 -0
- package/dist/web/array.js.map +0 -0
- package/dist/web/async-pool-executor.js +0 -0
- package/dist/web/async-pool-executor.js.map +0 -0
- package/dist/web/browser/form-utilities.js +7 -6
- package/dist/web/browser/form-utilities.js.map +1 -1
- package/dist/web/browser/geo-location.js +0 -0
- package/dist/web/browser/geo-location.js.map +0 -0
- package/dist/web/browser/index.js +27 -0
- package/dist/web/browser/index.js.map +1 -1
- package/dist/web/browser/native-bridge.js +0 -0
- package/dist/web/browser/native-bridge.js.map +0 -0
- package/dist/web/cancellable-promise.js +0 -0
- package/dist/web/cancellable-promise.js.map +0 -0
- package/dist/web/clean-url-search-params.js +0 -0
- package/dist/web/clean-url-search-params.js.map +0 -0
- package/dist/web/collections/cache-dictionary.js +0 -0
- package/dist/web/collections/cache-dictionary.js.map +0 -0
- package/dist/web/collections/default-map.js +0 -0
- package/dist/web/collections/default-map.js.map +0 -0
- package/dist/web/collections/default-weak-map.js +0 -0
- package/dist/web/collections/default-weak-map.js.map +0 -0
- package/dist/web/collections/index.js +0 -0
- package/dist/web/collections/index.js.map +0 -0
- package/dist/web/draft.js +150 -0
- package/dist/web/draft.js.map +1 -0
- package/dist/web/event-bus.js +0 -0
- package/dist/web/event-bus.js.map +0 -0
- package/dist/web/from.js +0 -0
- package/dist/web/from.js.map +0 -0
- package/dist/web/index.js +1 -0
- package/dist/web/index.js.map +1 -1
- package/dist/web/key-value-pair.js +0 -0
- package/dist/web/key-value-pair.js.map +0 -0
- package/dist/web/lazy.js +0 -0
- package/dist/web/lazy.js.map +0 -0
- package/dist/web/math.js +0 -0
- package/dist/web/math.js.map +0 -0
- package/dist/web/order.js +0 -0
- package/dist/web/order.js.map +0 -0
- package/dist/web/parse.js +0 -0
- package/dist/web/parse.js.map +0 -0
- package/dist/web/patch.js +0 -0
- package/dist/web/patch.js.map +0 -0
- package/dist/web/stopwatch.js +0 -0
- package/dist/web/stopwatch.js.map +0 -0
- package/dist/web/string.js +0 -0
- package/dist/web/string.js.map +0 -0
- package/dist/web/timer.js +0 -0
- package/dist/web/timer.js.map +0 -0
- package/dist/web/timespan.js +0 -0
- package/dist/web/timespan.js.map +0 -0
- package/dist/web/type-code.js +0 -0
- package/dist/web/type-code.js.map +0 -0
- package/dist/web/type.js +0 -0
- package/dist/web/type.js.map +0 -0
- package/dist/web/types.js +0 -0
- package/dist/web/types.js.map +0 -0
- package/dist/web/utilities.js +81 -32
- package/dist/web/utilities.js.map +1 -1
- package/package.json +60 -56
- package/src/browser/form-utilities.ts +7 -6
- package/src/browser/geo-location.ts +113 -113
- package/src/browser/index.ts +37 -0
- package/src/browser/native-bridge.ts +116 -116
- package/src/cancellable-promise.ts +76 -76
- package/src/collections/index.ts +2 -2
- package/src/draft.ts +268 -0
- package/src/event-bus.ts +83 -83
- package/src/index.ts +42 -37
- package/src/parse.ts +240 -240
- package/src/string.ts +128 -128
- package/src/type-code.ts +15 -15
- package/src/type.ts +371 -371
- package/src/types.ts +30 -30
- package/src/utilities.ts +480 -404
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
// ─── Tipos ───────────────────────────────────────────────
|
|
2
|
-
interface BridgeOptions {
|
|
3
|
-
channel: Channel;
|
|
4
|
-
timeout?: number | null;
|
|
5
|
-
callbackName?: string; // ← nuevo
|
|
6
|
-
}
|
|
7
|
-
interface PendingCall {
|
|
8
|
-
resolve: (value: unknown) => void;
|
|
9
|
-
reject: (reason: Error) => void;
|
|
10
|
-
timer: ReturnType<typeof setTimeout> | null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface Channel {
|
|
14
|
-
postMessage: (message: string) => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// ─── Clase ──────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
export default class NativeBridge {
|
|
22
|
-
private _pending = new Map<number, PendingCall>();
|
|
23
|
-
private _callId = 0;
|
|
24
|
-
private readonly _defaultTimeout: number | null;
|
|
25
|
-
private readonly _channel: Channel;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
constructor({ channel, timeout = 3000, callbackName = '__nativeResponseHandler' }: BridgeOptions) {
|
|
29
|
-
this._defaultTimeout = timeout;
|
|
30
|
-
this._channel = channel;
|
|
31
|
-
|
|
32
|
-
if (!this._channel) {
|
|
33
|
-
throw new Error('Channel not found');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
(window as any)[callbackName] = this._callback;
|
|
37
|
-
|
|
38
|
-
// Proxy: cualquier propiedad no existente se convierte en call()
|
|
39
|
-
return new Proxy(this, {
|
|
40
|
-
get(target: NativeBridge, prop: string | symbol) {
|
|
41
|
-
if (prop in target) {
|
|
42
|
-
return (target as any)[prop];
|
|
43
|
-
}
|
|
44
|
-
return (data?: unknown, timeout?: number | null) => target.call(String(prop), data, timeout);
|
|
45
|
-
},
|
|
46
|
-
}) as unknown as NativeBridge;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
private _callback(id: number, error: any, result: any) {
|
|
51
|
-
|
|
52
|
-
const pending = this._pending.get(id);
|
|
53
|
-
if (!pending) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (pending.timer !== null) {
|
|
58
|
-
clearTimeout(pending.timer);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this._pending.delete(id);
|
|
62
|
-
|
|
63
|
-
if (error) {
|
|
64
|
-
pending.reject(error instanceof Error? error : new Error(String(error)));
|
|
65
|
-
} else {
|
|
66
|
-
pending.resolve(result);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
call(method: string, data: unknown = null, timeout?: number | null): Promise<unknown> {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const effectiveTimeout = timeout !== undefined ? timeout : this._defaultTimeout;
|
|
74
|
-
|
|
75
|
-
return new Promise<unknown>((resolve, reject) => {
|
|
76
|
-
const id = this._callId++;
|
|
77
|
-
|
|
78
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
79
|
-
|
|
80
|
-
// Solo crear timer si hay timeout
|
|
81
|
-
if (effectiveTimeout !== null && effectiveTimeout > 0) {
|
|
82
|
-
timer = setTimeout(() => {
|
|
83
|
-
this._pending.delete(id);
|
|
84
|
-
reject(new Error(`Timeout (${effectiveTimeout}ms): ${method}`));
|
|
85
|
-
}, effectiveTimeout);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
this._pending.set(id, { resolve, reject, timer });
|
|
89
|
-
this._channel!.postMessage(JSON.stringify({ id, method, data }));
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
send(method: string, data: unknown = null): void {
|
|
95
|
-
this._channel!.postMessage(JSON.stringify({ method, data }));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Número de llamadas pendientes
|
|
100
|
-
*/
|
|
101
|
-
get pendingCount(): number {
|
|
102
|
-
return this._pending.size;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Cancela todas las llamadas pendientes
|
|
107
|
-
*/
|
|
108
|
-
cancelAll(): void {
|
|
109
|
-
for (const [, { reject, timer }] of this._pending) {
|
|
110
|
-
if (timer !== null) {
|
|
111
|
-
clearTimeout(timer);
|
|
112
|
-
}
|
|
113
|
-
reject(new Error('Cancelled'));
|
|
114
|
-
}
|
|
115
|
-
this._pending.clear();
|
|
116
|
-
}
|
|
1
|
+
// ─── Tipos ───────────────────────────────────────────────
|
|
2
|
+
interface BridgeOptions {
|
|
3
|
+
channel: Channel;
|
|
4
|
+
timeout?: number | null;
|
|
5
|
+
callbackName?: string; // ← nuevo
|
|
6
|
+
}
|
|
7
|
+
interface PendingCall {
|
|
8
|
+
resolve: (value: unknown) => void;
|
|
9
|
+
reject: (reason: Error) => void;
|
|
10
|
+
timer: ReturnType<typeof setTimeout> | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Channel {
|
|
14
|
+
postMessage: (message: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// ─── Clase ──────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
export default class NativeBridge {
|
|
22
|
+
private _pending = new Map<number, PendingCall>();
|
|
23
|
+
private _callId = 0;
|
|
24
|
+
private readonly _defaultTimeout: number | null;
|
|
25
|
+
private readonly _channel: Channel;
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
constructor({ channel, timeout = 3000, callbackName = '__nativeResponseHandler' }: BridgeOptions) {
|
|
29
|
+
this._defaultTimeout = timeout;
|
|
30
|
+
this._channel = channel;
|
|
31
|
+
|
|
32
|
+
if (!this._channel) {
|
|
33
|
+
throw new Error('Channel not found');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
(window as any)[callbackName] = this._callback;
|
|
37
|
+
|
|
38
|
+
// Proxy: cualquier propiedad no existente se convierte en call()
|
|
39
|
+
return new Proxy(this, {
|
|
40
|
+
get(target: NativeBridge, prop: string | symbol) {
|
|
41
|
+
if (prop in target) {
|
|
42
|
+
return (target as any)[prop];
|
|
43
|
+
}
|
|
44
|
+
return (data?: unknown, timeout?: number | null) => target.call(String(prop), data, timeout);
|
|
45
|
+
},
|
|
46
|
+
}) as unknown as NativeBridge;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
private _callback(id: number, error: any, result: any) {
|
|
51
|
+
|
|
52
|
+
const pending = this._pending.get(id);
|
|
53
|
+
if (!pending) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (pending.timer !== null) {
|
|
58
|
+
clearTimeout(pending.timer);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this._pending.delete(id);
|
|
62
|
+
|
|
63
|
+
if (error) {
|
|
64
|
+
pending.reject(error instanceof Error? error : new Error(String(error)));
|
|
65
|
+
} else {
|
|
66
|
+
pending.resolve(result);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
call(method: string, data: unknown = null, timeout?: number | null): Promise<unknown> {
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
const effectiveTimeout = timeout !== undefined ? timeout : this._defaultTimeout;
|
|
74
|
+
|
|
75
|
+
return new Promise<unknown>((resolve, reject) => {
|
|
76
|
+
const id = this._callId++;
|
|
77
|
+
|
|
78
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
79
|
+
|
|
80
|
+
// Solo crear timer si hay timeout
|
|
81
|
+
if (effectiveTimeout !== null && effectiveTimeout > 0) {
|
|
82
|
+
timer = setTimeout(() => {
|
|
83
|
+
this._pending.delete(id);
|
|
84
|
+
reject(new Error(`Timeout (${effectiveTimeout}ms): ${method}`));
|
|
85
|
+
}, effectiveTimeout);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this._pending.set(id, { resolve, reject, timer });
|
|
89
|
+
this._channel!.postMessage(JSON.stringify({ id, method, data }));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
send(method: string, data: unknown = null): void {
|
|
95
|
+
this._channel!.postMessage(JSON.stringify({ method, data }));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Número de llamadas pendientes
|
|
100
|
+
*/
|
|
101
|
+
get pendingCount(): number {
|
|
102
|
+
return this._pending.size;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Cancela todas las llamadas pendientes
|
|
107
|
+
*/
|
|
108
|
+
cancelAll(): void {
|
|
109
|
+
for (const [, { reject, timer }] of this._pending) {
|
|
110
|
+
if (timer !== null) {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
reject(new Error('Cancelled'));
|
|
114
|
+
}
|
|
115
|
+
this._pending.clear();
|
|
116
|
+
}
|
|
117
117
|
}
|
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
type CancellablePromiseExecutor<T> = (
|
|
2
|
-
resolve: (value: T | PromiseLike<T>) => void,
|
|
3
|
-
reject: (reason?: unknown) => void,
|
|
4
|
-
signal: AbortSignal
|
|
5
|
-
) => void;
|
|
6
|
-
|
|
7
|
-
export default class CancellablePromise<T> extends Promise<T> {
|
|
8
|
-
|
|
9
|
-
private _controller: AbortController;
|
|
10
|
-
|
|
11
|
-
static get [Symbol.species]() {
|
|
12
|
-
return Promise;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
constructor(executor: CancellablePromiseExecutor<T>) {
|
|
16
|
-
const controller = new AbortController();
|
|
17
|
-
|
|
18
|
-
super((resolve, reject) => {
|
|
19
|
-
controller.signal.addEventListener("abort", () => {
|
|
20
|
-
reject(controller.signal.reason);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
executor(resolve, reject, controller.signal);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
this._controller = controller;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get cancelled(): boolean {
|
|
30
|
-
return this._controller.signal.aborted;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
cancel(reason: unknown = new Error("Promesa cancelada")): void {
|
|
34
|
-
if (!this.cancelled) {
|
|
35
|
-
this._controller.abort(reason);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static async compete<T>(promises: CancellablePromise<T>[]): Promise<T> {
|
|
40
|
-
return await Promise.race(promises).then(
|
|
41
|
-
(resultado) => {
|
|
42
|
-
for (const p of promises) {
|
|
43
|
-
p.cancel();
|
|
44
|
-
}
|
|
45
|
-
return resultado;
|
|
46
|
-
},
|
|
47
|
-
(error) => {
|
|
48
|
-
for (const p of promises) {
|
|
49
|
-
p.cancel();
|
|
50
|
-
}
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
static async cooperate<T>(promises: CancellablePromise<T>[]): Promise<T[]> {
|
|
57
|
-
try {
|
|
58
|
-
return await Promise.all(promises);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
for (const p of promises) {
|
|
61
|
-
p.cancel();
|
|
62
|
-
}
|
|
63
|
-
throw error;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
static withTimeout<T>(executor: CancellablePromiseExecutor<T>, ms: number): CancellablePromise<T> {
|
|
68
|
-
return new CancellablePromise<T>((resolve, reject, signal) => {
|
|
69
|
-
const timer = setTimeout(() => {
|
|
70
|
-
reject(new Error(`Timeout: ${ms}ms`));
|
|
71
|
-
}, ms);
|
|
72
|
-
|
|
73
|
-
signal.addEventListener("abort", () => clearTimeout(timer));
|
|
74
|
-
executor(resolve, reject, signal);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
1
|
+
type CancellablePromiseExecutor<T> = (
|
|
2
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
3
|
+
reject: (reason?: unknown) => void,
|
|
4
|
+
signal: AbortSignal
|
|
5
|
+
) => void;
|
|
6
|
+
|
|
7
|
+
export default class CancellablePromise<T> extends Promise<T> {
|
|
8
|
+
|
|
9
|
+
private _controller: AbortController;
|
|
10
|
+
|
|
11
|
+
static get [Symbol.species]() {
|
|
12
|
+
return Promise;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
constructor(executor: CancellablePromiseExecutor<T>) {
|
|
16
|
+
const controller = new AbortController();
|
|
17
|
+
|
|
18
|
+
super((resolve, reject) => {
|
|
19
|
+
controller.signal.addEventListener("abort", () => {
|
|
20
|
+
reject(controller.signal.reason);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
executor(resolve, reject, controller.signal);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
this._controller = controller;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get cancelled(): boolean {
|
|
30
|
+
return this._controller.signal.aborted;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cancel(reason: unknown = new Error("Promesa cancelada")): void {
|
|
34
|
+
if (!this.cancelled) {
|
|
35
|
+
this._controller.abort(reason);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async compete<T>(promises: CancellablePromise<T>[]): Promise<T> {
|
|
40
|
+
return await Promise.race(promises).then(
|
|
41
|
+
(resultado) => {
|
|
42
|
+
for (const p of promises) {
|
|
43
|
+
p.cancel();
|
|
44
|
+
}
|
|
45
|
+
return resultado;
|
|
46
|
+
},
|
|
47
|
+
(error) => {
|
|
48
|
+
for (const p of promises) {
|
|
49
|
+
p.cancel();
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static async cooperate<T>(promises: CancellablePromise<T>[]): Promise<T[]> {
|
|
57
|
+
try {
|
|
58
|
+
return await Promise.all(promises);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
for (const p of promises) {
|
|
61
|
+
p.cancel();
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static withTimeout<T>(executor: CancellablePromiseExecutor<T>, ms: number): CancellablePromise<T> {
|
|
68
|
+
return new CancellablePromise<T>((resolve, reject, signal) => {
|
|
69
|
+
const timer = setTimeout(() => {
|
|
70
|
+
reject(new Error(`Timeout: ${ms}ms`));
|
|
71
|
+
}, ms);
|
|
72
|
+
|
|
73
|
+
signal.addEventListener("abort", () => clearTimeout(timer));
|
|
74
|
+
executor(resolve, reject, signal);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
77
|
}
|
package/src/collections/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {default as DefaultWeakMap} from './default-weak-map.js'
|
|
2
|
-
export {default as DefaultMap} from './default-map.js'
|
|
1
|
+
export {default as DefaultWeakMap} from './default-weak-map.js'
|
|
2
|
+
export {default as DefaultMap} from './default-map.js'
|
|
3
3
|
export {default as CacheDictionary} from './cache-dictionary.js';
|
package/src/draft.ts
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Methods injected into a draft proxy in addition to the original object's properties.
|
|
3
|
+
*
|
|
4
|
+
* @template T - The shape of the wrapped object.
|
|
5
|
+
*/
|
|
6
|
+
type DraftMethods<T> = {
|
|
7
|
+
/**
|
|
8
|
+
* Apply pending changes to the original object.
|
|
9
|
+
*
|
|
10
|
+
* @param key - Optional property key. If provided, commits only that property.
|
|
11
|
+
* If omitted, commits every modified property recursively.
|
|
12
|
+
* @returns `true` when a single-property commit was applied,
|
|
13
|
+
* `false` when the property was not modified,
|
|
14
|
+
* `void` when committing the whole draft.
|
|
15
|
+
*/
|
|
16
|
+
commit(key?: keyof T): boolean | void;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Discard pending changes, restoring values from the original object.
|
|
20
|
+
*
|
|
21
|
+
* @param key - Optional property key. If provided, rolls back only that property.
|
|
22
|
+
* If omitted, rolls back every modified property recursively.
|
|
23
|
+
* @returns `true` when a single-property rollback was applied,
|
|
24
|
+
* `false` when the property was not modified,
|
|
25
|
+
* `void` when rolling back the whole draft.
|
|
26
|
+
*/
|
|
27
|
+
rollback(key?: keyof T): boolean | void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check whether the draft (or a specific property) has pending changes.
|
|
31
|
+
*
|
|
32
|
+
* @param key - Optional property key. If omitted, checks the whole draft.
|
|
33
|
+
* @returns `true` if there are pending changes.
|
|
34
|
+
*/
|
|
35
|
+
isModified(key?: keyof T): boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the list of property keys with pending changes at this draft level.
|
|
39
|
+
*
|
|
40
|
+
* @returns Array of modified property keys (does not recurse into children).
|
|
41
|
+
*/
|
|
42
|
+
modifiedProps(): (keyof T)[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Manually mark (or unmark) a property as modified.
|
|
46
|
+
* Primarily used by child drafts to notify their parent, but also
|
|
47
|
+
* available for advanced manual control.
|
|
48
|
+
*
|
|
49
|
+
* @param key - Property key to flag.
|
|
50
|
+
* @param modified - `true` to mark as modified (default), `false` to clear.
|
|
51
|
+
*/
|
|
52
|
+
setModified(key: keyof T, modified?: boolean): void;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A draft proxy: the original object's properties plus draft control methods.
|
|
57
|
+
*
|
|
58
|
+
* @template T - The shape of the wrapped object.
|
|
59
|
+
*/
|
|
60
|
+
export type Draft<T extends object> = T & DraftMethods<T>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Link from a child draft to its parent, used to propagate modification status.
|
|
64
|
+
*/
|
|
65
|
+
type ParentLink = {
|
|
66
|
+
/** Parent draft methods reference (we only need `setModified`). */
|
|
67
|
+
parent: { setModified(key: PropertyKey, modified?: boolean): void };
|
|
68
|
+
/** Key under which this draft is stored in the parent. */
|
|
69
|
+
keyInParent: PropertyKey;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// ─────────────────────────────────────────────
|
|
73
|
+
// deepDraft (internal)
|
|
74
|
+
// ─────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Recursively wrap a value so that nested objects/arrays become child drafts
|
|
78
|
+
* linked to a parent. Primitives, `Date`, `RegExp`, `Map` and `Set` instances
|
|
79
|
+
* are cloned but not wrapped.
|
|
80
|
+
*
|
|
81
|
+
* @param value - The value to process.
|
|
82
|
+
* @param parentLink - Optional link to a parent draft for change propagation.
|
|
83
|
+
* @returns A drafted value (or a plain clone for non-wrappable types).
|
|
84
|
+
*/
|
|
85
|
+
function deepDraft(value: any, parentLink?: ParentLink): any {
|
|
86
|
+
if (value === null || typeof value !== "object") return value;
|
|
87
|
+
if (value instanceof Date) return new Date(value);
|
|
88
|
+
if (value instanceof RegExp) return new RegExp(value);
|
|
89
|
+
|
|
90
|
+
if (value instanceof Map) {
|
|
91
|
+
const copy = new Map();
|
|
92
|
+
for (const [k, v] of value) {
|
|
93
|
+
copy.set(deepDraft(k), deepDraft(v));
|
|
94
|
+
}
|
|
95
|
+
return copy;
|
|
96
|
+
}
|
|
97
|
+
if (value instanceof Set) {
|
|
98
|
+
const copy = new Set();
|
|
99
|
+
for (const v of value) {
|
|
100
|
+
copy.add(deepDraft(v));
|
|
101
|
+
}
|
|
102
|
+
return copy;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return draft(value, parentLink);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a draft proxy over `original`. The proxy exposes the same properties
|
|
110
|
+
* as the original (with nested objects wrapped as child drafts) plus the
|
|
111
|
+
* control methods `commit`, `rollback`, `isModified`, `modifiedProps` and
|
|
112
|
+
* `setModified`.
|
|
113
|
+
*
|
|
114
|
+
* Mutations on the draft do not affect the original until `commit()` is called.
|
|
115
|
+
* Nested mutations bubble up: modifying a deep property marks every ancestor
|
|
116
|
+
* draft as modified at the corresponding key.
|
|
117
|
+
*
|
|
118
|
+
* @template T - The shape of the wrapped object.
|
|
119
|
+
* @param original - The object to wrap. It will only be mutated on `commit()`.
|
|
120
|
+
* @param parentLink - Internal link used when this draft is a child of another.
|
|
121
|
+
* Do not pass manually.
|
|
122
|
+
* @returns A draft proxy over a deep copy of `original`.
|
|
123
|
+
*/
|
|
124
|
+
export default function draft<T extends object>(
|
|
125
|
+
original: T,
|
|
126
|
+
parentLink?: ParentLink
|
|
127
|
+
): Draft<T> {
|
|
128
|
+
const copy: any = Array.isArray(original)
|
|
129
|
+
? []
|
|
130
|
+
: Object.create(Object.getPrototypeOf(original));
|
|
131
|
+
|
|
132
|
+
const touched = new Set<PropertyKey>();
|
|
133
|
+
|
|
134
|
+
const notifyParent = (modified: boolean): void => {
|
|
135
|
+
if (parentLink) {
|
|
136
|
+
parentLink.parent.setModified(parentLink.keyInParent, modified);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Apply commit for a single key: delegate to the child draft if present,
|
|
142
|
+
* otherwise copy the value into `original`.
|
|
143
|
+
*/
|
|
144
|
+
const applyCommit = (key: PropertyKey): void => {
|
|
145
|
+
const val = copy[key];
|
|
146
|
+
if (
|
|
147
|
+
val !== null &&
|
|
148
|
+
typeof val === "object" &&
|
|
149
|
+
"commit" in val
|
|
150
|
+
) {
|
|
151
|
+
(val as Draft<any>).commit();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (key in copy) {
|
|
155
|
+
(original as any)[key] = copy[key];
|
|
156
|
+
} else {
|
|
157
|
+
delete (original as any)[key];
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Apply rollback for a single key: delegate to the child draft if present,
|
|
162
|
+
* otherwise restore the value from `original`.
|
|
163
|
+
*/
|
|
164
|
+
const applyRollback = (key: PropertyKey): void => {
|
|
165
|
+
const val = copy[key];
|
|
166
|
+
if (
|
|
167
|
+
val !== null &&
|
|
168
|
+
typeof val === "object" &&
|
|
169
|
+
"rollback" in val
|
|
170
|
+
) {
|
|
171
|
+
(val as Draft<any>).rollback();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (key in original) {
|
|
175
|
+
copy[key] = (original as any)[key];
|
|
176
|
+
} else {
|
|
177
|
+
delete copy[key];
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const methods: DraftMethods<T> = {
|
|
182
|
+
commit(key) {
|
|
183
|
+
if (key === undefined) {
|
|
184
|
+
for (const k of touched) applyCommit(k);
|
|
185
|
+
touched.clear();
|
|
186
|
+
notifyParent(false);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (!touched.has(key)) return false;
|
|
190
|
+
applyCommit(key);
|
|
191
|
+
touched.delete(key);
|
|
192
|
+
if (touched.size === 0) notifyParent(false);
|
|
193
|
+
return true;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
rollback(key) {
|
|
197
|
+
if (key === undefined) {
|
|
198
|
+
for (const k of touched) applyRollback(k);
|
|
199
|
+
touched.clear();
|
|
200
|
+
notifyParent(false);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (!touched.has(key)) return false;
|
|
204
|
+
applyRollback(key);
|
|
205
|
+
touched.delete(key);
|
|
206
|
+
if (touched.size === 0) notifyParent(false);
|
|
207
|
+
return true;
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
isModified(key) {
|
|
211
|
+
return key === undefined ? touched.size > 0 : touched.has(key);
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
modifiedProps() {
|
|
215
|
+
return [...touched] as (keyof T)[];
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
setModified(key, modified = true) {
|
|
219
|
+
if (modified) {
|
|
220
|
+
touched.add(key);
|
|
221
|
+
notifyParent(true);
|
|
222
|
+
} else {
|
|
223
|
+
touched.delete(key);
|
|
224
|
+
if (touched.size === 0) notifyParent(false);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const isMethodKey = (prop: PropertyKey): prop is keyof DraftMethods<T> =>
|
|
230
|
+
typeof prop === "string" && prop in methods;
|
|
231
|
+
|
|
232
|
+
// Populate `copy` with child drafts linked to this draft.
|
|
233
|
+
for (const key of Reflect.ownKeys(original)) {
|
|
234
|
+
copy[key] = deepDraft((original as any)[key], {
|
|
235
|
+
parent: methods,
|
|
236
|
+
keyInParent: key,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return new Proxy(copy, {
|
|
241
|
+
get(target, prop, receiver) {
|
|
242
|
+
if (isMethodKey(prop)) return (methods as any)[prop];
|
|
243
|
+
return Reflect.get(target, prop, receiver);
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
set(target, prop, value, receiver) {
|
|
247
|
+
if (isMethodKey(prop)) {
|
|
248
|
+
throw new Error(`"${String(prop)}" is a reserved draft property`);
|
|
249
|
+
}
|
|
250
|
+
const wrapped = deepDraft(value, {
|
|
251
|
+
parent: methods,
|
|
252
|
+
keyInParent: prop,
|
|
253
|
+
});
|
|
254
|
+
touched.add(prop);
|
|
255
|
+
notifyParent(true);
|
|
256
|
+
return Reflect.set(target, prop, wrapped, receiver);
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
deleteProperty(target, prop) {
|
|
260
|
+
if (isMethodKey(prop)) {
|
|
261
|
+
throw new Error(`"${String(prop)}" is a reserved draft property`);
|
|
262
|
+
}
|
|
263
|
+
touched.add(prop);
|
|
264
|
+
notifyParent(true);
|
|
265
|
+
return Reflect.deleteProperty(target, prop);
|
|
266
|
+
},
|
|
267
|
+
}) as Draft<T>;
|
|
268
|
+
}
|