@zenfs/core 2.2.3 → 2.3.1

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.
Files changed (51) hide show
  1. package/dist/backends/backend.js +6 -9
  2. package/dist/backends/cow.js +4 -4
  3. package/dist/backends/fetch.js +8 -6
  4. package/dist/backends/memory.js +4 -2
  5. package/dist/backends/passthrough.js +2 -0
  6. package/dist/backends/port.d.ts +16 -89
  7. package/dist/backends/port.js +35 -171
  8. package/dist/backends/single_buffer.d.ts +2 -2
  9. package/dist/backends/single_buffer.js +165 -198
  10. package/dist/backends/store/fs.js +50 -73
  11. package/dist/backends/store/map.js +1 -2
  12. package/dist/backends/store/store.js +23 -27
  13. package/dist/config.js +2 -3
  14. package/dist/context.js +2 -2
  15. package/dist/internal/devices.js +7 -10
  16. package/dist/internal/file_index.js +3 -8
  17. package/dist/internal/filesystem.js +19 -12
  18. package/dist/internal/index_fs.js +3 -4
  19. package/dist/internal/inode.js +144 -187
  20. package/dist/internal/rpc.d.ts +143 -0
  21. package/dist/internal/rpc.js +251 -0
  22. package/dist/mixins/async.js +5 -6
  23. package/dist/mixins/mutexed.js +16 -10
  24. package/dist/path.js +3 -4
  25. package/dist/polyfills.js +51 -22
  26. package/dist/readline.js +32 -30
  27. package/dist/utils.d.ts +2 -0
  28. package/dist/utils.js +11 -5
  29. package/dist/vfs/acl.js +44 -68
  30. package/dist/vfs/async.js +4 -4
  31. package/dist/vfs/dir.js +12 -8
  32. package/dist/vfs/file.js +22 -18
  33. package/dist/vfs/ioctl.js +39 -62
  34. package/dist/vfs/promises.js +48 -39
  35. package/dist/vfs/shared.js +4 -5
  36. package/dist/vfs/stats.js +104 -77
  37. package/dist/vfs/streams.js +11 -8
  38. package/dist/vfs/sync.js +23 -26
  39. package/dist/vfs/watchers.js +9 -3
  40. package/dist/vfs/xattr.js +6 -12
  41. package/package.json +2 -2
  42. package/scripts/test.js +16 -7
  43. package/tests/backend/fetch.test.ts +14 -14
  44. package/tests/backend/port.test.ts +25 -17
  45. package/tests/common/handle.test.ts +5 -3
  46. package/tests/fetch/run.sh +2 -1
  47. package/tests/fs/scaling.test.ts +32 -0
  48. package/tests/fs/watch.test.ts +2 -5
  49. package/tests/setup/single-buffer.ts +1 -1
  50. package/tests/tsconfig.json +3 -2
  51. package/types/uint8array.d.ts +64 -0
@@ -0,0 +1,143 @@
1
+ import type { ExceptionJSON } from 'kerium';
2
+ import type { TransferListItem } from 'node:worker_threads';
3
+ import type { WithOptional } from 'utilium';
4
+ import type { Backend, FilesystemOf } from '../backends/backend.js';
5
+ import type { PortFS } from '../backends/port.js';
6
+ import type { CreationOptions, FileSystem, UsageInfo } from '../internal/filesystem.js';
7
+ import '../polyfills.js';
8
+ export interface WebMessagePort {
9
+ postMessage(value: unknown, transfer?: TransferListItem[]): void;
10
+ addEventListener(type: 'message', listener: (ev: {
11
+ data: any;
12
+ }) => void): void;
13
+ removeEventListener(type: 'message', listener: (ev: {
14
+ data: any;
15
+ }) => void): void;
16
+ }
17
+ export interface NodeMessagePort {
18
+ postMessage(value: unknown, transfer?: TransferListItem[]): void;
19
+ on(type: 'message', listener: (ev: any) => void): void;
20
+ off(type: 'message', listener: (ev: any) => void): void;
21
+ }
22
+ export type Channel = NodeMessagePort | WebMessagePort | WebSocket;
23
+ /** @internal */
24
+ export interface Port<T extends Channel = Channel> {
25
+ readonly channel: T;
26
+ /** Send a request */
27
+ send<M extends Message>(message: M, transfer?: TransferListItem[]): void;
28
+ /** Add a response handler */
29
+ addHandler<M extends Message>(handler: (message: M) => void): void;
30
+ /** Remove a response handler */
31
+ removeHandler<M extends Message>(handler: (message: M) => void): void;
32
+ /** Remove all handlers */
33
+ disconnect?(): void;
34
+ }
35
+ export declare function isPort<T extends Channel>(port: unknown): port is Port<T>;
36
+ /**
37
+ * Creates a new RPC port from a `Worker` or `MessagePort` that extends `EventTarget`
38
+ */
39
+ export declare function fromWeb<T extends WebMessagePort>(port: T): Port<T>;
40
+ /**
41
+ * Creates a new RPC port from a Node.js `Worker` or `MessagePort`.
42
+ */
43
+ export declare function fromNode<T extends NodeMessagePort>(port: T): Port<T>;
44
+ /**
45
+ * Creates a new RPC port from a WebSocket.
46
+ * @experimental
47
+ */
48
+ export declare function fromWebSocket<T extends WebSocket>(ws: T): Port<T>;
49
+ export declare function from<T extends Channel>(port: T | Port<T>): Port<T>;
50
+ /**
51
+ * The options for the RPC options
52
+ * @category Backends and Configuration
53
+ */
54
+ export interface Options {
55
+ /**
56
+ * The target port that you want to connect to, or the current port if in a port context.
57
+ */
58
+ port: Port;
59
+ /**
60
+ * How long to wait for a request to complete
61
+ */
62
+ timeout?: number;
63
+ }
64
+ /**
65
+ * The API for remote procedure calls
66
+ * @category Internals
67
+ * @internal
68
+ */
69
+ export interface Methods {
70
+ usage(): UsageInfo;
71
+ ready(): void;
72
+ rename(oldPath: string, newPath: string): void;
73
+ createFile(path: string, options: CreationOptions): Uint8Array;
74
+ unlink(path: string): void;
75
+ rmdir(path: string): void;
76
+ mkdir(path: string, options: CreationOptions): Uint8Array;
77
+ readdir(path: string): string[];
78
+ touch(path: string, metadata: Uint8Array): void;
79
+ exists(path: string): boolean;
80
+ link(target: string, link: string): void;
81
+ sync(): void;
82
+ read(path: string, buffer: Uint8Array, start: number, end: number): Uint8Array;
83
+ write(path: string, buffer: Uint8Array, offset: number): void;
84
+ stat(path: string): Uint8Array;
85
+ }
86
+ /**
87
+ * The methods that can be called on the RPC port
88
+ * @category Internals
89
+ * @internal
90
+ */
91
+ export type Method = keyof Methods;
92
+ /**
93
+ * An RPC message
94
+ * @category Internals
95
+ * @internal
96
+ */
97
+ export interface Message {
98
+ _zenfs: true;
99
+ id: string;
100
+ method: Method;
101
+ stack: string;
102
+ }
103
+ export interface Request<TMethod extends Method = Method> extends Message {
104
+ method: TMethod;
105
+ args: Parameters<Methods[TMethod]>;
106
+ }
107
+ export interface Response<TMethod extends Method = Method> extends Message {
108
+ error?: WithOptional<ExceptionJSON, 'code' | 'errno'>;
109
+ method: TMethod;
110
+ value: ReturnType<Methods[TMethod]>;
111
+ }
112
+ /**
113
+ * Encode a RPC message as a string using JSON.
114
+ * This is only done when structured cloning is not available.
115
+ * @internal
116
+ */
117
+ export declare function encodeMessage(message: Message): string;
118
+ /**
119
+ * Decode a RPC message from a string using JSON.
120
+ * This is only done when structured cloning is not available.
121
+ * @internal
122
+ */
123
+ export declare function decodeMessage<T extends Message>(message: string): T;
124
+ export declare function isMessage(arg: unknown): arg is Message;
125
+ /**
126
+ * An RPC executor
127
+ * @internal @hidden
128
+ */
129
+ export interface Executor extends PromiseWithResolvers<any> {
130
+ fs: PortFS;
131
+ timeout: ReturnType<typeof setTimeout>;
132
+ }
133
+ export declare function request<const TRequest extends Request, TValue>(request: Omit<TRequest, 'id' | 'stack' | '_zenfs'>, { port, timeout: ms, fs }: Partial<Options> & {
134
+ fs: PortFS;
135
+ }): Promise<TValue>;
136
+ export declare function handleResponse<const TMethod extends Method>(response: Response<TMethod>): void;
137
+ export declare function attach<T extends Message>(port: Port, handler: (message: T) => unknown): void;
138
+ export declare function detach<T extends Message>(port: Port, handler: (message: T) => unknown): void;
139
+ export declare function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<T>) => Promise<void>;
140
+ /** @internal */
141
+ export declare function handleRequest(port: Port, fs: FileSystem & {
142
+ _descriptors?: Map<number, File>;
143
+ }, request: Request): Promise<void>;
@@ -0,0 +1,251 @@
1
+ import { Errno, Exception, withErrno } from 'kerium';
2
+ import { err, info, warn } from 'kerium/log';
3
+ import { isJSON, pick } from 'utilium';
4
+ import { Inode } from '../internal/inode.js';
5
+ import '../polyfills.js';
6
+ export function isPort(port) {
7
+ return port != null && typeof port == 'object' && 'channel' in port && 'send' in port && 'addHandler' in port && 'removeHandler' in port;
8
+ }
9
+ /**
10
+ * Creates a new RPC port from a `Worker` or `MessagePort` that extends `EventTarget`
11
+ */
12
+ export function fromWeb(port) {
13
+ return {
14
+ channel: port,
15
+ send: port.postMessage.bind(port),
16
+ addHandler(handler) {
17
+ port.addEventListener('message', (event) => handler(event.data));
18
+ },
19
+ removeHandler(handler) {
20
+ port.removeEventListener('message', (event) => handler(event.data));
21
+ },
22
+ };
23
+ }
24
+ /**
25
+ * Creates a new RPC port from a Node.js `Worker` or `MessagePort`.
26
+ */
27
+ export function fromNode(port) {
28
+ return {
29
+ channel: port,
30
+ send: port.postMessage.bind(port),
31
+ addHandler: port.on.bind(port, 'message'),
32
+ removeHandler: port.off.bind(port, 'message'),
33
+ };
34
+ }
35
+ /**
36
+ * Creates a new RPC port from a WebSocket.
37
+ * @experimental
38
+ */
39
+ export function fromWebSocket(ws) {
40
+ return {
41
+ channel: ws,
42
+ send(message) {
43
+ ws.send(encodeMessage(message));
44
+ },
45
+ addHandler(handler) {
46
+ ws.addEventListener('message', event => {
47
+ handler(decodeMessage(event.data));
48
+ });
49
+ },
50
+ removeHandler(handler) {
51
+ ws.removeEventListener('message', event => {
52
+ handler(decodeMessage(event.data));
53
+ });
54
+ },
55
+ };
56
+ }
57
+ export function from(port) {
58
+ if (isPort(port))
59
+ return port;
60
+ if (port instanceof WebSocket)
61
+ return fromWebSocket(port);
62
+ if ('on' in port)
63
+ return fromNode(port);
64
+ if ('addEventListener' in port)
65
+ return fromWeb(port);
66
+ throw err(withErrno('EINVAL', 'Invalid port type'));
67
+ }
68
+ /*
69
+
70
+ Notes on encoding:
71
+
72
+
73
+ Buffer prefix ($):
74
+
75
+ Used to mark when a Uint8Array is encoded into JSON using base64.
76
+ These are encoded "special" since by default it becomes {"0":n,"1":n,...}
77
+ It shouldn't be possible to forge this since all paths start with / and inodes are serialized as buffers
78
+
79
+ Message prefix and version (Z...):
80
+
81
+ Used to indicate that this is a ZenFS message, rather than some 3rd party message.
82
+ Immediately following the message prefix is a plain-text version.
83
+ This is used in case the encoding changes in the future, so a client and server with mismatched versions can detect it.
84
+ */
85
+ const encodingVersion = 1;
86
+ /**
87
+ * Encode a RPC message as a string using JSON.
88
+ * This is only done when structured cloning is not available.
89
+ * @internal
90
+ */
91
+ export function encodeMessage(message) {
92
+ return `Z${encodingVersion}${JSON.stringify(message, (key, value) => {
93
+ if (key == '_zenfs')
94
+ return; // encoded differently
95
+ return value instanceof Uint8Array ? '$' + value.toBase64() : value;
96
+ })}`;
97
+ }
98
+ /**
99
+ * Decode a RPC message from a string using JSON.
100
+ * This is only done when structured cloning is not available.
101
+ * @internal
102
+ */
103
+ export function decodeMessage(message) {
104
+ if (!message.startsWith('Z'))
105
+ return {}; // ignore not-ZenFS messages
106
+ message = message.slice(1);
107
+ const v = parseInt(message); // hack so we don't have to figure out how long the version is
108
+ if (isNaN(v)) {
109
+ warn('Ignoring encoded message with missing version');
110
+ return {};
111
+ }
112
+ message = message.slice(v.toString().length);
113
+ if (!isJSON(message)) {
114
+ warn('Ignoring encoded message with invalid JSON');
115
+ return {};
116
+ }
117
+ if (v != encodingVersion)
118
+ throw err(withErrno('EPROTONOSUPPORT', `Version mismatch in RPC message encoding (got ${v}, expected ${encodingVersion})`));
119
+ return {
120
+ ...JSON.parse(message, (key, value) => (typeof value == 'string' && value.startsWith('$') ? Uint8Array.fromBase64(value.slice(1)) : value)),
121
+ _zenfs: true,
122
+ };
123
+ }
124
+ export function isMessage(arg) {
125
+ return typeof arg == 'object' && arg != null && '_zenfs' in arg && !!arg._zenfs;
126
+ }
127
+ function disposeExecutors(id) {
128
+ const executor = executors.get(id);
129
+ if (!executor)
130
+ return;
131
+ if (executor.timeout) {
132
+ clearTimeout(executor.timeout);
133
+ if (typeof executor.timeout == 'object')
134
+ executor.timeout.unref();
135
+ }
136
+ executor.fs._executors.delete(id);
137
+ executors.delete(id);
138
+ }
139
+ /**
140
+ * A map of *all* outstanding RPC requests
141
+ */
142
+ const executors = new Map();
143
+ export function request(request, { port, timeout: ms = 1000, fs }) {
144
+ const stack = '\n' + new Error().stack.slice('Error:'.length);
145
+ if (!port)
146
+ throw err(withErrno('EINVAL', 'Can not make an RPC request without a port'));
147
+ const { resolve, reject, promise } = Promise.withResolvers();
148
+ const id = Math.random().toString(16).slice(10);
149
+ const timeout = setTimeout(() => {
150
+ const error = err(withErrno('EIO', 'RPC Failed'));
151
+ error.stack += stack;
152
+ disposeExecutors(id);
153
+ reject(error);
154
+ }, ms);
155
+ const executor = { resolve, reject, promise, fs, timeout };
156
+ fs._executors.set(id, executor);
157
+ executors.set(id, executor);
158
+ port.send({ ...request, _zenfs: true, id, stack });
159
+ return promise;
160
+ }
161
+ // Why Typescript, WHY does the type need to be asserted even when the method is explicitly checked?
162
+ function __requestMethod(req) { }
163
+ function __responseMethod(res, ...t) {
164
+ return t.includes(res.method);
165
+ }
166
+ export function handleResponse(response) {
167
+ if (!isMessage(response))
168
+ return;
169
+ if (!executors.has(response.id)) {
170
+ const error = err(withErrno('EIO', 'Invalid RPC id: ' + response.id));
171
+ error.stack += response.stack;
172
+ throw error;
173
+ }
174
+ const { resolve, reject } = executors.get(response.id);
175
+ if (response.error) {
176
+ const e = Exception.fromJSON({ code: 'EIO', errno: Errno.EIO, ...response.error });
177
+ e.stack += response.stack;
178
+ disposeExecutors(response.id);
179
+ reject(e);
180
+ return;
181
+ }
182
+ disposeExecutors(response.id);
183
+ resolve(__responseMethod(response, 'stat', 'createFile', 'mkdir') ? new Inode(response.value) : response.value);
184
+ return;
185
+ }
186
+ export function attach(port, handler) {
187
+ if (!port)
188
+ throw err(withErrno('EINVAL', 'Cannot attach to non-existent port'));
189
+ info('Attached handler to port: ' + handler.name);
190
+ port.addHandler(handler);
191
+ }
192
+ export function detach(port, handler) {
193
+ if (!port)
194
+ throw err(withErrno('EINVAL', 'Cannot detach from non-existent port'));
195
+ info('Detached handler from port: ' + handler.name);
196
+ port.removeHandler(handler);
197
+ }
198
+ export function catchMessages(port) {
199
+ const events = [];
200
+ const handler = events.push.bind(events);
201
+ attach(port, handler);
202
+ return async function (fs) {
203
+ detach(port, handler);
204
+ for (const event of events) {
205
+ const request = 'data' in event ? event.data : event;
206
+ await handleRequest(port, fs, request);
207
+ }
208
+ };
209
+ }
210
+ /** @internal */
211
+ export async function handleRequest(port, fs, request) {
212
+ if (!isMessage(request))
213
+ return;
214
+ let value, error;
215
+ const transferList = [];
216
+ try {
217
+ switch (request.method) {
218
+ case 'read': {
219
+ __requestMethod(request);
220
+ const [path, buffer, start, end] = request.args;
221
+ await fs.read(path, buffer, start, end);
222
+ value = buffer;
223
+ break;
224
+ }
225
+ case 'stat':
226
+ case 'createFile':
227
+ case 'mkdir': {
228
+ __requestMethod(request);
229
+ // @ts-expect-error 2556
230
+ const md = await fs[request.method](...request.args);
231
+ const inode = md instanceof Inode ? md : new Inode(md);
232
+ value = new Uint8Array(inode.buffer, inode.byteOffset, inode.byteLength);
233
+ break;
234
+ }
235
+ case 'touch': {
236
+ __requestMethod(request);
237
+ const [path, metadata] = request.args;
238
+ await fs.touch(path, new Inode(metadata));
239
+ value = undefined;
240
+ break;
241
+ }
242
+ default:
243
+ // @ts-expect-error 2556
244
+ value = (await fs[request.method](...request.args));
245
+ }
246
+ }
247
+ catch (e) {
248
+ error = e instanceof Exception ? e.toJSON() : pick(e, 'message', 'stack');
249
+ }
250
+ port.send({ _zenfs: true, ...pick(request, 'id', 'method', 'stack'), error, value }, transferList);
251
+ }
@@ -28,15 +28,15 @@ export function Async(FS) {
28
28
  queueDone() {
29
29
  return this.sync();
30
30
  }
31
+ _promise = Promise.resolve();
31
32
  _async(promise) {
32
33
  this._promise = this._promise.then(() => promise);
33
34
  }
35
+ _isInitialized = false;
36
+ /** Tracks how many updates to the sync. cache we skipped during initialization */
37
+ _skippedCacheUpdates = 0;
34
38
  constructor(...args) {
35
39
  super(...args);
36
- this._promise = Promise.resolve();
37
- this._isInitialized = false;
38
- /** Tracks how many updates to the sync. cache we skipped during initialization */
39
- this._skippedCacheUpdates = 0;
40
40
  this._patchAsync();
41
41
  }
42
42
  async ready() {
@@ -191,7 +191,6 @@ export function Async(FS) {
191
191
  // TS does not narrow the union based on the key
192
192
  const originalMethod = this[key].bind(this);
193
193
  this[key] = async (...args) => {
194
- var _a, _b;
195
194
  const result = await originalMethod(...args);
196
195
  const stack = new Error().stack.split('\n').slice(2).join('\n');
197
196
  // From the async queue
@@ -206,7 +205,7 @@ export function Async(FS) {
206
205
  }
207
206
  try {
208
207
  // @ts-expect-error 2556 - The type of `args` is not narrowed
209
- (_b = (_a = this._sync) === null || _a === void 0 ? void 0 : _a[`${key}Sync`]) === null || _b === void 0 ? void 0 : _b.call(_a, ...args);
208
+ this._sync?.[`${key}Sync`]?.(...args);
210
209
  }
211
210
  catch (e) {
212
211
  const stack = e.stack.split('\n').slice(3).join('\n');
@@ -58,17 +58,17 @@ import '../polyfills.js';
58
58
  * @internal
59
59
  */
60
60
  export class MutexLock {
61
+ previous;
62
+ current = Promise.withResolvers();
63
+ _isLocked = true;
61
64
  get isLocked() {
62
65
  return this._isLocked;
63
66
  }
64
67
  constructor(previous) {
65
68
  this.previous = previous;
66
- this.current = Promise.withResolvers();
67
- this._isLocked = true;
68
69
  }
69
70
  async done() {
70
- var _a;
71
- await ((_a = this.previous) === null || _a === void 0 ? void 0 : _a.done());
71
+ await this.previous?.done();
72
72
  await this.current.promise;
73
73
  }
74
74
  unlock() {
@@ -84,6 +84,10 @@ export class MutexLock {
84
84
  * @category Internals
85
85
  */
86
86
  export class _MutexedFS {
87
+ /**
88
+ * @internal
89
+ */
90
+ _fs;
87
91
  get type() {
88
92
  return this._fs.type;
89
93
  }
@@ -114,6 +118,10 @@ export class _MutexedFS {
114
118
  usage() {
115
119
  return this._fs.usage();
116
120
  }
121
+ /**
122
+ * The current locks
123
+ */
124
+ currentLock;
117
125
  /**
118
126
  * Adds a lock for a path
119
127
  */
@@ -134,11 +142,11 @@ export class _MutexedFS {
134
142
  setTimeout(() => {
135
143
  if (lock.isLocked) {
136
144
  const error = withErrno('EDEADLK');
137
- error.stack += stack === null || stack === void 0 ? void 0 : stack.slice('Error'.length);
145
+ error.stack += stack?.slice('Error'.length);
138
146
  throw err(error);
139
147
  }
140
148
  }, 5000);
141
- await (previous === null || previous === void 0 ? void 0 : previous.done());
149
+ await previous?.done();
142
150
  return lock;
143
151
  }
144
152
  /**
@@ -147,8 +155,7 @@ export class _MutexedFS {
147
155
  * @internal
148
156
  */
149
157
  lockSync() {
150
- var _a;
151
- if ((_a = this.currentLock) === null || _a === void 0 ? void 0 : _a.isLocked) {
158
+ if (this.currentLock?.isLocked) {
152
159
  throw err(withErrno('EBUSY'));
153
160
  }
154
161
  return this.addLock();
@@ -158,8 +165,7 @@ export class _MutexedFS {
158
165
  * @internal
159
166
  */
160
167
  get isLocked() {
161
- var _a;
162
- return !!((_a = this.currentLock) === null || _a === void 0 ? void 0 : _a.isLocked);
168
+ return !!this.currentLock?.isLocked;
163
169
  }
164
170
  /* eslint-disable @typescript-eslint/no-unused-vars */
165
171
  async rename(oldPath, newPath) {
package/dist/path.js CHANGED
@@ -101,10 +101,9 @@ export function formatExt(ext) {
101
101
  return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
102
102
  }
103
103
  export function resolve(...parts) {
104
- var _a;
105
104
  let resolved = '';
106
- for (const part of [...parts.reverse(), (_a = this === null || this === void 0 ? void 0 : this.pwd) !== null && _a !== void 0 ? _a : defaultContext.pwd]) {
107
- if (!(part === null || part === void 0 ? void 0 : part.length))
105
+ for (const part of [...parts.reverse(), this?.pwd ?? defaultContext.pwd]) {
106
+ if (!part?.length)
108
107
  continue;
109
108
  resolved = `${part}/${resolved}`;
110
109
  if (part.startsWith('/')) {
@@ -144,7 +143,7 @@ export function join(...parts) {
144
143
  if (!parts.length)
145
144
  return '.';
146
145
  const joined = parts.join('/');
147
- if (!(joined === null || joined === void 0 ? void 0 : joined.length))
146
+ if (!joined?.length)
148
147
  return '.';
149
148
  return normalize(joined);
150
149
  }
package/dist/polyfills.js CHANGED
@@ -1,29 +1,58 @@
1
1
  /* node:coverage disable */
2
2
  /* eslint-disable @typescript-eslint/unbound-method */
3
- var _a, _b, _c, _d;
4
- var _e;
5
3
  import { warn } from 'kerium/log';
6
- (_a = Promise.withResolvers) !== null && _a !== void 0 ? _a : (Promise.withResolvers = (warn('Using a polyfill of Promise.withResolvers'),
7
- function () {
8
- let _resolve,
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- _reject;
11
- const promise = new Promise((resolve, reject) => {
12
- _resolve = resolve;
13
- _reject = reject;
4
+ Promise.withResolvers ??=
5
+ (warn('Using a polyfill of Promise.withResolvers'),
6
+ function () {
7
+ let _resolve,
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ _reject;
10
+ const promise = new Promise((resolve, reject) => {
11
+ _resolve = resolve;
12
+ _reject = reject;
13
+ });
14
+ return { promise, resolve: _resolve, reject: _reject };
14
15
  });
15
- return { promise, resolve: _resolve, reject: _reject };
16
- }));
17
16
  // @ts-expect-error 2540
18
- (_b = Symbol['dispose']) !== null && _b !== void 0 ? _b : (Symbol['dispose'] = (warn('Using a polyfill of Symbol.dispose'), Symbol('Symbol.dispose')));
17
+ Symbol['dispose'] ??= (warn('Using a polyfill of Symbol.dispose'), Symbol('Symbol.dispose'));
19
18
  // @ts-expect-error 2540
20
- (_c = Symbol['asyncDispose']) !== null && _c !== void 0 ? _c : (Symbol['asyncDispose'] = (warn('Using a polyfill of Symbol.asyncDispose'), Symbol('Symbol.asyncDispose')));
21
- function randomUUID() {
22
- const bytes = crypto.getRandomValues(new Uint8Array(16));
23
- bytes[6] = (bytes[6] & 0x0f) | 0x40;
24
- bytes[8] = (bytes[8] & 0x3f) | 0x80;
25
- const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');
26
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
27
- }
28
- (_d = (_e = globalThis.crypto).randomUUID) !== null && _d !== void 0 ? _d : (_e.randomUUID = (warn('Using a polyfill of crypto.randomUUID'), randomUUID));
19
+ Symbol['asyncDispose'] ??= (warn('Using a polyfill of Symbol.asyncDispose'), Symbol('Symbol.asyncDispose'));
20
+ globalThis.crypto.randomUUID ??=
21
+ (warn('Using a polyfill of crypto.randomUUID'),
22
+ function randomUUID() {
23
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
24
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
25
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
26
+ const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');
27
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
28
+ });
29
+ Uint8Array.prototype.toBase64 ??=
30
+ (warn('Using a polyfill of Uint8Array.prototype.toBase64'),
31
+ function toBase64() {
32
+ return btoa(String.fromCharCode(...this));
33
+ });
34
+ Uint8Array.fromBase64 ??=
35
+ (warn('Using a polyfill of Uint8Array.fromBase64'),
36
+ function fromBase64(base64) {
37
+ const binaryString = atob(base64);
38
+ const bytes = new Uint8Array(binaryString.length);
39
+ for (let i = 0; i < binaryString.length; i++) {
40
+ bytes[i] = binaryString.charCodeAt(i);
41
+ }
42
+ return bytes;
43
+ });
44
+ Uint8Array.prototype.toHex ??=
45
+ (warn('Using a polyfill of Uint8Array.prototype.toHex'),
46
+ function toHex() {
47
+ return [...this].map(b => b.toString(16).padStart(2, '0')).join('');
48
+ });
49
+ Uint8Array.fromHex ??=
50
+ (warn('Using a polyfill of Uint8Array.fromHex'),
51
+ function fromHex(hex) {
52
+ const bytes = new Uint8Array(hex.length / 2);
53
+ for (let i = 0; i < hex.length; i += 2) {
54
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
55
+ }
56
+ return bytes;
57
+ });
29
58
  /* node:coverage enable */