@ai-ecoverse/slicc-kernel 1.123.1 → 1.124.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/README.md +29 -6
- package/dist/kernel-worker.js +680 -85
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +88 -18
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +710 -115
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +88 -18
- package/dist/process-worker.js.map +3 -3
- package/dist/types/kernel/fd-table.d.ts +1 -1
- package/dist/types/kernel/host-ops.d.ts +1 -0
- package/dist/types/kernel/lock-service.d.ts +4 -0
- package/dist/types/kernel/locks.d.ts +3 -0
- package/dist/types/kernel/net/network.d.ts +2 -0
- package/dist/types/kernel/net/switch-conn.d.ts +28 -0
- package/dist/types/kernel/net/tls-client.d.ts +22 -0
- package/dist/types/kernel/net/tls-engine.d.ts +27 -0
- package/dist/types/kernel/net/tls-uplink.d.ts +35 -0
- package/dist/types/kernel/socket-syscalls.d.ts +10 -1
- package/dist/types/kernel/socket.d.ts +13 -1
- package/dist/types/process/js/js-net.d.ts +10 -4
- package/package.json +4 -4
|
@@ -2,7 +2,7 @@ import type { ClipboardChannel } from './clipboard.js';
|
|
|
2
2
|
import type { PtyPair } from './pty.js';
|
|
3
3
|
import type { SealedChannel } from './sealed.js';
|
|
4
4
|
import type { KernelTty } from './tty.js';
|
|
5
|
-
export type KernelErrno = 'EBADF' | 'EDEADLK' | 'ENOLCK' | 'EEXIST' | 'ELOOP' | 'EOVERFLOW' | 'ENAMETOOLONG' | 'EPIPE' | 'EMFILE' | 'EINVAL' | 'ENOENT' | 'ENOTDIR' | 'ENOEXEC' | 'ECHILD' | 'ENOSYS' | 'ESPIPE' | 'ENOSPC' | 'EINTR' | 'ESRCH' | 'ENOTTY' | 'ENXIO' | 'EPERM' | 'EIO' | 'EACCES' | 'EAGAIN' | 'ENOTSOCK' | 'EAFNOSUPPORT' | 'EPROTONOSUPPORT' | 'EPROTOTYPE' | 'EOPNOTSUPP' | 'EADDRINUSE' | 'EADDRNOTAVAIL' | 'ENETUNREACH' | 'ECONNREFUSED' | 'EINPROGRESS' | 'EISCONN' | 'ENOTCONN' | 'EROFS' | 'EFBIG' | 'EALREADY' | 'ETIMEDOUT' | 'ECONNABORTED' | 'EHOSTUNREACH' | 'ECONNRESET' | 'EMSGSIZE' | 'EDESTADDRREQ' | 'ENOPROTOOPT' | 'ENOTSUP' | 'EILSEQ';
|
|
5
|
+
export type KernelErrno = 'EBADF' | 'EPROTO' | 'EDEADLK' | 'ENOLCK' | 'EEXIST' | 'ELOOP' | 'EOVERFLOW' | 'ENAMETOOLONG' | 'EPIPE' | 'EMFILE' | 'EINVAL' | 'ENOENT' | 'ENOTDIR' | 'ENOEXEC' | 'ECHILD' | 'ENOSYS' | 'ESPIPE' | 'ENOSPC' | 'EINTR' | 'ESRCH' | 'ENOTTY' | 'ENXIO' | 'EPERM' | 'EIO' | 'EACCES' | 'EAGAIN' | 'ENOTSOCK' | 'EAFNOSUPPORT' | 'EPROTONOSUPPORT' | 'EPROTOTYPE' | 'EOPNOTSUPP' | 'EADDRINUSE' | 'EADDRNOTAVAIL' | 'ENETUNREACH' | 'ECONNREFUSED' | 'EINPROGRESS' | 'EISCONN' | 'ENOTCONN' | 'EROFS' | 'EFBIG' | 'EALREADY' | 'ETIMEDOUT' | 'ECONNABORTED' | 'EHOSTUNREACH' | 'ECONNRESET' | 'EMSGSIZE' | 'EDESTADDRREQ' | 'ENOPROTOOPT' | 'ENOTSUP' | 'EILSEQ';
|
|
6
6
|
export declare class KernelError extends Error {
|
|
7
7
|
readonly code: KernelErrno;
|
|
8
8
|
readonly detail?: string;
|
|
@@ -24,6 +24,8 @@ export interface KernelLocks {
|
|
|
24
24
|
dropFile(key: string, owner: number): void;
|
|
25
25
|
release(pid: number): void;
|
|
26
26
|
lock(pid: number, path: string, exclusive: boolean, fd: number): boolean;
|
|
27
|
+
wholeFree?(pid: number, path: string, exclusive: boolean): boolean;
|
|
28
|
+
flockFree?(key: string, owner: object, kind: FlockKind): boolean;
|
|
27
29
|
unlock(pid: number, path: string): void;
|
|
28
30
|
closed(pid: number, fd: number): void;
|
|
29
31
|
}
|
|
@@ -92,6 +94,8 @@ export declare class LockService implements KernelLocks {
|
|
|
92
94
|
private flockWait;
|
|
93
95
|
private flockDrop;
|
|
94
96
|
lock(pid: number, path: string, exclusive: boolean, fd: number): boolean;
|
|
97
|
+
wholeFree(pid: number, path: string, exclusive: boolean): boolean;
|
|
98
|
+
flockFree(key: string, owner: object, kind: FlockKind): boolean;
|
|
95
99
|
unlock(pid: number, path: string): void;
|
|
96
100
|
closed(pid: number, fd: number): void;
|
|
97
101
|
}
|
|
@@ -20,6 +20,7 @@ export declare class FlockTable {
|
|
|
20
20
|
private readonly held;
|
|
21
21
|
private readonly keys;
|
|
22
22
|
private readonly wakers;
|
|
23
|
+
isFree(key: string, owner: object, kind: FlockKind): boolean;
|
|
23
24
|
private free;
|
|
24
25
|
set(key: string, owner: object, kind: FlockKind | 'unlock'): boolean;
|
|
25
26
|
wait(key: string, owner: object, kind: FlockKind, blocking: () => AbortSignal): Promise<void>;
|
|
@@ -45,6 +46,8 @@ export declare class LockTable {
|
|
|
45
46
|
entries(): Array<[string, RecordLock[]]>;
|
|
46
47
|
holds(owner: number): boolean;
|
|
47
48
|
dropFile(key: string, owner: number): void;
|
|
49
|
+
wholeFree(pid: number, path: string, exclusive: boolean): boolean;
|
|
50
|
+
flockFree(key: string, owner: object, kind: FlockKind): boolean;
|
|
48
51
|
lock(pid: number, path: string, exclusive: boolean, fd: number): boolean;
|
|
49
52
|
unlock(pid: number, path: string): void;
|
|
50
53
|
closed(pid: number, fd: number): void;
|
|
@@ -13,9 +13,11 @@ export declare function missingTransport(): RealmTransport;
|
|
|
13
13
|
export declare function memoryCaStore(): CaStore;
|
|
14
14
|
export declare function caStore(name: string | false): CaStore;
|
|
15
15
|
export declare function packageTlsEngine(fs: KernelFs, modules: string): () => Promise<TlsEngineModule>;
|
|
16
|
+
export declare function packageTlsRoots(fs: KernelFs, modules: string): () => Promise<Uint8Array>;
|
|
16
17
|
export interface NetworkOptions {
|
|
17
18
|
transport?: RealmTransport;
|
|
18
19
|
engine: () => Promise<TlsEngine>;
|
|
20
|
+
roots?: () => Promise<Uint8Array>;
|
|
19
21
|
ca: () => Promise<RealmCa>;
|
|
20
22
|
now?: () => number;
|
|
21
23
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { UplinkConn } from './uplink.js';
|
|
2
|
+
export declare class SwitchConn implements UplinkConn {
|
|
3
|
+
private readonly raw;
|
|
4
|
+
private readonly box;
|
|
5
|
+
private pulling;
|
|
6
|
+
private waiters;
|
|
7
|
+
private switching;
|
|
8
|
+
private secure;
|
|
9
|
+
private failed;
|
|
10
|
+
constructor(raw: UplinkConn);
|
|
11
|
+
get localAddr(): string;
|
|
12
|
+
get remoteAddr(): string;
|
|
13
|
+
get upgraded(): boolean;
|
|
14
|
+
private wake;
|
|
15
|
+
private changed;
|
|
16
|
+
private pull;
|
|
17
|
+
private take;
|
|
18
|
+
private rawRead;
|
|
19
|
+
read(): Promise<Uint8Array | null>;
|
|
20
|
+
private settled;
|
|
21
|
+
write(bytes: Uint8Array): Promise<number>;
|
|
22
|
+
closeWrite(): void;
|
|
23
|
+
close(): void;
|
|
24
|
+
upgrade<T>(make: (raw: UplinkConn) => Promise<{
|
|
25
|
+
conn: UplinkConn;
|
|
26
|
+
info: T;
|
|
27
|
+
}>): Promise<T>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type TlsEngine, type TlsStore } from './tls-engine.js';
|
|
2
|
+
import type { UplinkConn } from './uplink.js';
|
|
3
|
+
export declare function verifyCode(flags: number): string | undefined;
|
|
4
|
+
export interface TlsClientInfo {
|
|
5
|
+
protocol: 'TLSv1.2' | 'TLSv1.3';
|
|
6
|
+
alpnProtocol: string | false;
|
|
7
|
+
authorized: boolean;
|
|
8
|
+
authorizationError?: string;
|
|
9
|
+
authorizationText?: string;
|
|
10
|
+
peerCertificates: Uint8Array[];
|
|
11
|
+
}
|
|
12
|
+
export interface TlsClientOptions {
|
|
13
|
+
engine: TlsEngine;
|
|
14
|
+
store: TlsStore | undefined;
|
|
15
|
+
servername: string;
|
|
16
|
+
alpn: readonly string[];
|
|
17
|
+
insecure: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function tlsClient(conn: UplinkConn, o: TlsClientOptions, taken?: Uint8Array): Promise<{
|
|
20
|
+
conn: UplinkConn;
|
|
21
|
+
info: TlsClientInfo;
|
|
22
|
+
}>;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
type TlsExport = 'malloc' | 'free' | 'tls_last_error' | 'tls_strerror' | 'tls_leaf_new' | 'tls_leaf_spki' | 'tls_leaf_add_cert' | 'tls_leaf_free' | 'tls_session_new' | 'tls_feed' | 'tls_feed_eof' | 'tls_in_room' | 'tls_read' | 'tls_write' | 'tls_out_size' | 'tls_out_take' | 'tls_close' | 'tls_alpn_http11' | 'tls_session_free';
|
|
2
|
+
type TlsClientExport = 'tls_ca_new' | 'tls_ca_add_der' | 'tls_ca_add_bundle' | 'tls_ca_free' | 'tls_client_new' | 'tls_handshake' | 'tls_verify_result' | 'tls_verify_info' | 'tls_alpn' | 'tls_version' | 'tls_peer_count' | 'tls_peer_der';
|
|
2
3
|
export type TlsEngineModule = {
|
|
3
4
|
readonly HEAPU8: Uint8Array;
|
|
4
5
|
} & {
|
|
5
6
|
readonly [K in TlsExport as `_${K}`]: (...args: number[]) => number;
|
|
7
|
+
} & {
|
|
8
|
+
readonly [K in TlsClientExport as `_${K}`]?: (...args: number[]) => number;
|
|
9
|
+
};
|
|
10
|
+
type TlsClientModule = TlsEngineModule & {
|
|
11
|
+
readonly [K in TlsClientExport as `_${K}`]: (...args: number[]) => number;
|
|
6
12
|
};
|
|
13
|
+
export declare const TLS_CLIENT_INSECURE_SKIP_VERIFY = 1;
|
|
7
14
|
export declare class TlsError extends Error {
|
|
8
15
|
readonly code: number;
|
|
9
16
|
constructor(code: number, message: string);
|
|
@@ -19,6 +26,20 @@ export declare class TlsEngine {
|
|
|
19
26
|
session(leaf: TlsLeaf, host: string): TlsSession;
|
|
20
27
|
withBytes<T>(bytes: Uint8Array, fn: (ptr: number) => T): T;
|
|
21
28
|
intoScratch(fn: (ptr: number, max: number) => number): number | Uint8Array;
|
|
29
|
+
get client(): TlsClientModule | undefined;
|
|
30
|
+
private clientModule;
|
|
31
|
+
store(): TlsStore;
|
|
32
|
+
clientSession(store: TlsStore | undefined, servername: string, alpn: readonly string[], flags: number): TlsSession;
|
|
33
|
+
verifyInfo(flags: number): string;
|
|
34
|
+
}
|
|
35
|
+
export declare class TlsStore {
|
|
36
|
+
private readonly engine;
|
|
37
|
+
private readonly m;
|
|
38
|
+
readonly ptr: number;
|
|
39
|
+
constructor(engine: TlsEngine, m: TlsClientModule, ptr: number);
|
|
40
|
+
add(der: Uint8Array): void;
|
|
41
|
+
addBundle(bytes: Uint8Array): number;
|
|
42
|
+
release(): void;
|
|
22
43
|
}
|
|
23
44
|
export declare class TlsLeaf {
|
|
24
45
|
private readonly engine;
|
|
@@ -41,6 +62,12 @@ export declare class TlsSession {
|
|
|
41
62
|
take(): Uint8Array;
|
|
42
63
|
close(): void;
|
|
43
64
|
free(): void;
|
|
65
|
+
private get c();
|
|
66
|
+
handshake(): boolean;
|
|
67
|
+
get verifyResult(): number;
|
|
68
|
+
get version(): number;
|
|
69
|
+
get alpn(): string | undefined;
|
|
70
|
+
peerChain(): Uint8Array[];
|
|
44
71
|
}
|
|
45
72
|
export declare function loadTlsEngine(load: () => Promise<TlsEngineModule>): Promise<TlsEngine>;
|
|
46
73
|
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { KernelSocket, LoopbackNet } from '../socket.js';
|
|
2
|
+
import { type TlsClientInfo } from './tls-client.js';
|
|
3
|
+
import type { TlsEngine } from './tls-engine.js';
|
|
4
|
+
import type { ResolveFamily } from './uplink.js';
|
|
5
|
+
export interface TlsClientSupport {
|
|
6
|
+
engine: () => Promise<TlsEngine>;
|
|
7
|
+
roots: () => Promise<Uint8Array>;
|
|
8
|
+
}
|
|
9
|
+
export interface SecureRequest {
|
|
10
|
+
servername?: string;
|
|
11
|
+
alpn?: string[];
|
|
12
|
+
ca?: true | string[];
|
|
13
|
+
insecure?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SecureProcess {
|
|
16
|
+
net: LoopbackNet;
|
|
17
|
+
blocking(): AbortSignal;
|
|
18
|
+
resolve(name: string, family: ResolveFamily, blocking: () => AbortSignal): Promise<string[]>;
|
|
19
|
+
}
|
|
20
|
+
export declare function serverName(name: string): string;
|
|
21
|
+
export interface SecureSession {
|
|
22
|
+
path: 'kernel';
|
|
23
|
+
substitutes: false;
|
|
24
|
+
protocol: TlsClientInfo['protocol'];
|
|
25
|
+
alpnProtocol: string | false;
|
|
26
|
+
authorized: boolean;
|
|
27
|
+
authorizationError?: string;
|
|
28
|
+
authorizationText?: string;
|
|
29
|
+
servername: string;
|
|
30
|
+
peerCertificates: string[];
|
|
31
|
+
}
|
|
32
|
+
export declare function supportOf(net: LoopbackNet): TlsClientSupport;
|
|
33
|
+
export declare function uplinkAddress(proc: SecureProcess, host: string): Promise<string>;
|
|
34
|
+
export declare function connectSecure(proc: SecureProcess, socket: KernelSocket, host: string, port: number, req: SecureRequest): Promise<SecureSession>;
|
|
35
|
+
export declare function upgradeSecure(proc: SecureProcess, socket: KernelSocket, req: SecureRequest): Promise<SecureSession>;
|
|
@@ -63,7 +63,16 @@ export type SocketSyscall = {
|
|
|
63
63
|
port: number;
|
|
64
64
|
servername?: string;
|
|
65
65
|
alpn?: string[];
|
|
66
|
-
ca?:
|
|
66
|
+
ca?: true | string[];
|
|
67
|
+
insecure?: boolean;
|
|
68
|
+
path?: 'kernel' | 'transport';
|
|
69
|
+
} | {
|
|
70
|
+
op: 'sock-tls-upgrade';
|
|
71
|
+
fd: number;
|
|
72
|
+
servername?: string;
|
|
73
|
+
alpn?: string[];
|
|
74
|
+
ca?: true | string[];
|
|
75
|
+
insecure?: boolean;
|
|
67
76
|
} | {
|
|
68
77
|
op: 'sock-recvfrom';
|
|
69
78
|
fd: number;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Datagram, InetAddr, KernelDgram } from './dgram.js';
|
|
2
2
|
import { type KernelFile, type PollState } from './fd-table.js';
|
|
3
3
|
import { type Destination, Routes } from './net/routes.js';
|
|
4
|
-
import
|
|
4
|
+
import { SwitchConn } from './net/switch-conn.js';
|
|
5
|
+
import type { TlsClientSupport } from './net/tls-uplink.js';
|
|
6
|
+
import type { NetworkUplink, UplinkConn } from './net/uplink.js';
|
|
5
7
|
import { KernelPipe } from './pipe.js';
|
|
6
8
|
import type { SealedCaller } from './sealed.js';
|
|
7
9
|
import type { UnixDgram } from './unix-dgram.js';
|
|
@@ -85,7 +87,16 @@ export declare class KernelSocket implements KernelFile {
|
|
|
85
87
|
connect(addr: SockAddr, viaUplink?: boolean, cred?: PeerCred, stamp?: ConnectorStamp, waits?: boolean, name?: SocketName): Promise<void> | undefined;
|
|
86
88
|
private join;
|
|
87
89
|
private queued;
|
|
90
|
+
private switch;
|
|
91
|
+
connectSecure(addr: Extract<SockAddr, {
|
|
92
|
+
family: 'inet';
|
|
93
|
+
}>, route: UplinkRoute, secure: (conn: SwitchConn) => Promise<void>): Promise<void>;
|
|
94
|
+
upgradeSecure<T>(make: (raw: UplinkConn) => Promise<{
|
|
95
|
+
conn: UplinkConn;
|
|
96
|
+
info: T;
|
|
97
|
+
}>): Promise<T>;
|
|
88
98
|
private dial;
|
|
99
|
+
private dialFailed;
|
|
89
100
|
private unlink;
|
|
90
101
|
shutdown(how: number): void;
|
|
91
102
|
getOption(level: number, name: number): number;
|
|
@@ -109,6 +120,7 @@ export declare class LoopbackNet {
|
|
|
109
120
|
useOrigins(serve: (conn: KernelSocket, origin: string) => Promise<void>): void;
|
|
110
121
|
serveOrigin(conn: KernelSocket, origin: string): void;
|
|
111
122
|
useUplink(route: UplinkRoute): void;
|
|
123
|
+
tlsClient: TlsClientSupport | undefined;
|
|
112
124
|
uplinkFor(host: string): UplinkRoute | undefined;
|
|
113
125
|
destination(host: string): Destination;
|
|
114
126
|
bindDgram(socket: KernelDgram, addr: SockAddr, kernel?: boolean): InetAddr;
|
|
@@ -4,16 +4,21 @@ export interface JsAddress {
|
|
|
4
4
|
port: number;
|
|
5
5
|
}
|
|
6
6
|
export interface JsTlsSession {
|
|
7
|
-
readonly
|
|
8
|
-
readonly
|
|
9
|
-
readonly
|
|
7
|
+
readonly path: 'transport' | 'kernel';
|
|
8
|
+
readonly substitutes: boolean;
|
|
9
|
+
readonly protocol: 'transport' | 'TLSv1.2' | 'TLSv1.3';
|
|
10
|
+
readonly alpnProtocol: string | false;
|
|
11
|
+
readonly authorized: boolean;
|
|
12
|
+
readonly authorizationError?: string;
|
|
10
13
|
readonly servername: string;
|
|
14
|
+
readonly peerCertificates?: readonly Uint8Array[];
|
|
11
15
|
}
|
|
12
16
|
export interface JsTlsOptions {
|
|
13
17
|
servername?: string;
|
|
14
18
|
alpn?: string[];
|
|
15
19
|
rejectUnauthorized?: boolean;
|
|
16
20
|
ca?: unknown;
|
|
21
|
+
path?: 'kernel' | 'transport';
|
|
17
22
|
}
|
|
18
23
|
export interface JsConnection {
|
|
19
24
|
readonly fd: number;
|
|
@@ -40,9 +45,10 @@ export interface JsListenOptions {
|
|
|
40
45
|
}
|
|
41
46
|
export interface JsConnectOptions {
|
|
42
47
|
host?: string;
|
|
43
|
-
port
|
|
48
|
+
port?: number;
|
|
44
49
|
signal?: AbortSignal;
|
|
45
50
|
tls?: JsTlsOptions;
|
|
51
|
+
socket?: JsConnection;
|
|
46
52
|
}
|
|
47
53
|
export interface JsResolveOptions {
|
|
48
54
|
family?: 0 | 4 | 6;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-kernel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.124.0",
|
|
4
4
|
"description": "SLICC's wasm-realm kernel: run Emscripten programs in the browser against OPFS, with fork, spawn, pipes and waitpid",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -67,12 +67,12 @@
|
|
|
67
67
|
"@ai-ecoverse/slicc-node": "2.5.1",
|
|
68
68
|
"@ai-ecoverse/slicc-scrub": "1.0.0",
|
|
69
69
|
"@ai-ecoverse/slicc-shared-web": "1.23.2",
|
|
70
|
-
"@ai-ecoverse/wasi-cargo": "0.99.0-
|
|
70
|
+
"@ai-ecoverse/wasi-cargo": "0.99.0-3",
|
|
71
71
|
"@ai-ecoverse/wasi-pnpm": "12.9.1-9",
|
|
72
72
|
"@ai-ecoverse/wasi-ripgrep": "15.2.0-5",
|
|
73
73
|
"@ai-ecoverse/wasi-rustc": "1.98.1-4",
|
|
74
74
|
"@ai-ecoverse/wasi-zig": "0.16.0-15",
|
|
75
|
-
"@ai-ecoverse/wasix-python": "3.14.2-
|
|
75
|
+
"@ai-ecoverse/wasix-python": "3.14.2-27",
|
|
76
76
|
"@ai-ecoverse/wasm-bash": "5.3.0-19",
|
|
77
77
|
"@ai-ecoverse/wasm-coreutils": "9.12.0-9",
|
|
78
78
|
"@ai-ecoverse/wasm-curl": "8.22.0-6",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@ai-ecoverse/wasm-procps": "4.0.7-3",
|
|
82
82
|
"@ai-ecoverse/wasm-sqlite3": "3.54.0-5",
|
|
83
83
|
"@ai-ecoverse/wasm-tar": "1.35.0-7",
|
|
84
|
-
"@ai-ecoverse/wasm-tls-engine": "4.2.0-
|
|
84
|
+
"@ai-ecoverse/wasm-tls-engine": "4.2.0-2",
|
|
85
85
|
"@ai-ecoverse/wasm-tree": "2.3.2-4",
|
|
86
86
|
"@ai-ecoverse/wasm-util-linux": "2.42.4-3",
|
|
87
87
|
"@biomejs/biome": "2.5.15",
|