@joycostudio/studio 0.1.3 → 0.1.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.
@@ -0,0 +1,314 @@
1
+ // src/discovery/generate.ts
2
+ import { existsSync as existsSync2, readFileSync, readdirSync, realpathSync } from "fs";
3
+ import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
4
+ import path3 from "path";
5
+ import ts from "typescript";
6
+
7
+ // src/runtime/files.ts
8
+ import { createHash, randomUUID } from "crypto";
9
+ import { mkdir, readFile, realpath, rename, rm, stat } from "fs/promises";
10
+ import { writeFile } from "fs/promises";
11
+ import path from "path";
12
+ var inside = (root, target) => {
13
+ const relative = path.relative(root, target);
14
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
15
+ };
16
+ var exists = (target) => stat(target).then(
17
+ () => true,
18
+ () => false
19
+ );
20
+ async function resolveToolFile(root, file) {
21
+ const fail2 = (reason) => new Error(`[studio] tool file "${file}" ${reason}`);
22
+ if (path.isAbsolute(file)) throw fail2("must be relative to the project root");
23
+ if (path.extname(file) !== ".json") throw fail2("must be a .json file");
24
+ const absolute = path.resolve(root, file);
25
+ if (!inside(root, absolute)) throw fail2("escapes the project root");
26
+ let probe = absolute;
27
+ while (!await exists(probe)) probe = path.dirname(probe);
28
+ if (probe !== root && !inside(await realpath(root), await realpath(probe))) throw fail2("escapes the project root");
29
+ return absolute;
30
+ }
31
+ var hashOf = (content) => createHash("sha256").update(content).digest("hex").slice(0, 16);
32
+ var serialize = (value) => `${JSON.stringify(value, null, 2)}
33
+ `;
34
+ async function readDisk(file) {
35
+ try {
36
+ const content = await readFile(file, "utf8");
37
+ return { content, hash: hashOf(content) };
38
+ } catch (cause) {
39
+ if (cause.code === "ENOENT") return null;
40
+ throw cause;
41
+ }
42
+ }
43
+ async function writeAtomic(file, content) {
44
+ await mkdir(path.dirname(file), { recursive: true });
45
+ const temp = `${file}.${randomUUID()}.tmp`;
46
+ try {
47
+ await writeFile(temp, content, "utf8");
48
+ await rename(temp, file);
49
+ } catch (cause) {
50
+ await rm(temp, { force: true });
51
+ throw cause;
52
+ }
53
+ }
54
+
55
+ // src/discovery/settings.ts
56
+ import { existsSync } from "fs";
57
+ import path2 from "path";
58
+ import { createJiti } from "jiti";
59
+ var CANDIDATES = ["studio.config.ts", "studio.config.mts", "studio.config.js", "studio.config.mjs"];
60
+ function importer(root) {
61
+ return createJiti(import.meta.url, {
62
+ moduleCache: false,
63
+ fsCache: false,
64
+ tryNative: false,
65
+ tsconfigPaths: existsSync(path2.join(root, "tsconfig.json")) ? path2.join(root, "tsconfig.json") : false
66
+ });
67
+ }
68
+ async function readSettings(cwd, explicit) {
69
+ const file = explicit ? path2.resolve(cwd, explicit) : CANDIDATES.map((name) => path2.join(cwd, name)).find(existsSync);
70
+ if (explicit && !existsSync(file)) throw new Error(`[studio] no ${explicit} found in ${cwd}`);
71
+ const root = file ? path2.dirname(file) : path2.resolve(cwd);
72
+ const config = file ? await importer(root).import(file, { default: true }) : {};
73
+ if (!config || typeof config !== "object" || config.tools !== void 0 && !Array.isArray(config.tools))
74
+ throw new Error("[studio] config must default-export defineConfig({ ... })");
75
+ if (config.tools !== void 0 && config.toolsDir !== void 0)
76
+ throw new Error("[studio] Use either tools (manual registration) or toolsDir (discovery), not both.");
77
+ return { config, root, file };
78
+ }
79
+
80
+ // src/discovery/generate.ts
81
+ var slash = (value) => value.split(path3.sep).join("/");
82
+ var relativeImport = (from, to) => {
83
+ const relative = slash(path3.relative(path3.dirname(from), to)).replace(/\.(tsx?|jsx?|mts|mjs)$/, "");
84
+ return /^\.\.?\//.test(relative) ? relative : `./${relative}`;
85
+ };
86
+ var inside2 = (root, file) => {
87
+ const relative = path3.relative(root, file);
88
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative);
89
+ };
90
+ var fail = (file, message) => {
91
+ throw new Error(`[studio] ${file}: ${message}`);
92
+ };
93
+ var HEADER = "// Generated by Studio. Do not edit.\n";
94
+ var DiscoveryInputError = class extends Error {
95
+ constructor(cause, inputs) {
96
+ super(cause instanceof Error ? cause.message : String(cause), { cause });
97
+ this.inputs = inputs;
98
+ }
99
+ inputs;
100
+ };
101
+ function shape(value) {
102
+ if (value === null) return "null";
103
+ if (Array.isArray(value)) return `[${[...new Set(value.map(shape))].sort().join("|")}]`;
104
+ if (typeof value === "object")
105
+ return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${shape(item)}`).join(",")}}`;
106
+ return typeof value;
107
+ }
108
+ function declaration(file) {
109
+ const source = ts.createSourceFile(file, readFileSync(file, "utf8"), ts.ScriptTarget.Latest, true);
110
+ const exported = source.statements.find(ts.isExportAssignment);
111
+ const call = exported?.expression;
112
+ if (!call || !ts.isCallExpression(call) || !ts.isIdentifier(call.expression) || call.expression.text !== "defineTool")
113
+ return fail(file, "default-export defineTool({ id, controls }) directly so discovery can inspect it.");
114
+ const object = call.arguments[0];
115
+ if (!object || !ts.isObjectLiteralExpression(object)) return fail(file, "defineTool requires an object literal.");
116
+ const properties = /* @__PURE__ */ new Map();
117
+ for (const member of object.properties) {
118
+ if (ts.isSpreadAssignment(member) || !member.name || ts.isComputedPropertyName(member.name))
119
+ return fail(file, "spread/computed tool options are not supported; put dynamic logic inside controls.");
120
+ const key = member.name.getText(source).replace(/^['"]|['"]$/g, "");
121
+ if (properties.has(key)) return fail(file, `duplicate option ${key}`);
122
+ properties.set(key, member);
123
+ }
124
+ const literal = (key) => {
125
+ const member = properties.get(key);
126
+ if (!member) return void 0;
127
+ if (!ts.isPropertyAssignment(member) || !ts.isStringLiteral(member.initializer))
128
+ return fail(file, `${key} must be a static string literal.`);
129
+ return member.initializer.text;
130
+ };
131
+ const id = literal("id");
132
+ if (!id || !/^[a-z0-9][a-z0-9-]*$/.test(id)) return fail(file, "id must be an explicit kebab-case string.");
133
+ if (properties.has("file") || properties.has("initial"))
134
+ return fail(file, "discovered tools use config instead of file/initial; omit config for adjacent config.json.");
135
+ const configurable = properties.has("controls");
136
+ const config = literal("config");
137
+ if (config !== void 0 && !configurable) return fail(file, "config requires controls.");
138
+ return { id, config, configurable, preview: literal("preview"), source };
139
+ }
140
+ function pageRoute(app, file, explicit) {
141
+ if (explicit) return explicit;
142
+ const directory = path3.dirname(file);
143
+ if (!app || !inside2(app, directory)) return fail(file, "tools outside the app tree require an explicit preview URL.");
144
+ if (!["tsx", "ts", "jsx", "js"].some((ext) => existsSync2(path3.join(directory, `page.${ext}`))))
145
+ return fail(file, "add an adjacent page.tsx or specify preview explicitly.");
146
+ const segments = slash(path3.relative(app, directory)).split("/").filter(Boolean);
147
+ if (segments.some((part) => part.includes("[") || part.startsWith("@") || /^\(\./.test(part) || part.startsWith("_")))
148
+ return fail(file, "dynamic, parallel, intercepted, and private routes require an explicit preview URL.");
149
+ return "/" + segments.filter((part) => !/^\(.+\)$/.test(part)).join("/");
150
+ }
151
+ function walk(directory) {
152
+ return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
153
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) return [];
154
+ const file = path3.join(directory, entry.name);
155
+ return entry.isDirectory() ? walk(file) : entry.name === "studio.tool.ts" ? [file] : [];
156
+ }).sort();
157
+ }
158
+ function compilerConfig(root, dependencies) {
159
+ const configFile = path3.join(root, "tsconfig.json");
160
+ if (!existsSync2(configFile)) return fail(root, "discovery requires tsconfig.json.");
161
+ const host = {
162
+ ...ts.sys,
163
+ readDirectory: () => [],
164
+ readFile(file) {
165
+ dependencies.add(path3.resolve(file));
166
+ return ts.sys.readFile(file);
167
+ },
168
+ onUnRecoverableConfigFileDiagnostic(error) {
169
+ throw new Error(ts.flattenDiagnosticMessageText(error.messageText, "\n"));
170
+ }
171
+ };
172
+ const parsed = ts.getParsedCommandLineOfConfigFile(configFile, { resolveJsonModule: true }, host);
173
+ if (!parsed) return fail(configFile, "could not read TypeScript settings.");
174
+ return parsed.options;
175
+ }
176
+ async function writeChanged(file, content) {
177
+ if (await readFile2(file, "utf8").catch(() => "") === content) return;
178
+ await writeAtomic(file, content);
179
+ }
180
+ async function generate(cwd, explicit, publish = true, previous) {
181
+ const { root, config, file: settings } = await readSettings(cwd, explicit);
182
+ if (config.tools !== void 0) return void 0;
183
+ const app = ["app", "src/app"].map((dir) => path3.join(root, dir)).find(existsSync2);
184
+ const toolsDir = config.toolsDir === void 0 ? app : path3.resolve(root, config.toolsDir);
185
+ if (!toolsDir || !existsSync2(toolsDir))
186
+ return fail(root, "no tools directory found; create app/ or configure toolsDir.");
187
+ if (!inside2(realpathSync(root), realpathSync(toolsDir)))
188
+ return fail(toolsDir, "toolsDir must stay inside the project.");
189
+ const sources = new Set(walk(toolsDir));
190
+ const dependencies = /* @__PURE__ */ new Set();
191
+ if (settings) dependencies.add(settings);
192
+ const options = compilerConfig(root, dependencies);
193
+ const output = path3.join(root, ".studio");
194
+ const registry = path3.join(output, "registry.generated.ts");
195
+ const types = path3.join(output, "types.generated.ts");
196
+ const imports = [
197
+ HEADER,
198
+ '/// <reference path="./types.generated.ts" />',
199
+ "import type { StudioTool, DiscoveredToolConfigs } from '@joycostudio/studio'"
200
+ ];
201
+ const typeImports = [HEADER, "import '@joycostudio/studio'"];
202
+ const declarations = [];
203
+ const definitions = [];
204
+ const mappings = [];
205
+ const ids = /* @__PURE__ */ new Set();
206
+ const destinations = /* @__PURE__ */ new Set();
207
+ const seeds = /* @__PURE__ */ new Map();
208
+ const trackImports = (file) => {
209
+ if (dependencies.has(file)) return;
210
+ dependencies.add(file);
211
+ if (!/\.[cm]?[jt]sx?$/.test(file)) return;
212
+ for (const imported of ts.preProcessFile(readFileSync(file, "utf8")).importedFiles) {
213
+ const resolved = ts.resolveModuleName(imported.fileName, file, options, ts.sys).resolvedModule?.resolvedFileName;
214
+ if (resolved && inside2(root, resolved) && !resolved.includes(`${path3.sep}node_modules${path3.sep}`) && !inside2(output, resolved))
215
+ trackImports(resolved);
216
+ }
217
+ };
218
+ for (const [index, file] of [...sources].entries()) {
219
+ const tool = declaration(file);
220
+ if (ids.has(tool.id)) return fail(file, `duplicate tool id "${tool.id}".`);
221
+ ids.add(tool.id);
222
+ trackImports(file);
223
+ const preview = pageRoute(app, file, tool.preview);
224
+ const title = (preview.split("/").filter(Boolean).at(-1) || tool.id).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
225
+ imports.push(`import tool${index} from ${JSON.stringify(relativeImport(registry, file))}`);
226
+ let values = "";
227
+ const resolvedTool = { source: file, title, preview };
228
+ if (tool.configurable) {
229
+ const inputs = /* @__PURE__ */ new Set();
230
+ try {
231
+ const specifier = tool.config ?? "./config.json";
232
+ const resolutionHost = {
233
+ ...ts.sys,
234
+ fileExists(candidate) {
235
+ if (inside2(root, candidate) && !candidate.includes(`${path3.sep}node_modules${path3.sep}`))
236
+ inputs.add(candidate);
237
+ return ts.sys.fileExists(candidate);
238
+ },
239
+ directoryExists(directory) {
240
+ const exists2 = ts.sys.directoryExists(directory);
241
+ if (!exists2 && inside2(root, directory) && !directory.includes(`${path3.sep}node_modules${path3.sep}`))
242
+ inputs.add(directory);
243
+ return exists2;
244
+ }
245
+ };
246
+ const resolved = ts.resolveModuleName(specifier, file, options, resolutionHost).resolvedModule?.resolvedFileName;
247
+ if (!resolved || !resolved.endsWith(".json"))
248
+ return fail(file, `cannot resolve JSON config "${specifier}" through tsconfig.json.`);
249
+ const target = await resolveToolFile(root, slash(path3.relative(root, resolved)));
250
+ inputs.add(target);
251
+ const real = realpathSync(target);
252
+ if (destinations.has(real)) return fail(file, "another tool already writes this config file.");
253
+ destinations.add(real);
254
+ const value = JSON.parse(readFileSync(target, "utf8"));
255
+ if (!value || typeof value !== "object" || Array.isArray(value))
256
+ return fail(file, "config must be a JSON object.");
257
+ resolvedTool.file = slash(path3.relative(root, target));
258
+ resolvedTool.initial = value;
259
+ dependencies.add(target);
260
+ const key = `${tool.id}:${real}`;
261
+ const prior = previous?.seeds.get(key);
262
+ const seed = prior && shape(prior) === shape(value) ? prior : value;
263
+ seeds.set(key, seed);
264
+ typeImports.push(`import type config${index} from ${JSON.stringify(relativeImport(types, target))}`);
265
+ mappings.push(` ${JSON.stringify(tool.id)}: typeof config${index}`);
266
+ values = `, file: ${JSON.stringify(slash(path3.relative(root, target)))}, initial: JSON.parse(${JSON.stringify(JSON.stringify(seed))}) as DiscoveredToolConfigs[${JSON.stringify(tool.id)}]`;
267
+ } catch (cause) {
268
+ throw new DiscoveryInputError(cause, /* @__PURE__ */ new Set([...dependencies, ...inputs]));
269
+ }
270
+ }
271
+ declarations.push(resolvedTool);
272
+ definitions.push(
273
+ ` { title: ${JSON.stringify(title)}, ...tool${index}, preview: ${JSON.stringify(preview)}${values} }`
274
+ );
275
+ }
276
+ const write = async () => {
277
+ await mkdir2(output, { recursive: true });
278
+ await writeChanged(
279
+ types,
280
+ `${typeImports.join("\n")}
281
+
282
+ declare module '@joycostudio/studio' {
283
+ interface DiscoveredToolConfigs {
284
+ ${mappings.join("\n")}
285
+ }
286
+ }
287
+ `
288
+ );
289
+ await writeChanged(
290
+ registry,
291
+ `${imports.join("\n")}
292
+
293
+ export const tools = [
294
+ ${definitions.join(",\n")}
295
+ ] satisfies readonly StudioTool[]
296
+ `
297
+ );
298
+ };
299
+ if (publish) await write();
300
+ return { root, toolsDir, sources, dependencies, registry, seeds, declarations, write };
301
+ }
302
+
303
+ export {
304
+ resolveToolFile,
305
+ hashOf,
306
+ serialize,
307
+ readDisk,
308
+ writeAtomic,
309
+ importer,
310
+ readSettings,
311
+ DiscoveryInputError,
312
+ generate
313
+ };
314
+ //# sourceMappingURL=chunk-HVGJXFXT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/discovery/generate.ts","../src/runtime/files.ts","../src/discovery/settings.ts"],"sourcesContent":["import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs'\nimport { mkdir, readFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport ts from 'typescript'\nimport { resolveToolFile, writeAtomic } from '../runtime/files'\nimport { readSettings } from './settings'\n\nconst slash = (value: string) => value.split(path.sep).join('/')\nconst relativeImport = (from: string, to: string) => {\n const relative = slash(path.relative(path.dirname(from), to)).replace(/\\.(tsx?|jsx?|mts|mjs)$/, '')\n return /^\\.\\.?\\//.test(relative) ? relative : `./${relative}`\n}\nconst inside = (root: string, file: string) => {\n const relative = path.relative(root, file)\n return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n}\nconst fail = (file: string, message: string): never => {\n throw new Error(`[studio] ${file}: ${message}`)\n}\nconst HEADER = '// Generated by Studio. Do not edit.\\n'\n\n/** Inputs from a rejected update still need watching so finishing a tool can recover it. */\nexport class DiscoveryInputError extends Error {\n constructor(\n cause: unknown,\n readonly inputs: Set<string>,\n ) {\n super(cause instanceof Error ? cause.message : String(cause), { cause })\n }\n}\n\n// Value edits keep the browser seed stable; a different JSON shape needs a new module.\nfunction shape(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) return `[${[...new Set(value.map(shape))].sort().join('|')}]`\n if (typeof value === 'object')\n return `{${Object.entries(value)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, item]) => `${JSON.stringify(key)}:${shape(item)}`)\n .join(',')}}`\n return typeof value\n}\n\nexport type Discovery = {\n root: string\n toolsDir: string\n sources: Set<string>\n dependencies: Set<string>\n registry: string\n /** Browser bootstrap values retained across runtime-managed, value-only updates. */\n seeds: Map<string, object>\n declarations: { source: string; preview: string; title: string; file?: string; initial?: object }[]\n /** Publish validated output after the runtime has accepted a refresh. */\n write(): Promise<void>\n}\n\n/** Parse declarations before importing them: their contextual types do not exist on a clean checkout yet. */\nfunction declaration(file: string) {\n const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true)\n const exported = source.statements.find(ts.isExportAssignment)\n const call = exported?.expression\n if (!call || !ts.isCallExpression(call) || !ts.isIdentifier(call.expression) || call.expression.text !== 'defineTool')\n return fail(file, 'default-export defineTool({ id, controls }) directly so discovery can inspect it.')\n const object = call.arguments[0]\n if (!object || !ts.isObjectLiteralExpression(object)) return fail(file, 'defineTool requires an object literal.')\n const properties = new Map<string, ts.ObjectLiteralElementLike>()\n for (const member of object.properties) {\n if (ts.isSpreadAssignment(member) || !member.name || ts.isComputedPropertyName(member.name))\n return fail(file, 'spread/computed tool options are not supported; put dynamic logic inside controls.')\n const key = member.name.getText(source).replace(/^['\"]|['\"]$/g, '')\n if (properties.has(key)) return fail(file, `duplicate option ${key}`)\n properties.set(key, member)\n }\n const literal = (key: string) => {\n const member = properties.get(key)\n if (!member) return undefined\n if (!ts.isPropertyAssignment(member) || !ts.isStringLiteral(member.initializer))\n return fail(file, `${key} must be a static string literal.`)\n return member.initializer.text\n }\n const id = literal('id')\n if (!id || !/^[a-z0-9][a-z0-9-]*$/.test(id)) return fail(file, 'id must be an explicit kebab-case string.')\n if (properties.has('file') || properties.has('initial'))\n return fail(file, 'discovered tools use config instead of file/initial; omit config for adjacent config.json.')\n const configurable = properties.has('controls')\n const config = literal('config')\n if (config !== undefined && !configurable) return fail(file, 'config requires controls.')\n return { id, config, configurable, preview: literal('preview'), source }\n}\n\nfunction pageRoute(app: string | undefined, file: string, explicit?: string) {\n if (explicit) return explicit\n const directory = path.dirname(file)\n if (!app || !inside(app, directory)) return fail(file, 'tools outside the app tree require an explicit preview URL.')\n if (!['tsx', 'ts', 'jsx', 'js'].some((ext) => existsSync(path.join(directory, `page.${ext}`))))\n return fail(file, 'add an adjacent page.tsx or specify preview explicitly.')\n const segments = slash(path.relative(app, directory)).split('/').filter(Boolean)\n if (segments.some((part) => part.includes('[') || part.startsWith('@') || /^\\(\\./.test(part) || part.startsWith('_')))\n return fail(file, 'dynamic, parallel, intercepted, and private routes require an explicit preview URL.')\n return '/' + segments.filter((part) => !/^\\(.+\\)$/.test(part)).join('/')\n}\n\nfunction walk(directory: string): string[] {\n return readdirSync(directory, { withFileTypes: true })\n .flatMap((entry) => {\n if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.isSymbolicLink()) return []\n const file = path.join(directory, entry.name)\n return entry.isDirectory() ? walk(file) : entry.name === 'studio.tool.ts' ? [file] : []\n })\n .sort()\n}\n\nfunction compilerConfig(root: string, dependencies: Set<string>) {\n const configFile = path.join(root, 'tsconfig.json')\n if (!existsSync(configFile)) return fail(root, 'discovery requires tsconfig.json.')\n const host: ts.ParseConfigFileHost = {\n ...ts.sys,\n readDirectory: () => [],\n readFile(file) {\n dependencies.add(path.resolve(file))\n return ts.sys.readFile(file)\n },\n onUnRecoverableConfigFileDiagnostic(error) {\n throw new Error(ts.flattenDiagnosticMessageText(error.messageText, '\\n'))\n },\n }\n const parsed = ts.getParsedCommandLineOfConfigFile(configFile, { resolveJsonModule: true }, host)\n if (!parsed) return fail(configFile, 'could not read TypeScript settings.')\n return parsed.options\n}\n\nasync function writeChanged(file: string, content: string) {\n if ((await readFile(file, 'utf8').catch(() => '')) === content) return\n await writeAtomic(file, content)\n}\n\n/** Generate only after all declarations and paths validate. Never loads preview components. */\nexport async function generate(\n cwd: string,\n explicit?: string,\n publish = true,\n previous?: Discovery,\n): Promise<Discovery | undefined> {\n const { root, config, file: settings } = await readSettings(cwd, explicit)\n if (config.tools !== undefined) return undefined\n const app = ['app', 'src/app'].map((dir) => path.join(root, dir)).find(existsSync)\n const toolsDir = config.toolsDir === undefined ? app : path.resolve(root, config.toolsDir)\n if (!toolsDir || !existsSync(toolsDir))\n return fail(root, 'no tools directory found; create app/ or configure toolsDir.')\n if (!inside(realpathSync(root), realpathSync(toolsDir)))\n return fail(toolsDir, 'toolsDir must stay inside the project.')\n const sources = new Set(walk(toolsDir))\n const dependencies = new Set<string>()\n if (settings) dependencies.add(settings)\n const options = compilerConfig(root, dependencies)\n const output = path.join(root, '.studio')\n const registry = path.join(output, 'registry.generated.ts')\n const types = path.join(output, 'types.generated.ts')\n const imports = [\n HEADER,\n '/// <reference path=\"./types.generated.ts\" />',\n \"import type { StudioTool, DiscoveredToolConfigs } from '@joycostudio/studio'\",\n ]\n const typeImports = [HEADER, \"import '@joycostudio/studio'\"]\n const declarations: Discovery['declarations'] = []\n const definitions: string[] = []\n const mappings: string[] = []\n const ids = new Set<string>()\n const destinations = new Set<string>()\n const seeds = new Map<string, object>()\n // Track local control helpers as well as the marker itself, without watching node_modules.\n const trackImports = (file: string) => {\n if (dependencies.has(file)) return\n dependencies.add(file)\n if (!/\\.[cm]?[jt]sx?$/.test(file)) return\n for (const imported of ts.preProcessFile(readFileSync(file, 'utf8')).importedFiles) {\n const resolved = ts.resolveModuleName(imported.fileName, file, options, ts.sys).resolvedModule?.resolvedFileName\n if (\n resolved &&\n inside(root, resolved) &&\n !resolved.includes(`${path.sep}node_modules${path.sep}`) &&\n !inside(output, resolved)\n )\n trackImports(resolved)\n }\n }\n for (const [index, file] of [...sources].entries()) {\n const tool = declaration(file)\n if (ids.has(tool.id)) return fail(file, `duplicate tool id \"${tool.id}\".`)\n ids.add(tool.id)\n trackImports(file)\n const preview = pageRoute(app, file, tool.preview)\n const title = (preview.split('/').filter(Boolean).at(-1) || tool.id)\n .replace(/[-_]/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase())\n imports.push(`import tool${index} from ${JSON.stringify(relativeImport(registry, file))}`)\n let values = ''\n const resolvedTool: Discovery['declarations'][number] = { source: file, title, preview }\n if (tool.configurable) {\n const inputs = new Set<string>()\n try {\n const specifier = tool.config ?? './config.json'\n const resolutionHost: ts.ModuleResolutionHost = {\n ...ts.sys,\n fileExists(candidate) {\n if (inside(root, candidate) && !candidate.includes(`${path.sep}node_modules${path.sep}`))\n inputs.add(candidate)\n return ts.sys.fileExists(candidate)\n },\n directoryExists(directory) {\n const exists = ts.sys.directoryExists(directory)\n if (!exists && inside(root, directory) && !directory.includes(`${path.sep}node_modules${path.sep}`))\n inputs.add(directory)\n return exists\n },\n }\n const resolved = ts.resolveModuleName(specifier, file, options, resolutionHost).resolvedModule?.resolvedFileName\n if (!resolved || !resolved.endsWith('.json'))\n return fail(file, `cannot resolve JSON config \"${specifier}\" through tsconfig.json.`)\n const target = await resolveToolFile(root, slash(path.relative(root, resolved)))\n inputs.add(target)\n const real = realpathSync(target)\n if (destinations.has(real)) return fail(file, 'another tool already writes this config file.')\n destinations.add(real)\n const value = JSON.parse(readFileSync(target, 'utf8'))\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return fail(file, 'config must be a JSON object.')\n resolvedTool.file = slash(path.relative(root, target))\n resolvedTool.initial = value\n dependencies.add(target)\n const key = `${tool.id}:${real}`\n const prior = previous?.seeds.get(key)\n const seed = prior && shape(prior) === shape(value) ? prior : value\n seeds.set(key, seed)\n typeImports.push(`import type config${index} from ${JSON.stringify(relativeImport(types, target))}`)\n mappings.push(` ${JSON.stringify(tool.id)}: typeof config${index}`)\n // JSON.parse preserves JSON keys such as __proto__ without JS object-literal semantics.\n values = `, file: ${JSON.stringify(slash(path.relative(root, target)))}, initial: JSON.parse(${JSON.stringify(JSON.stringify(seed))}) as DiscoveredToolConfigs[${JSON.stringify(tool.id)}]`\n } catch (cause) {\n throw new DiscoveryInputError(cause, new Set([...dependencies, ...inputs]))\n }\n }\n declarations.push(resolvedTool)\n definitions.push(\n ` { title: ${JSON.stringify(title)}, ...tool${index}, preview: ${JSON.stringify(preview)}${values} }`,\n )\n }\n const write = async () => {\n await mkdir(output, { recursive: true })\n await writeChanged(\n types,\n `${typeImports.join('\\n')}\\n\\ndeclare module '@joycostudio/studio' {\\n interface DiscoveredToolConfigs {\\n${mappings.join('\\n')}\\n }\\n}\\n`,\n )\n await writeChanged(\n registry,\n `${imports.join('\\n')}\\n\\nexport const tools = [\\n${definitions.join(',\\n')}\\n] satisfies readonly StudioTool[]\\n`,\n )\n }\n if (publish) await write()\n return { root, toolsDir, sources, dependencies, registry, seeds, declarations, write }\n}\n","import { createHash, randomUUID } from 'node:crypto'\nimport { mkdir, readFile, realpath, rename, rm, stat } from 'node:fs/promises'\nimport { writeFile } from 'node:fs/promises'\nimport path from 'node:path'\n\nconst inside = (root: string, target: string) => {\n const relative = path.relative(root, target)\n return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative)\n}\n\nconst exists = (target: string) =>\n stat(target).then(\n () => true,\n () => false,\n )\n\n/**\n * Resolve a tool's persistence target, refusing anything that is not a `.json`\n * file inside `root` — including paths that escape through a symlink.\n */\nexport async function resolveToolFile(root: string, file: string): Promise<string> {\n const fail = (reason: string) => new Error(`[studio] tool file \"${file}\" ${reason}`)\n if (path.isAbsolute(file)) throw fail('must be relative to the project root')\n if (path.extname(file) !== '.json') throw fail('must be a .json file')\n const absolute = path.resolve(root, file)\n if (!inside(root, absolute)) throw fail('escapes the project root')\n\n let probe = absolute\n while (!(await exists(probe))) probe = path.dirname(probe)\n if (probe !== root && !inside(await realpath(root), await realpath(probe))) throw fail('escapes the project root')\n return absolute\n}\n\nexport const hashOf = (content: string) => createHash('sha256').update(content).digest('hex').slice(0, 16)\n\nexport const serialize = (value: unknown) => `${JSON.stringify(value, null, 2)}\\n`\n\n/** The file's content and hash, or null when it does not exist. */\nexport async function readDisk(file: string): Promise<{ content: string; hash: string } | null> {\n try {\n const content = await readFile(file, 'utf8')\n return { content, hash: hashOf(content) }\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw cause\n }\n}\n\n/** Write through a sibling temp file + rename so readers never see a half-written config. */\nexport async function writeAtomic(file: string, content: string) {\n await mkdir(path.dirname(file), { recursive: true })\n const temp = `${file}.${randomUUID()}.tmp`\n try {\n await writeFile(temp, content, 'utf8')\n await rename(temp, file)\n } catch (cause) {\n await rm(temp, { force: true })\n throw cause\n }\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport { createJiti } from 'jiti'\nimport type { StudioConfig } from '../core/tool'\n\nconst CANDIDATES = ['studio.config.ts', 'studio.config.mts', 'studio.config.js', 'studio.config.mjs']\n\nexport function importer(root: string) {\n return createJiti(import.meta.url, {\n moduleCache: false,\n fsCache: false,\n tryNative: false,\n tsconfigPaths: existsSync(path.join(root, 'tsconfig.json')) ? path.join(root, 'tsconfig.json') : false,\n })\n}\n\nexport async function readSettings(cwd: string, explicit?: string) {\n const file = explicit ? path.resolve(cwd, explicit) : CANDIDATES.map((name) => path.join(cwd, name)).find(existsSync)\n if (explicit && !existsSync(file!)) throw new Error(`[studio] no ${explicit} found in ${cwd}`)\n const root = file ? path.dirname(file) : path.resolve(cwd)\n const config = file ? await importer(root).import<StudioConfig>(file, { default: true }) : {}\n if (!config || typeof config !== 'object' || (config.tools !== undefined && !Array.isArray(config.tools)))\n throw new Error('[studio] config must default-export defineConfig({ ... })')\n if (config.tools !== undefined && config.toolsDir !== undefined)\n throw new Error('[studio] Use either tools (manual registration) or toolsDir (discovery), not both.')\n return { config, root, file }\n}\n"],"mappings":"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","names":["existsSync","mkdir","readFile","path","fail","path","path","inside","existsSync","readFile","exists","mkdir"]}