@yugoo-design/dsh-design-studio-dsh 0.1.0-beta.3 → 0.1.0-beta.4
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 +11 -1
- package/lib/client-adapter.d.ts +2 -0
- package/lib/client-adapter.js +2 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +141 -60
- package/lib/page-client.js +6182 -440
- package/lib/page-rpc.d.ts +1 -1
- package/lib/page-rpc.js +9 -0
- package/lib/typert.remote-client.js +24 -0
- package/licenses/bundled-js/@floating-ui/core@1.8.0/LICENSE +20 -0
- package/licenses/bundled-js/@floating-ui/dom@1.8.0/LICENSE +20 -0
- package/licenses/bundled-js/@floating-ui/react-dom@2.1.9/LICENSE +20 -0
- package/licenses/bundled-js/@floating-ui/utils@0.2.12/LICENSE +20 -0
- package/licenses/bundled-js/@radix-ui/number@1.1.3/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/primitive@1.1.7/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-collection@1.1.15/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-compose-refs@1.1.5/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-context@1.2.2/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-direction@1.1.4/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-dismissable-layer@1.1.19/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-focus-guards@1.1.6/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-focus-scope@1.1.16/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-id@1.1.4/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-popper@1.3.7/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-portal@1.1.17/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-presence@1.1.10/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-primitive@2.1.10/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-select@2.3.7/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-slot@1.3.3/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-use-callback-ref@1.1.4/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-use-controllable-state@1.2.6/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-use-effect-event@0.0.5/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-use-layout-effect@1.1.4/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-use-previous@1.1.4/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-use-size@1.1.4/LICENSE +21 -0
- package/licenses/bundled-js/@radix-ui/react-visually-hidden@1.2.11/LICENSE +21 -0
- package/licenses/bundled-js/aria-hidden@1.2.6/LICENSE +21 -0
- package/licenses/bundled-js/get-nonce@1.0.1/LICENSE +21 -0
- package/licenses/bundled-js/lucide-react@0.562.0/LICENSE +39 -0
- package/licenses/bundled-js/react-remove-scroll-bar@2.3.8/ATTRIBUTION.md +19 -0
- package/licenses/bundled-js/react-remove-scroll-bar@2.3.8/UPSTREAM-LICENSE-2.3.7.txt +21 -0
- package/licenses/bundled-js/react-remove-scroll@2.7.2/LICENSE +21 -0
- package/licenses/bundled-js/react-style-singleton@2.2.3/LICENSE +21 -0
- package/licenses/bundled-js/tslib@2.8.1/LICENSE.txt +12 -0
- package/licenses/bundled-js/use-callback-ref@1.3.3/LICENSE +21 -0
- package/licenses/bundled-js/use-sidecar@1.1.3/LICENSE +21 -0
- package/licenses/dsh-distribution.json +725 -10
- package/licenses/first-party/dsh-design-panel/LICENSE +202 -0
- package/licenses/first-party/dsh-design-panel/NOTICE +13 -0
- package/licenses/first-party/dsh-design-studio-ui/LICENSE +202 -0
- package/licenses/first-party/dsh-design-studio-ui/NOTICE +13 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ explicit `kitSources` mode remains available with its strict startup validation.
|
|
|
9
9
|
|
|
10
10
|
`dsh-design-studio-dsh` is the DSH-only adapter around the host-neutral read-only Studio application and shared `StudioWorkspace`. It owns the session-free `studioManagement` Typert Remote, a root-scoped Settings launcher, and the plugin-owned `/design-studio/` full-page route. It does not change the Agent loop, Design Project selection, Design Files, or G/R/A services, and it persists nothing: Studio 1.0 has no durable record, no working copies, and no mutation endpoint.
|
|
11
11
|
|
|
12
|
-
The Remote exposes exactly
|
|
12
|
+
The Remote exposes exactly six read methods: `workspace`, `readKitWorkspace` (exact `name@version` pin required; a diverging response identity diagnoses), `readKitFiles` and `readKitFile` (the same exact Kit pin), `readBundleWorkspace`, and `readBundleFile`. Kit file inspection includes `kit.yaml`, `mindset.md` and other authored files beneath the selected Kit root, excluding `.git` and `node_modules`; the text viewer accepts regular UTF-8 files up to 1 MiB, with bounded tree traversal. Missing sources, forbidden paths, binary content and oversized files diagnose rather than falling back to another version. The catalog snapshot projects `ctx.design.listKits()`. Bundle source reads run through `ctx.fs`: the `kitSources` config lists installed content packages (`packageName`, resolved like the Design Core kit-loader) or absolute directories (`root`); every path resolves through the filesystem capability and must stay canonically contained in a configured root, so traversal and symlink escapes never read outside the authored trees and non-UTF-8 content diagnoses as binary. Invalid or ambiguous configuration — a missing root, a root without `mindset.md`, a relative path, a duplicate root, or one Kit/Bundle name declared by two roots — fails loud at startup. Without `kitSources` the catalog stays readable and Bundle reads diagnose `studio.source_not_configured`. Disposal rejects new work and drains admitted calls.
|
|
13
13
|
|
|
14
14
|
Strict codecs follow the current Typert `create(): TypertSchema` factory contract.
|
|
15
15
|
The existing Zod validators supply that parser capability; both Host registration
|
|
@@ -26,3 +26,13 @@ dependencies or remaining static/dynamic module imports. Its page-route test als
|
|
|
26
26
|
checks the emitted JavaScript with the shared loader analyzer and exercises real
|
|
27
27
|
bundler rejection cases. These checks establish browser module closure; rendered
|
|
28
28
|
Host interaction remains a separate integration check.
|
|
29
|
+
|
|
30
|
+
Only the standalone browser page injects `PanelSelect` and
|
|
31
|
+
`PANEL_SELECT_MENU_CSS` from the isolated public `dsh-design-panel/select` into
|
|
32
|
+
Studio's optional neutral choice-control port. This is a shared control and popup
|
|
33
|
+
style, not the broad provider API or Panel/DSL workspace renderer. The server,
|
|
34
|
+
Settings launcher and transports retain their existing dependency boundary.
|
|
35
|
+
The menu snapshots Studio's semantic
|
|
36
|
+
tokens and fonts; inside an open native dialog it stays within that dialog's top
|
|
37
|
+
layer, otherwise it uses the owning document body. These source changes do not
|
|
38
|
+
establish installed Web/Desktop interaction acceptance.
|
package/lib/client-adapter.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ type DshRemoteResult<T> = {
|
|
|
32
32
|
interface DshStudioRemote {
|
|
33
33
|
workspace(query: string): Promise<DshRemoteResult<Awaited<ReturnType<StudioManagementClient['workspace']>>>>;
|
|
34
34
|
readKitWorkspace(name: string, version: string): Promise<DshRemoteResult<Awaited<ReturnType<StudioManagementClient['readKitWorkspace']>>>>;
|
|
35
|
+
readKitFiles(name: string, version: string): Promise<DshRemoteResult<Awaited<ReturnType<NonNullable<StudioManagementClient['readKitFiles']>>>>>;
|
|
36
|
+
readKitFile(name: string, version: string, path: string): Promise<DshRemoteResult<Awaited<ReturnType<NonNullable<StudioManagementClient['readKitFile']>>>>>;
|
|
35
37
|
readBundleWorkspace(name: string, version?: string): Promise<DshRemoteResult<Awaited<ReturnType<StudioManagementClient['readBundleWorkspace']>>>>;
|
|
36
38
|
readBundleFile(name: string, path: string, version?: string): Promise<DshRemoteResult<Awaited<ReturnType<StudioManagementClient['readBundleFile']>>>>;
|
|
37
39
|
}
|
package/lib/client-adapter.js
CHANGED
|
@@ -45,6 +45,8 @@ function createDshStudioClient(remote) {
|
|
|
45
45
|
})
|
|
46
46
|
});
|
|
47
47
|
},
|
|
48
|
+
readKitFiles: (input) => unwrap("readKitFiles", remote.readKitFiles(input.name, input.version)),
|
|
49
|
+
readKitFile: (input) => unwrap("readKitFile", remote.readKitFile(input.name, input.version, input.path)),
|
|
48
50
|
readBundleWorkspace: (name, version) => unwrap("readBundleWorkspace", remote.readBundleWorkspace(name, version)),
|
|
49
51
|
readBundleFile: (input) => unwrap("readBundleFile", remote.readBundleFile(input.name, input.path, input.version))
|
|
50
52
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ declare class DshStudioManagementService extends TypertRemoteService {
|
|
|
51
51
|
private readonly config;
|
|
52
52
|
private management?;
|
|
53
53
|
private bundles?;
|
|
54
|
+
private readonly configuredKitRoots;
|
|
54
55
|
private accepting;
|
|
55
56
|
private readonly inFlight;
|
|
56
57
|
/**
|
|
@@ -67,6 +68,13 @@ declare class DshStudioManagementService extends TypertRemoteService {
|
|
|
67
68
|
workspace(query: string): Promise<StudioWorkspaceView>;
|
|
68
69
|
/** Read one exact installed Kit version's workspace and verify the returned identity matches. */
|
|
69
70
|
readKitWorkspace(name: string, version: string): Promise<StudioKitWorkspaceResult>;
|
|
71
|
+
/** List all authored files of one exact installed Kit, including kit.yaml. */
|
|
72
|
+
readKitFiles(name: string, version: string): Promise<readonly string[]>;
|
|
73
|
+
/** Read one root-confined Kit file as bounded UTF-8 text. */
|
|
74
|
+
readKitFile(name: string, version: string, path: string): Promise<{
|
|
75
|
+
readonly content: string;
|
|
76
|
+
}>;
|
|
77
|
+
private kitFiles;
|
|
70
78
|
/** Read one Bundle's read-only source workspace. */
|
|
71
79
|
readBundleWorkspace(name: string, version?: string): Promise<StudioBundleWorkspaceResult>;
|
|
72
80
|
/** Read one complete Bundle file byte-exactly as UTF-8 text. */
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,104 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
-
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
3
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
4
|
+
import { DesignKitVersion } from "@yugoo-design/dsh-design-core";
|
|
5
5
|
import { StudioBinaryContentError, StudioBundleSourceService, StudioManagementService, loadStudioKit, snapshotKit } from "@yugoo-design/dsh-design-studio";
|
|
6
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
7
|
+
//#region src/kit-files.ts
|
|
8
|
+
/** Installed source reads share the same DSH filesystem containment boundary. */
|
|
9
|
+
const MAX_TEXT_BYTES = 1048576;
|
|
10
|
+
const excludedDirectory = (name) => /^(?:\.git|node_modules)$/iu.test(name);
|
|
11
|
+
/** A code survives the Remote gateway while the message remains useful in the viewer. */
|
|
12
|
+
function kitFileError(code, message) {
|
|
13
|
+
return Object.assign(/* @__PURE__ */ new Error(`${code}: ${message}`), { code });
|
|
14
|
+
}
|
|
15
|
+
/** One read's installed resource root is the complete filesystem boundary, not its catalog id. */
|
|
16
|
+
async function openInstalledSourceFiles(fs, root, kind = "Bundle") {
|
|
17
|
+
if (!isAbsolute(root)) throw new Error(`design studio dsh: installed ${kind} resource root must be absolute`);
|
|
18
|
+
const base = await fs.resolve(root);
|
|
19
|
+
if ((await fs.stat(base))?.type !== "directory") throw new Error(`design studio dsh: installed ${kind} resource root is not a directory`);
|
|
20
|
+
const target = async (path) => {
|
|
21
|
+
const resolved = await fs.resolve(path);
|
|
22
|
+
if (!fs.contains(base, resolved)) {
|
|
23
|
+
if (kind === "Kit") throw kitFileError("kit_file.path_forbidden", "Path escapes installed Kit resource root.");
|
|
24
|
+
throw new Error(`design studio dsh: path escapes installed ${kind} resource root`);
|
|
25
|
+
}
|
|
26
|
+
return resolved;
|
|
27
|
+
};
|
|
28
|
+
const code = (error) => typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
29
|
+
return {
|
|
30
|
+
async readFile(path) {
|
|
31
|
+
if (typeof path !== "string" || /[\\:\u0000-\u001f\u007f]/u.test(path) || path.split("/").some((part) => !part || part === "." || part === ".." || excludedDirectory(part))) throw kitFileError("kit_file.path_forbidden", "Invalid Kit file path.");
|
|
32
|
+
try {
|
|
33
|
+
const resolved = await target(join(root, path));
|
|
34
|
+
const info = await fs.stat(resolved);
|
|
35
|
+
if (info?.type !== "file") throw kitFileError("kit_file.missing", "Kit file not found or not a regular file.");
|
|
36
|
+
if ((info.size ?? 0) > MAX_TEXT_BYTES) throw kitFileError("kit_file.too_large", "This file exceeds the 1 MiB text viewer limit.");
|
|
37
|
+
const bytes = await fs.readBytes(resolved, void 0, MAX_TEXT_BYTES);
|
|
38
|
+
let content;
|
|
39
|
+
try {
|
|
40
|
+
content = new TextDecoder("utf-8", {
|
|
41
|
+
fatal: true,
|
|
42
|
+
ignoreBOM: true
|
|
43
|
+
}).decode(bytes);
|
|
44
|
+
} catch {
|
|
45
|
+
throw kitFileError("kit_file.binary", "Kit file is binary or invalid UTF-8 text.");
|
|
46
|
+
}
|
|
47
|
+
if (content.includes("\0")) throw kitFileError("kit_file.binary", "Kit file is binary, not UTF-8 text.");
|
|
48
|
+
return { content };
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (code(error) === "FS_TOO_LARGE") throw kitFileError("kit_file.too_large", "This file exceeds the 1 MiB text viewer limit.");
|
|
51
|
+
if (code(error) === "FS_NOT_FOUND") throw kitFileError("kit_file.missing", "Kit file not found.");
|
|
52
|
+
if (code(error) === "FS_NOT_TEXT") throw kitFileError("kit_file.binary", "Kit file is binary or invalid UTF-8 text.");
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
listKitRoots: async () => [],
|
|
57
|
+
async readText(path) {
|
|
58
|
+
try {
|
|
59
|
+
return await fs.readText(await target(path));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (code(error) === "FS_NOT_FOUND") return void 0;
|
|
62
|
+
if (code(error) === "FS_NOT_TEXT") throw new StudioBinaryContentError(path);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
async listDir(path) {
|
|
67
|
+
try {
|
|
68
|
+
return (await fs.listDir(await target(path))).map((entry) => entry.name);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (code(error) === "FS_NOT_FOUND" || code(error) === "FS_NOT_DIRECTORY") return [];
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
async isDirectory(path) {
|
|
75
|
+
try {
|
|
76
|
+
return (await fs.stat(await target(path)))?.type === "directory";
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (code(error) === "FS_NOT_FOUND") return false;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
async listFiles(path) {
|
|
83
|
+
const files = [];
|
|
84
|
+
let visited = 0;
|
|
85
|
+
const walk = async (directory, prefix, depth) => {
|
|
86
|
+
if (depth > 32) throw new Error(`design studio dsh: ${kind} file tree exceeds depth limit`);
|
|
87
|
+
for (const entry of await fs.listDir(directory)) {
|
|
88
|
+
if (++visited > 2e4) throw new Error(`design studio dsh: ${kind} file tree exceeds entry limit`);
|
|
89
|
+
if (kind === "Kit" && excludedDirectory(entry.name)) continue;
|
|
90
|
+
if (!fs.contains(base, entry.target)) continue;
|
|
91
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
92
|
+
if (entry.type === "directory") await walk(entry.target, relative, depth + 1);
|
|
93
|
+
else if (entry.type === "file") files.push(relative);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
await walk(await target(path), "", 0);
|
|
97
|
+
return files.sort();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
6
102
|
//#region src/kit-sources.ts
|
|
7
103
|
/** Read-only Kit source access over the DSH host filesystem capability. */
|
|
8
104
|
/**
|
|
@@ -132,6 +228,7 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
132
228
|
config;
|
|
133
229
|
management;
|
|
134
230
|
bundles;
|
|
231
|
+
configuredKitRoots = /* @__PURE__ */ new Map();
|
|
135
232
|
accepting = true;
|
|
136
233
|
inFlight = /* @__PURE__ */ new Set();
|
|
137
234
|
/**
|
|
@@ -150,6 +247,7 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
150
247
|
await Promise.allSettled([...this.inFlight]);
|
|
151
248
|
this.management = void 0;
|
|
152
249
|
this.bundles = void 0;
|
|
250
|
+
this.configuredKitRoots.clear();
|
|
153
251
|
}, "design-studio-dsh.readonly");
|
|
154
252
|
const catalog = Object.freeze({ snapshot: async () => Object.freeze({
|
|
155
253
|
kits: Object.freeze(this.host.design.listKits().map(snapshotKit).sort((left, right) => left.name.localeCompare(right.name) || left.version.localeCompare(right.version))),
|
|
@@ -159,9 +257,9 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
159
257
|
const configs = this.config.kitSources ?? [];
|
|
160
258
|
if (configs.length === 0) return;
|
|
161
259
|
const roots = configs.map((entry) => resolveKitSourceRoot(entry, this.ctx.baseUrl));
|
|
162
|
-
this.bundles = await this.openBundleService(this.host.design.listKits(), roots);
|
|
260
|
+
this.bundles = await this.openBundleService(this.host.design.listKits(), roots, true);
|
|
163
261
|
}
|
|
164
|
-
async openBundleService(kits, roots) {
|
|
262
|
+
async openBundleService(kits, roots, recordKitRoots = false) {
|
|
165
263
|
const catalog = { snapshot: async () => ({ kits: kits.map(snapshotKit) }) };
|
|
166
264
|
const source = await openDshStudioBundleSource(this.host.fs, roots);
|
|
167
265
|
const kitNames = /* @__PURE__ */ new Map();
|
|
@@ -176,6 +274,7 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
176
274
|
const previousKit = kitNames.get(kit.name);
|
|
177
275
|
if (previousKit !== void 0) throw new Error(`design studio dsh: Kit ${JSON.stringify(kit.name)} is declared by two kitSources roots: ${JSON.stringify(previousKit)} and ${JSON.stringify(root)}`);
|
|
178
276
|
kitNames.set(kit.name, root);
|
|
277
|
+
if (recordKitRoots) this.configuredKitRoots.set(`${kit.name}\0${kit.version}`, root);
|
|
179
278
|
for (const skill of kit.skills) {
|
|
180
279
|
const previousBundle = bundleNames.get(skill.name);
|
|
181
280
|
if (previousBundle !== void 0) throw new Error(`design studio dsh: Bundle ${JSON.stringify(skill.name)} is declared by two kitSources roots: ${JSON.stringify(previousBundle)} and ${JSON.stringify(root)}`);
|
|
@@ -213,7 +312,7 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
213
312
|
if (!selected) return void 0;
|
|
214
313
|
const root = this.host.design.resolveBundleRoot(pin) ?? selected.definition.resourceRoot;
|
|
215
314
|
if (!root) return void 0;
|
|
216
|
-
files = await
|
|
315
|
+
files = await openInstalledSourceFiles(this.host.fs, root);
|
|
217
316
|
return root;
|
|
218
317
|
},
|
|
219
318
|
readText: (path) => requireFiles().readText(path),
|
|
@@ -245,6 +344,42 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
245
344
|
return result;
|
|
246
345
|
});
|
|
247
346
|
}
|
|
347
|
+
/** List all authored files of one exact installed Kit, including kit.yaml. */
|
|
348
|
+
readKitFiles(name, version) {
|
|
349
|
+
return this.run(async () => {
|
|
350
|
+
const { root, files } = await this.kitFiles(name, version);
|
|
351
|
+
return files.listFiles(root);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/** Read one root-confined Kit file as bounded UTF-8 text. */
|
|
355
|
+
readKitFile(name, version, path) {
|
|
356
|
+
return this.run(async () => (await this.kitFiles(name, version)).files.readFile(path));
|
|
357
|
+
}
|
|
358
|
+
async kitFiles(name, version) {
|
|
359
|
+
if (typeof name !== "string" || !name || typeof version !== "string" || !version) throw kitFileError("workspace.version_missing", "An exact Kit name and version are required.");
|
|
360
|
+
let exactVersion;
|
|
361
|
+
try {
|
|
362
|
+
exactVersion = DesignKitVersion(version);
|
|
363
|
+
} catch {
|
|
364
|
+
throw kitFileError("workspace.version_missing", "An exact Kit major.minor version is required.");
|
|
365
|
+
}
|
|
366
|
+
const kit = typeof this.host.design.resolveKit === "function" ? this.host.design.resolveKit({
|
|
367
|
+
kit: name,
|
|
368
|
+
version: exactVersion
|
|
369
|
+
}) : this.host.design.listKits().find((candidate) => candidate.name === name && candidate.version === version);
|
|
370
|
+
if (!kit || kit.name !== name || kit.version !== version) throw kitFileError("workspace.version_missing", `Kit ${JSON.stringify(name)}@${JSON.stringify(version)} is not installed.`);
|
|
371
|
+
const configured = this.configuredKitRoots.get(`${name}\0${version}`);
|
|
372
|
+
const root = this.config.followInstalledSources ? kit.sourceRoot ?? configured : configured;
|
|
373
|
+
if (!root) throw kitFileError("studio.source_not_configured", `No source root is configured for Kit ${JSON.stringify(name)}@${JSON.stringify(version)}.`);
|
|
374
|
+
try {
|
|
375
|
+
return {
|
|
376
|
+
root,
|
|
377
|
+
files: await openInstalledSourceFiles(this.host.fs, root, "Kit")
|
|
378
|
+
};
|
|
379
|
+
} catch (error) {
|
|
380
|
+
throw kitFileError("studio.source_unavailable", `Kit source is unavailable; the process may retain last-good content: ${String(error)}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
248
383
|
/** Read one Bundle's read-only source workspace. */
|
|
249
384
|
readBundleWorkspace(name, version) {
|
|
250
385
|
return this.run(async () => {
|
|
@@ -289,62 +424,6 @@ var DshStudioManagementService = class extends TypertRemoteService {
|
|
|
289
424
|
return result;
|
|
290
425
|
}
|
|
291
426
|
};
|
|
292
|
-
/** One read's installed resource root is the complete filesystem boundary, not its catalog id. */
|
|
293
|
-
async function openInstalledBundleFiles(fs, root) {
|
|
294
|
-
if (!isAbsolute(root)) throw new Error("design studio dsh: installed Bundle resource root must be absolute");
|
|
295
|
-
const base = await fs.resolve(root);
|
|
296
|
-
if ((await fs.stat(base))?.type !== "directory") throw new Error("design studio dsh: installed Bundle resource root is not a directory");
|
|
297
|
-
const target = async (path) => {
|
|
298
|
-
const resolved = await fs.resolve(path);
|
|
299
|
-
if (!fs.contains(base, resolved)) throw new Error("design studio dsh: path escapes installed Bundle resource root");
|
|
300
|
-
return resolved;
|
|
301
|
-
};
|
|
302
|
-
const code = (error) => typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
303
|
-
return {
|
|
304
|
-
listKitRoots: async () => [],
|
|
305
|
-
async readText(path) {
|
|
306
|
-
try {
|
|
307
|
-
return await fs.readText(await target(path));
|
|
308
|
-
} catch (error) {
|
|
309
|
-
if (code(error) === "FS_NOT_FOUND") return void 0;
|
|
310
|
-
if (code(error) === "FS_NOT_TEXT") throw new StudioBinaryContentError(path);
|
|
311
|
-
throw error;
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
async listDir(path) {
|
|
315
|
-
try {
|
|
316
|
-
return (await fs.listDir(await target(path))).map((entry) => entry.name);
|
|
317
|
-
} catch (error) {
|
|
318
|
-
if (code(error) === "FS_NOT_FOUND" || code(error) === "FS_NOT_DIRECTORY") return [];
|
|
319
|
-
throw error;
|
|
320
|
-
}
|
|
321
|
-
},
|
|
322
|
-
async isDirectory(path) {
|
|
323
|
-
try {
|
|
324
|
-
return (await fs.stat(await target(path)))?.type === "directory";
|
|
325
|
-
} catch (error) {
|
|
326
|
-
if (code(error) === "FS_NOT_FOUND") return false;
|
|
327
|
-
throw error;
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
async listFiles(path) {
|
|
331
|
-
const files = [];
|
|
332
|
-
let visited = 0;
|
|
333
|
-
const walk = async (directory, prefix, depth) => {
|
|
334
|
-
if (depth > 32) throw new Error("design studio dsh: Bundle file tree exceeds depth limit");
|
|
335
|
-
for (const entry of await fs.listDir(directory)) {
|
|
336
|
-
if (++visited > 2e4) throw new Error("design studio dsh: Bundle file tree exceeds entry limit");
|
|
337
|
-
if (!fs.contains(base, entry.target)) continue;
|
|
338
|
-
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
339
|
-
if (entry.type === "directory") await walk(entry.target, relative, depth + 1);
|
|
340
|
-
else if (entry.type === "file") files.push(relative);
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
await walk(await target(path), "", 0);
|
|
344
|
-
return files.sort();
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
427
|
function sourceNotConfigured(name) {
|
|
349
428
|
return Object.freeze({ diagnostics: Object.freeze([Object.freeze({
|
|
350
429
|
code: "studio.source_not_configured",
|
|
@@ -372,6 +451,8 @@ function markRemote(name, method) {
|
|
|
372
451
|
}
|
|
373
452
|
markRemote("workspace", DshStudioManagementService.prototype.workspace);
|
|
374
453
|
markRemote("readKitWorkspace", DshStudioManagementService.prototype.readKitWorkspace);
|
|
454
|
+
markRemote("readKitFiles", DshStudioManagementService.prototype.readKitFiles);
|
|
455
|
+
markRemote("readKitFile", DshStudioManagementService.prototype.readKitFile);
|
|
375
456
|
markRemote("readBundleWorkspace", DshStudioManagementService.prototype.readBundleWorkspace);
|
|
376
457
|
markRemote("readBundleFile", DshStudioManagementService.prototype.readBundleFile);
|
|
377
458
|
//#endregion
|