wasm-idle 1.0.12 → 1.1.2
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 +150 -37
- package/dist/playground/applicationAssets.js +2 -0
- package/dist/playground/assetBridge.js +106 -31
- package/dist/playground/assets.d.ts +2 -2
- package/dist/playground/c3.js +22 -0
- package/dist/playground/c3Memory.d.ts +19 -0
- package/dist/playground/c3Memory.js +117 -0
- package/dist/playground/cachedClang.js +143 -34
- package/dist/playground/clangAssetIntegrity.js +6 -6
- package/dist/playground/clangWorkerProtocol.d.ts +12 -1
- package/dist/playground/cobol.d.ts +3 -0
- package/dist/playground/cobol.js +10 -1
- package/dist/playground/commonlisp.d.ts +8 -0
- package/dist/playground/commonlisp.js +33 -0
- package/dist/playground/commonlispAssets.d.ts +30 -0
- package/dist/playground/commonlispAssets.js +184 -0
- package/dist/playground/compiledArtifactCache.d.ts +3 -1
- package/dist/playground/compiledArtifactCache.js +31 -5
- package/dist/playground/container/ContainerExplorer.svelte +307 -0
- package/dist/playground/container/ContainerExplorer.svelte.d.ts +14 -0
- package/dist/playground/container/binding.d.ts +26 -0
- package/dist/playground/container/binding.js +247 -0
- package/dist/playground/container/clang-runtime.d.ts +38 -0
- package/dist/playground/container/clang-runtime.js +343 -0
- package/dist/playground/container/client.d.ts +15 -0
- package/dist/playground/container/client.js +189 -0
- package/dist/playground/container/explorer.d.ts +31 -0
- package/dist/playground/container/explorer.js +112 -0
- package/dist/playground/container/owner.worker.d.ts +1 -0
- package/dist/playground/container/owner.worker.js +390 -0
- package/dist/playground/container/protocol.d.ts +73 -0
- package/dist/playground/container/protocol.js +28 -0
- package/dist/playground/container/release.d.ts +31 -0
- package/dist/playground/container/release.js +612 -0
- package/dist/playground/container/runtime-adapter.d.ts +73 -0
- package/dist/playground/container/runtime-adapter.js +474 -0
- package/dist/playground/container/shell-control.d.ts +49 -0
- package/dist/playground/container/shell-control.js +303 -0
- package/dist/playground/container/workspace.d.ts +8 -0
- package/dist/playground/container/workspace.js +29 -0
- package/dist/playground/d.d.ts +6 -1
- package/dist/playground/d.js +83 -5
- package/dist/playground/dotnet.js +6 -8
- package/dist/playground/fortran.d.ts +3 -0
- package/dist/playground/fortran.js +10 -1
- package/dist/playground/go.js +14 -3
- package/dist/playground/goDebugInstrumentation.js +184 -54
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +14 -0
- package/dist/playground/java.js +3 -2
- package/dist/playground/javaRuntimeErrors.d.ts +9 -0
- package/dist/playground/javaRuntimeErrors.js +60 -0
- package/dist/playground/javaRuntimeStdin.d.ts +9 -0
- package/dist/playground/javaRuntimeStdin.js +145 -0
- package/dist/playground/lean4.d.ts +13 -0
- package/dist/playground/lean4.js +50 -0
- package/dist/playground/lean4Assets.d.ts +25 -0
- package/dist/playground/lean4Assets.js +123 -0
- package/dist/playground/lfortran.js +10 -0
- package/dist/playground/lfortranAssets.d.ts +2 -2
- package/dist/playground/loadingProgress.js +4 -1
- package/dist/playground/nim.js +22 -0
- package/dist/playground/objectivec.d.ts +3 -0
- package/dist/playground/objectivec.js +11 -2
- package/dist/playground/options.d.ts +2 -0
- package/dist/playground/python.js +8 -0
- package/dist/playground/runtimeAssetCache.d.ts +1 -32
- package/dist/playground/runtimeAssetCache.js +1 -193
- package/dist/playground/runtimeSession.d.ts +2 -2
- package/dist/playground/runtimeSession.js +11 -11
- package/dist/playground/rust.d.ts +6 -0
- package/dist/playground/rust.js +103 -6
- package/dist/playground/staticStdinRing.d.ts +2 -1
- package/dist/playground/staticStdinRing.js +21 -10
- package/dist/playground/staticWorkerRuntime.d.ts +15 -1
- package/dist/playground/staticWorkerRuntime.js +119 -17
- package/dist/playground/tinygo.d.ts +3 -0
- package/dist/playground/tinygo.js +81 -6
- package/dist/playground/v.d.ts +3 -0
- package/dist/playground/v.js +10 -1
- package/dist/playground/wasmAwkVersion.d.ts +13 -13
- package/dist/playground/wasmAwkVersion.js +4 -4
- package/dist/playground/wasmBqnVersion.d.ts +2 -2
- package/dist/playground/wasmBqnVersion.js +2 -2
- package/dist/playground/wasmC3Version.d.ts +2 -2
- package/dist/playground/wasmC3Version.js +2 -2
- package/dist/playground/wasmClojureScriptVersion.d.ts +2 -2
- package/dist/playground/wasmClojureScriptVersion.js +2 -2
- package/dist/playground/wasmCommonLispVersion.d.ts +32 -0
- package/dist/playground/wasmCommonLispVersion.js +33 -0
- package/dist/playground/wasmDIntegrity.d.ts +5 -5
- package/dist/playground/wasmDIntegrity.js +5 -5
- package/dist/playground/wasmDVersion.d.ts +16 -1
- package/dist/playground/wasmDVersion.js +16 -1
- package/dist/playground/wasmDebugVersion.d.ts +1 -1
- package/dist/playground/wasmDebugVersion.js +1 -1
- package/dist/playground/wasmForthVersion.d.ts +2 -2
- package/dist/playground/wasmForthVersion.js +2 -2
- package/dist/playground/wasmGleamVersion.d.ts +2 -2
- package/dist/playground/wasmGleamVersion.js +2 -2
- package/dist/playground/wasmGrainVersion.d.ts +2 -2
- package/dist/playground/wasmGrainVersion.js +2 -2
- package/dist/playground/wasmJVersion.d.ts +2 -2
- package/dist/playground/wasmJVersion.js +2 -2
- package/dist/playground/wasmJanetVersion.js +2 -2
- package/dist/playground/wasmJuliaVersion.js +2 -2
- package/dist/playground/wasmLean4Version.d.ts +110 -0
- package/dist/playground/wasmLean4Version.js +111 -0
- package/dist/playground/wasmLfortranVersion.d.ts +2 -2
- package/dist/playground/wasmLfortranVersion.js +2 -2
- package/dist/playground/wasmNimVersion.d.ts +9 -9
- package/dist/playground/wasmNimVersion.js +6 -6
- package/dist/playground/wasmOcamlVersion.d.ts +2 -2
- package/dist/playground/wasmOcamlVersion.js +1 -1
- package/dist/playground/wasmPascalVersion.js +2 -2
- package/dist/playground/wasmPerlVersion.js +2 -2
- package/dist/playground/wasmPrologVersion.d.ts +2 -2
- package/dist/playground/wasmPrologVersion.js +2 -2
- package/dist/playground/wasmReScriptVersion.d.ts +2 -2
- package/dist/playground/wasmReScriptVersion.js +2 -2
- package/dist/playground/wasmRustVersion.d.ts +8 -8
- package/dist/playground/wasmRustVersion.js +7 -7
- package/dist/playground/wasmTclVersion.d.ts +4 -4
- package/dist/playground/wasmTclVersion.js +2 -2
- package/dist/playground/wasmTinyGoVersion.d.ts +14 -14
- package/dist/playground/wasmTinyGoVersion.js +14 -14
- package/dist/playground/wasmTypeScriptVersion.d.ts +5 -5
- package/dist/playground/wasmTypeScriptVersion.js +5 -5
- package/dist/playground/worker/cachedClangCompile.js +52 -4
- package/dist/playground/worker/cachedClangExecute.js +6 -5
- package/dist/playground/worker/clang.js +32 -10
- package/dist/playground/worker/cobol.js +2 -2
- package/dist/playground/worker/d.js +12 -5
- package/dist/playground/worker/fortran.js +2 -2
- package/dist/playground/worker/go.js +16 -5
- package/dist/playground/worker/haskell.js +10 -7
- package/dist/playground/worker/java.js +92 -39
- package/dist/playground/worker/lua.js +2 -3
- package/dist/playground/worker/php.js +15 -9
- package/dist/playground/worker/python.js +131 -131
- package/dist/playground/worker/pythonDebugPreview.d.ts +2 -0
- package/dist/playground/worker/pythonDebugPreview.js +55 -0
- package/dist/playground/worker/pythonExecution.d.ts +8 -0
- package/dist/playground/worker/pythonExecution.js +183 -0
- package/dist/playground/worker/pythonStdio.d.ts +32 -0
- package/dist/playground/worker/pythonStdio.js +253 -0
- package/dist/playground/worker/pythonWorkspaceFiles.d.ts +6 -0
- package/dist/playground/worker/pythonWorkspaceFiles.js +26 -0
- package/dist/playground/worker/pythonWorkspaceImports.d.ts +4 -0
- package/dist/playground/worker/pythonWorkspaceImports.js +129 -0
- package/dist/playground/worker/r.js +13 -6
- package/dist/playground/worker/rust.js +34 -2
- package/dist/playground/worker/sqlite.js +4 -3
- package/dist/playground/worker/tinygo.js +11 -3
- package/dist/playground/worker/typescript.js +2 -3
- package/dist/playground/worker/v.js +2 -2
- package/dist/playground/worker/wat.js +2 -3
- package/dist/playground/worker/zig.js +20 -6
- package/dist/playground/workerSession.d.ts +1 -28
- package/dist/playground/workerSession.js +1 -164
- package/package.json +30 -15
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { CONTAINER_WORKER_PROTOCOL, OWNER_CLEANUP_DEADLINE_MS, snapshotContainerArguments } from './protocol';
|
|
2
|
+
export function createContainerWorkerClient(options) {
|
|
3
|
+
const channel = crypto.randomUUID();
|
|
4
|
+
const worker = options.workerFactory?.() ??
|
|
5
|
+
new Worker(new URL('./owner.worker.ts', import.meta.url), {
|
|
6
|
+
type: 'module',
|
|
7
|
+
name: 'wasm-idle-container-owner'
|
|
8
|
+
});
|
|
9
|
+
const listeners = new Set();
|
|
10
|
+
const pending = new Map();
|
|
11
|
+
let nextId = 1, disposed = false, disposal;
|
|
12
|
+
let retirementError;
|
|
13
|
+
const cleanupDeadlineMs = options.cleanupDeadlineMs ?? OWNER_CLEANUP_DEADLINE_MS;
|
|
14
|
+
const abortError = () => new DOMException('Container operation cancelled', 'AbortError');
|
|
15
|
+
const post = (data) => worker.postMessage(data);
|
|
16
|
+
const retire = (error) => {
|
|
17
|
+
if (disposed)
|
|
18
|
+
return;
|
|
19
|
+
retirementError = error;
|
|
20
|
+
disposed = true;
|
|
21
|
+
worker.removeEventListener('message', message);
|
|
22
|
+
worker.removeEventListener('error', crash);
|
|
23
|
+
worker.removeEventListener('messageerror', crash);
|
|
24
|
+
worker.terminate();
|
|
25
|
+
for (const entry of pending.values()) {
|
|
26
|
+
entry.cleanup();
|
|
27
|
+
entry.reject(error);
|
|
28
|
+
}
|
|
29
|
+
pending.clear();
|
|
30
|
+
listeners.clear();
|
|
31
|
+
};
|
|
32
|
+
const crash = (event) => {
|
|
33
|
+
if (event.type === 'messageerror') {
|
|
34
|
+
retire(new Error('The container owner Worker message could not be decoded'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const failure = event;
|
|
38
|
+
retire(failure.error ?? new Error(failure.message || 'The container owner Worker crashed'));
|
|
39
|
+
};
|
|
40
|
+
const message = (event) => {
|
|
41
|
+
const data = event.data;
|
|
42
|
+
if (!data || data.version !== CONTAINER_WORKER_PROTOCOL || data.channel !== channel)
|
|
43
|
+
return;
|
|
44
|
+
if (data.type === 'event') {
|
|
45
|
+
for (const listener of listeners) {
|
|
46
|
+
try {
|
|
47
|
+
listener(data.event);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error('Container UI event handler failed', error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const entry = pending.get(data.id);
|
|
56
|
+
if (!entry)
|
|
57
|
+
return;
|
|
58
|
+
if (data.type === 'progress') {
|
|
59
|
+
try {
|
|
60
|
+
if (data.progress.kind === 'set')
|
|
61
|
+
entry.options?.progress?.set?.(data.progress.value, data.progress.stage);
|
|
62
|
+
else
|
|
63
|
+
entry.options?.progress?.report?.(data.progress.event);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Container UI progress handler failed', error);
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
entry.cleanup();
|
|
71
|
+
pending.delete(data.id);
|
|
72
|
+
if (data.type === 'error') {
|
|
73
|
+
const error = Object.assign(new Error(data.error.message), {
|
|
74
|
+
name: data.error.name,
|
|
75
|
+
code: data.error.code
|
|
76
|
+
});
|
|
77
|
+
entry.reject(error);
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
entry.resolve(data.value);
|
|
81
|
+
};
|
|
82
|
+
worker.addEventListener('message', message);
|
|
83
|
+
worker.addEventListener('error', crash);
|
|
84
|
+
worker.addEventListener('messageerror', crash);
|
|
85
|
+
const initialized = new Promise((resolve, reject) => {
|
|
86
|
+
const timer = setTimeout(() => retire(new Error('Container owner initialization timed out')), options.initializationTimeoutMs ?? 60_000);
|
|
87
|
+
pending.set(0, { resolve: () => resolve(), reject, cleanup: () => clearTimeout(timer) });
|
|
88
|
+
try {
|
|
89
|
+
post({
|
|
90
|
+
version: CONTAINER_WORKER_PROTOCOL,
|
|
91
|
+
channel,
|
|
92
|
+
id: 0,
|
|
93
|
+
type: 'init',
|
|
94
|
+
settings: snapshotContainerArguments({
|
|
95
|
+
runtimeAssets: options.runtimeAssets,
|
|
96
|
+
applicationRootURL: options.applicationRootURL,
|
|
97
|
+
workspaceId: options.workspaceId
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
retire(error);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
void initialized.catch(() => { });
|
|
106
|
+
const call = async (method, args = [], settings = {}) => {
|
|
107
|
+
if (disposed)
|
|
108
|
+
throw retirementError;
|
|
109
|
+
if (settings.signal?.aborted)
|
|
110
|
+
throw abortError();
|
|
111
|
+
// Snapshot before the first await; caller mutation never changes a pending request.
|
|
112
|
+
const values = snapshotContainerArguments(args);
|
|
113
|
+
await initialized;
|
|
114
|
+
if (disposed)
|
|
115
|
+
throw retirementError;
|
|
116
|
+
if (settings.signal?.aborted)
|
|
117
|
+
throw abortError();
|
|
118
|
+
const id = nextId++;
|
|
119
|
+
return await new Promise((resolve, reject) => {
|
|
120
|
+
const cancel = () => {
|
|
121
|
+
const entry = pending.get(id);
|
|
122
|
+
if (!entry || entry.cancelTimer)
|
|
123
|
+
return;
|
|
124
|
+
try {
|
|
125
|
+
post({ version: CONTAINER_WORKER_PROTOCOL, channel, id, type: 'cancel' });
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
retire(error);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
entry.cancelTimer = setTimeout(() => retire(new Error('Container owner did not finish cancellation before its cleanup deadline')), cleanupDeadlineMs);
|
|
132
|
+
};
|
|
133
|
+
pending.set(id, {
|
|
134
|
+
resolve: (value) => settings.signal?.aborted ? reject(abortError()) : resolve(value),
|
|
135
|
+
reject,
|
|
136
|
+
options: settings,
|
|
137
|
+
cleanup() {
|
|
138
|
+
settings.signal?.removeEventListener('abort', cancel);
|
|
139
|
+
const timer = pending.get(id)?.cancelTimer;
|
|
140
|
+
if (timer)
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
settings.signal?.addEventListener('abort', cancel, { once: true });
|
|
145
|
+
try {
|
|
146
|
+
post({
|
|
147
|
+
version: CONTAINER_WORKER_PROTOCOL,
|
|
148
|
+
channel,
|
|
149
|
+
id,
|
|
150
|
+
type: 'call',
|
|
151
|
+
method,
|
|
152
|
+
args: values
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const entry = pending.get(id);
|
|
157
|
+
entry?.cleanup();
|
|
158
|
+
pending.delete(id);
|
|
159
|
+
reject(error);
|
|
160
|
+
}
|
|
161
|
+
if (settings.signal?.aborted)
|
|
162
|
+
cancel();
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
call,
|
|
167
|
+
onEvent(listener) {
|
|
168
|
+
listeners.add(listener);
|
|
169
|
+
return () => listeners.delete(listener);
|
|
170
|
+
},
|
|
171
|
+
dispose() {
|
|
172
|
+
return (disposal ??= (async () => {
|
|
173
|
+
if (disposed)
|
|
174
|
+
return;
|
|
175
|
+
const timer = setTimeout(() => retire(new Error('Container owner disposal deadline expired')), cleanupDeadlineMs);
|
|
176
|
+
try {
|
|
177
|
+
await call('dispose');
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
/* Retiring the Worker is the final cleanup boundary. */
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
clearTimeout(timer);
|
|
184
|
+
retire(new Error('Container owner has been disposed'));
|
|
185
|
+
}
|
|
186
|
+
})());
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ContainerWorkspaceEntry } from './protocol';
|
|
2
|
+
export interface ExplorerEntry extends ContainerWorkspaceEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
parent: string;
|
|
5
|
+
implicit?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ExplorerModel {
|
|
8
|
+
readonly entries: ReadonlyMap<string, ExplorerEntry>;
|
|
9
|
+
readonly children: ReadonlyMap<string, readonly ExplorerEntry[]>;
|
|
10
|
+
}
|
|
11
|
+
export interface ExplorerRow {
|
|
12
|
+
entry: ExplorerEntry;
|
|
13
|
+
depth: number;
|
|
14
|
+
expanded: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const EXPLORER_PAGE_SIZE = 256;
|
|
17
|
+
/** Build directory entries without following symbolic links or changing POSIX path spelling. */
|
|
18
|
+
export declare function buildExplorerModel(entries: readonly ContainerWorkspaceEntry[]): ExplorerModel;
|
|
19
|
+
/** Missing or removed folders fall back to the nearest actual directory, then /workspace. */
|
|
20
|
+
export declare function resolveExplorerDirectory(model: ExplorerModel, requested: string): string;
|
|
21
|
+
export declare function explorerBreadcrumbs(directory: string): {
|
|
22
|
+
path: string;
|
|
23
|
+
name: string;
|
|
24
|
+
}[];
|
|
25
|
+
/** Flatten only expanded branches and retain a single page of rows, even in large workspaces. */
|
|
26
|
+
export declare function visibleExplorerRows(model: ExplorerModel, directory: string, expanded: ReadonlySet<string>, requestedOffset?: number): {
|
|
27
|
+
rows: ExplorerRow[];
|
|
28
|
+
total: number;
|
|
29
|
+
offset: number;
|
|
30
|
+
remaining: number;
|
|
31
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export const EXPLORER_PAGE_SIZE = 256;
|
|
2
|
+
function isRelativePath(path) {
|
|
3
|
+
return (path.length > 0 &&
|
|
4
|
+
path.length <= 4096 &&
|
|
5
|
+
!path.includes('\0') &&
|
|
6
|
+
path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'));
|
|
7
|
+
}
|
|
8
|
+
function parentPath(path) {
|
|
9
|
+
const separator = path.lastIndexOf('/');
|
|
10
|
+
return separator === -1 ? '' : path.slice(0, separator);
|
|
11
|
+
}
|
|
12
|
+
/** Build directory entries without following symbolic links or changing POSIX path spelling. */
|
|
13
|
+
export function buildExplorerModel(entries) {
|
|
14
|
+
const nodes = new Map([
|
|
15
|
+
['', { path: '', name: '/workspace', parent: '', type: 'directory', size: 0 }]
|
|
16
|
+
]);
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
if (!isRelativePath(entry.path) || nodes.has(entry.path))
|
|
19
|
+
continue;
|
|
20
|
+
nodes.set(entry.path, {
|
|
21
|
+
...entry,
|
|
22
|
+
name: entry.path.slice(entry.path.lastIndexOf('/') + 1),
|
|
23
|
+
parent: parentPath(entry.path)
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
for (const entry of [...nodes.values()]) {
|
|
27
|
+
let path = entry.parent;
|
|
28
|
+
while (path) {
|
|
29
|
+
if (!nodes.has(path))
|
|
30
|
+
nodes.set(path, {
|
|
31
|
+
path,
|
|
32
|
+
parent: parentPath(path),
|
|
33
|
+
name: path.slice(path.lastIndexOf('/') + 1),
|
|
34
|
+
type: 'directory',
|
|
35
|
+
size: 0,
|
|
36
|
+
implicit: true
|
|
37
|
+
});
|
|
38
|
+
path = parentPath(path);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const [path, entry] of nodes) {
|
|
42
|
+
let ancestor = entry.parent;
|
|
43
|
+
while (path) {
|
|
44
|
+
if (nodes.get(ancestor)?.type !== 'directory') {
|
|
45
|
+
nodes.delete(path);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (!ancestor)
|
|
49
|
+
break;
|
|
50
|
+
ancestor = parentPath(ancestor);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const children = new Map();
|
|
54
|
+
for (const entry of nodes.values()) {
|
|
55
|
+
if (!entry.path)
|
|
56
|
+
continue;
|
|
57
|
+
const siblings = children.get(entry.parent) ?? [];
|
|
58
|
+
siblings.push(entry);
|
|
59
|
+
children.set(entry.parent, siblings);
|
|
60
|
+
}
|
|
61
|
+
for (const siblings of children.values())
|
|
62
|
+
siblings.sort((a, b) => Number(b.type === 'directory') - Number(a.type === 'directory') ||
|
|
63
|
+
a.name.localeCompare(b.name, 'en', { numeric: true }) ||
|
|
64
|
+
a.path.localeCompare(b.path, 'en'));
|
|
65
|
+
return { entries: nodes, children };
|
|
66
|
+
}
|
|
67
|
+
/** Missing or removed folders fall back to the nearest actual directory, then /workspace. */
|
|
68
|
+
export function resolveExplorerDirectory(model, requested) {
|
|
69
|
+
let path = isRelativePath(requested) ? requested : '';
|
|
70
|
+
while (path && model.entries.get(path)?.type !== 'directory')
|
|
71
|
+
path = parentPath(path);
|
|
72
|
+
return path;
|
|
73
|
+
}
|
|
74
|
+
export function explorerBreadcrumbs(directory) {
|
|
75
|
+
const result = [{ path: '', name: '/workspace' }];
|
|
76
|
+
if (!isRelativePath(directory))
|
|
77
|
+
return result;
|
|
78
|
+
const parts = directory.split('/');
|
|
79
|
+
for (let index = 0; index < parts.length; index++)
|
|
80
|
+
result.push({ path: parts.slice(0, index + 1).join('/'), name: parts[index] });
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/** Flatten only expanded branches and retain a single page of rows, even in large workspaces. */
|
|
84
|
+
export function visibleExplorerRows(model, directory, expanded, requestedOffset = 0) {
|
|
85
|
+
const root = resolveExplorerDirectory(model, directory);
|
|
86
|
+
const visible = [];
|
|
87
|
+
const pending = [...(model.children.get(root) ?? [])]
|
|
88
|
+
.reverse()
|
|
89
|
+
.map((entry) => ({ entry, depth: 0 }));
|
|
90
|
+
while (pending.length) {
|
|
91
|
+
const row = pending.pop();
|
|
92
|
+
const isExpanded = row.entry.type === 'directory' && expanded.has(row.entry.path);
|
|
93
|
+
visible.push({ ...row, expanded: isExpanded });
|
|
94
|
+
if (isExpanded) {
|
|
95
|
+
const children = model.children.get(row.entry.path) ?? [];
|
|
96
|
+
for (let index = children.length - 1; index >= 0; index--)
|
|
97
|
+
pending.push({ entry: children[index], depth: row.depth + 1 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const lastPage = Math.max(0, Math.ceil(visible.length / EXPLORER_PAGE_SIZE) - 1) * EXPLORER_PAGE_SIZE;
|
|
101
|
+
const requested = Number.isSafeInteger(requestedOffset) && requestedOffset > 0
|
|
102
|
+
? Math.floor(requestedOffset / EXPLORER_PAGE_SIZE) * EXPLORER_PAGE_SIZE
|
|
103
|
+
: 0;
|
|
104
|
+
const offset = Math.min(requested, lastPage);
|
|
105
|
+
const rows = visible.slice(offset, offset + EXPLORER_PAGE_SIZE);
|
|
106
|
+
return {
|
|
107
|
+
rows,
|
|
108
|
+
total: visible.length,
|
|
109
|
+
offset,
|
|
110
|
+
remaining: visible.length - offset - rows.length
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|