@branch-fiction/extension-sdk 0.1.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.
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env node
2
+ import { u as validateManifest } from "./manifest-CQaa55kR.mjs";
3
+ import { n as readDevConfig, r as writeDevConfig, t as createDevServer } from "./server-BcwliPFy.mjs";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { dirname, posix, resolve } from "node:path";
6
+ import { spawn, spawnSync } from "node:child_process";
7
+ import process$1 from "node:process";
8
+ import { createInterface } from "node:readline/promises";
9
+ import { fileURLToPath } from "node:url";
10
+ import { serve } from "@hono/node-server";
11
+ import http from "node:http";
12
+ var BridgeError = class extends Error {
13
+ status;
14
+ constructor(status, message) {
15
+ super(message);
16
+ this.status = status;
17
+ this.name = "BridgeError";
18
+ }
19
+ };
20
+ var UnpairedError = class extends Error {
21
+ constructor(message = "not paired") {
22
+ super(message);
23
+ this.name = "UnpairedError";
24
+ }
25
+ };
26
+ function createBridge(opts = {}) {
27
+ const envPort = Number(process.env.BRANCH_FICTION_BRIDGE_PORT);
28
+ const port = opts.port ?? (Number.isFinite(envPort) && envPort > 0 ? envPort : 1421);
29
+ let token = opts.token;
30
+ async function pair(extensionId, code) {
31
+ const r = await request(port, "/v1/extension-dev/pair", {
32
+ method: "POST",
33
+ body: JSON.stringify({
34
+ extensionId,
35
+ code
36
+ })
37
+ });
38
+ if (r.status === 200) {
39
+ const parsed = JSON.parse(r.body);
40
+ token = parsed.token;
41
+ return parsed.token;
42
+ }
43
+ if (r.status === 401) throw new BridgeError(r.status, r.body || "invalid or expired code");
44
+ throw new BridgeError(r.status, r.body || `pair failed (${r.status})`);
45
+ }
46
+ async function prepareDb(extensionId) {
47
+ if (!token) throw new UnpairedError();
48
+ const r = await request(port, "/v1/extension-dev/extension-db/prepare", {
49
+ method: "POST",
50
+ headers: { authorization: `Bearer ${token}` },
51
+ body: JSON.stringify({ extensionId })
52
+ });
53
+ if (r.status === 200) return JSON.parse(r.body);
54
+ if (r.status === 401) throw new UnpairedError(r.body || "unpaired");
55
+ throw new BridgeError(r.status, r.body || `prepareDb failed (${r.status})`);
56
+ }
57
+ return {
58
+ port,
59
+ setToken: (t) => {
60
+ token = t;
61
+ },
62
+ pair,
63
+ prepareDb
64
+ };
65
+ }
66
+ function request(port, path, init) {
67
+ return new Promise((resolve, reject) => {
68
+ const req = http.request({
69
+ host: "127.0.0.1",
70
+ port,
71
+ path,
72
+ method: init.method,
73
+ headers: {
74
+ "content-type": "application/json",
75
+ ...init.headers ?? {}
76
+ }
77
+ }, (res) => {
78
+ let body = "";
79
+ res.setEncoding("utf8");
80
+ res.on("data", (chunk) => {
81
+ body += chunk;
82
+ });
83
+ res.on("end", () => resolve({
84
+ status: res.statusCode ?? 0,
85
+ body
86
+ }));
87
+ });
88
+ req.on("error", (err) => {
89
+ if (err.code === "ECONNREFUSED") reject(new BridgeError(0, `could not reach Branch Fiction on port ${port} — is the app running?`));
90
+ else reject(err);
91
+ });
92
+ if (init.body) req.write(init.body);
93
+ req.end();
94
+ });
95
+ }
96
+ //#endregion
97
+ //#region src/dev/cli.ts
98
+ function parseArgs(argv) {
99
+ let extensionDir = process$1.cwd();
100
+ let vitePort = 5173;
101
+ let hostPort = 1422;
102
+ let bridgePort;
103
+ for (let i = 0; i < argv.length; i++) {
104
+ const arg = argv[i];
105
+ if (arg === "--dir") extensionDir = resolve(argv[++i] ?? "");
106
+ else if (arg === "--vite-port") vitePort = Number(argv[++i]);
107
+ else if (arg === "--host-port") hostPort = Number(argv[++i]);
108
+ else if (arg === "--bridge-port") bridgePort = Number(argv[++i]);
109
+ else if (arg === "--help" || arg === "-h") {
110
+ printHelp();
111
+ process$1.exit(0);
112
+ }
113
+ }
114
+ return {
115
+ extensionDir,
116
+ vitePort,
117
+ hostPort,
118
+ bridgePort
119
+ };
120
+ }
121
+ function printHelp() {
122
+ console.log(`Usage: branch-fiction-extension-dev [--dir <extension source dir>] [--vite-port <n>] [--host-port <n>] [--bridge-port <n>]\n\nSpawns Vite (UI HMR), tsdown --watch (worker rebuild), and a local\ndev server. Open the printed URL in your browser to iterate.\n\nRequires the Branch Fiction app to be running for extension DB prep + book seeding.`);
123
+ }
124
+ function hasFile(dir, basename) {
125
+ for (const ext of [
126
+ ".ts",
127
+ ".js",
128
+ ".mjs",
129
+ ".cjs"
130
+ ]) if (existsSync(resolve(dir, basename + ext))) return true;
131
+ return false;
132
+ }
133
+ function writeGeneratedViteConfig(extensionDir, root, hostPort) {
134
+ const cacheDir = resolve(extensionDir, "node_modules/.cache/branch-fiction-extension-dev");
135
+ mkdirSync(cacheDir, { recursive: true });
136
+ const path = resolve(cacheDir, "vite.config.mjs");
137
+ writeFileSync(path, `import { branchFictionExtensionDev } from '@branch-fiction/extension-sdk/vite';
138
+ export default {
139
+ root: ${JSON.stringify(resolve(extensionDir, root))},
140
+ plugins: [branchFictionExtensionDev({ hostPort: ${hostPort} })]
141
+ };
142
+ `);
143
+ return path;
144
+ }
145
+ function denoTripleSuffix() {
146
+ const ext = process$1.platform === "win32" ? ".exe" : "";
147
+ if (process$1.platform === "darwin" && process$1.arch === "arm64") return `aarch64-apple-darwin${ext}`;
148
+ if (process$1.platform === "darwin" && process$1.arch === "x64") return `x86_64-apple-darwin${ext}`;
149
+ if (process$1.platform === "linux" && process$1.arch === "x64") return `x86_64-unknown-linux-gnu${ext}`;
150
+ if (process$1.platform === "win32" && process$1.arch === "x64") return "x86_64-pc-windows-msvc.exe";
151
+ return null;
152
+ }
153
+ function locateDeno(bridgeDeno) {
154
+ if (bridgeDeno && existsSync(bridgeDeno)) return bridgeDeno;
155
+ const here = dirname(fileURLToPath(import.meta.url));
156
+ const triple = denoTripleSuffix();
157
+ if (triple) {
158
+ const candidates = [resolve(here, `deno-${triple}`), resolve(here, "..", "..", "..", "..", "src-tauri", "binaries", `deno-${triple}`)];
159
+ for (const c of candidates) if (existsSync(c)) return c;
160
+ }
161
+ if (spawnSync("deno", ["--version"], { stdio: "ignore" }).status === 0) {
162
+ console.warn(`[dev] Branch Fiction app didn't report a bundled deno — falling back to \`deno\` on PATH. Update the app to a newer version to use the bundled sidecar.`);
163
+ return "deno";
164
+ }
165
+ throw new Error(`No deno binary found. The Branch Fiction app should ship one as a sidecar — make sure you're running an up-to-date app, or install Deno (https://deno.land) yourself.`);
166
+ }
167
+ function locateExtensionHostBundle() {
168
+ const here = dirname(fileURLToPath(import.meta.url));
169
+ const sibling = resolve(here, "extension-host.bundle.js");
170
+ if (existsSync(sibling)) return sibling;
171
+ const workspaceCandidate = resolve(here, "..", "..", "..", "..", "src-tauri", "resources", "extension-host.bundle.js");
172
+ if (existsSync(workspaceCandidate)) return workspaceCandidate;
173
+ throw new Error(`extension-host.bundle.js not found — did \`pnpm vendor:bundle\` run during extension-sdk build?`);
174
+ }
175
+ async function ensurePairedAndPrepare(bridge, configPath, extensionId) {
176
+ for (let attempt = 0; attempt < 2; attempt++) {
177
+ const config = readDevConfig(configPath);
178
+ if (config.bridgeToken) bridge.setToken(config.bridgeToken);
179
+ if (!config.bridgeToken) {
180
+ const code = await promptPairingCode();
181
+ const token = await bridge.pair(extensionId, code);
182
+ writeDevConfig(configPath, {
183
+ ...config,
184
+ bridgeToken: token
185
+ });
186
+ console.log(`[dev] paired — token saved to ${configPath}`);
187
+ }
188
+ try {
189
+ return await bridge.prepareDb(extensionId);
190
+ } catch (e) {
191
+ if (e instanceof UnpairedError) {
192
+ console.warn("[dev] pairing token rejected — re-pairing");
193
+ const fresh = readDevConfig(configPath);
194
+ delete fresh.bridgeToken;
195
+ writeDevConfig(configPath, fresh);
196
+ bridge.setToken(void 0);
197
+ continue;
198
+ }
199
+ throw e;
200
+ }
201
+ }
202
+ throw new Error("failed to prepare extension DB after re-pair");
203
+ }
204
+ async function promptPairingCode() {
205
+ console.log("\n[dev] No pairing yet. In the Branch Fiction app: Settings → Extensions → Enable extension dev mode → Pair new dev client.");
206
+ const rl = createInterface({
207
+ input: process$1.stdin,
208
+ output: process$1.stdout
209
+ });
210
+ try {
211
+ while (true) {
212
+ const answer = (await rl.question("[dev] Enter the pairing code: ")).trim();
213
+ if (/^[0-9a-f]{32}$/i.test(answer)) return answer.toLowerCase();
214
+ console.log("[dev] expected a 32-character pairing code");
215
+ }
216
+ } finally {
217
+ rl.close();
218
+ }
219
+ }
220
+ async function main() {
221
+ const args = parseArgs(process$1.argv.slice(2));
222
+ const manifestPath = resolve(args.extensionDir, "manifest.json");
223
+ if (!existsSync(manifestPath)) {
224
+ console.error(`No manifest.json at ${manifestPath}`);
225
+ process$1.exit(1);
226
+ }
227
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
228
+ validateManifest(manifest);
229
+ const extensionHostBundle = locateExtensionHostBundle();
230
+ const configPath = resolve(args.extensionDir, "dev.config.json");
231
+ const prepared = await ensurePairedAndPrepare(createBridge({ port: args.bridgePort }), configPath, manifest.id);
232
+ const denoBin = locateDeno(prepared.denoBin);
233
+ const viteOrigin = `http://localhost:${args.vitePort}`;
234
+ const { app } = createDevServer({
235
+ extensionDir: args.extensionDir,
236
+ hostPort: args.hostPort,
237
+ viteOrigin,
238
+ extensionHostBundle,
239
+ denoBin,
240
+ dbPath: prepared.dbPath,
241
+ dataDir: prepared.dataDir,
242
+ assetsDir: prepared.assetsDir,
243
+ configPath
244
+ });
245
+ serve({
246
+ fetch: app.fetch,
247
+ port: args.hostPort
248
+ });
249
+ const viteArgs = [
250
+ "exec",
251
+ "vite",
252
+ "--port",
253
+ String(args.vitePort),
254
+ "--strictPort"
255
+ ];
256
+ if (!hasFile(args.extensionDir, "vite.config")) {
257
+ const entry = manifest.path?.entry;
258
+ const root = entry ? posix.dirname(entry.replace(/^\.\/+/, "")) : ".";
259
+ const generated = writeGeneratedViteConfig(args.extensionDir, root, args.hostPort);
260
+ viteArgs.push("--config", generated);
261
+ }
262
+ const vite = spawn("pnpm", viteArgs, {
263
+ cwd: args.extensionDir,
264
+ stdio: "inherit"
265
+ });
266
+ vite.on("exit", (code) => {
267
+ console.log(`[dev] vite exited (code=${code})`);
268
+ process$1.exit(code ?? 0);
269
+ });
270
+ let tsdown = null;
271
+ if (hasFile(args.extensionDir, "tsdown.config")) {
272
+ tsdown = spawn("pnpm", [
273
+ "exec",
274
+ "tsdown",
275
+ "--watch"
276
+ ], {
277
+ cwd: args.extensionDir,
278
+ stdio: "inherit"
279
+ });
280
+ tsdown.on("exit", (code) => {
281
+ if (code !== 0) console.warn(`[dev] tsdown exited (code=${code})`);
282
+ });
283
+ } else console.log("[dev] no tsdown.config.* found — skipping worker bundle step");
284
+ process$1.on("SIGINT", () => {
285
+ vite.kill();
286
+ tsdown?.kill();
287
+ process$1.exit(0);
288
+ });
289
+ }
290
+ main().catch((e) => {
291
+ console.error(e);
292
+ process$1.exit(1);
293
+ });
294
+ //#endregion
295
+ export {};
296
+
297
+ //# sourceMappingURL=dev-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-cli.js","names":["process"],"sources":["../src/dev/bridge-client.ts","../src/dev/cli.ts"],"sourcesContent":["import http from 'node:http';\n\nexport const DEFAULT_BRIDGE_PORT = 1421;\n\nexport class BridgeError extends Error {\n status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.status = status;\n this.name = 'BridgeError';\n }\n}\n\nexport class UnpairedError extends Error {\n constructor(message = 'not paired') {\n super(message);\n this.name = 'UnpairedError';\n }\n}\n\nexport type PreparedDb = {\n dataDir: string;\n dbPath: string;\n assetsDir: string;\n denoBin?: string;\n};\n\nexport type Bridge = {\n port: number;\n setToken(token: string | undefined): void;\n pair(extensionId: string, code: string): Promise<string>;\n prepareDb(extensionId: string): Promise<PreparedDb>;\n};\n\nexport type CreateBridgeOptions = {\n port?: number;\n token?: string;\n};\n\nexport function createBridge(opts: CreateBridgeOptions = {}): Bridge {\n const envPort = Number(process.env.BRANCH_FICTION_BRIDGE_PORT);\n const port =\n opts.port ??\n (Number.isFinite(envPort) && envPort > 0 ? envPort : DEFAULT_BRIDGE_PORT);\n let token = opts.token;\n\n async function pair(extensionId: string, code: string): Promise<string> {\n const r = await request(port, '/v1/extension-dev/pair', {\n method: 'POST',\n body: JSON.stringify({ extensionId, code })\n });\n if (r.status === 200) {\n const parsed = JSON.parse(r.body) as { token: string };\n token = parsed.token;\n return parsed.token;\n }\n if (r.status === 401)\n throw new BridgeError(r.status, r.body || 'invalid or expired code');\n throw new BridgeError(r.status, r.body || `pair failed (${r.status})`);\n }\n\n async function prepareDb(extensionId: string): Promise<PreparedDb> {\n if (!token) throw new UnpairedError();\n const r = await request(port, '/v1/extension-dev/extension-db/prepare', {\n method: 'POST',\n headers: { authorization: `Bearer ${token}` },\n body: JSON.stringify({ extensionId })\n });\n if (r.status === 200) return JSON.parse(r.body) as PreparedDb;\n if (r.status === 401) throw new UnpairedError(r.body || 'unpaired');\n throw new BridgeError(r.status, r.body || `prepareDb failed (${r.status})`);\n }\n\n return {\n port,\n setToken: (t) => {\n token = t;\n },\n pair,\n prepareDb\n };\n}\n\ntype RequestInit = {\n method: 'GET' | 'POST';\n headers?: Record<string, string>;\n body?: string;\n};\n\ntype Response = { status: number; body: string };\n\nfunction request(port: number, path: string, init: RequestInit): Promise<Response> {\n return new Promise((resolve, reject) => {\n const req = http.request(\n {\n host: '127.0.0.1',\n port,\n path,\n method: init.method,\n headers: { 'content-type': 'application/json', ...(init.headers ?? {}) }\n },\n (res) => {\n let body = '';\n res.setEncoding('utf8');\n res.on('data', (chunk: string) => {\n body += chunk;\n });\n res.on('end', () => resolve({ status: res.statusCode ?? 0, body }));\n }\n );\n req.on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'ECONNREFUSED') {\n reject(\n new BridgeError(\n 0,\n `could not reach Branch Fiction on port ${port} — is the app running?`\n )\n );\n } else {\n reject(err);\n }\n });\n if (init.body) req.write(init.body);\n req.end();\n });\n}\n","#!/usr/bin/env node\nimport { spawn, spawnSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, posix, resolve } from 'node:path';\nimport process from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { fileURLToPath } from 'node:url';\n\nimport { serve } from '@hono/node-server';\n\nimport { validateManifest } from '../manifest';\nimport { type Bridge, createBridge, UnpairedError } from './bridge-client';\nimport { readDevConfig, writeDevConfig } from './config';\nimport { createDevServer } from './server';\n\ntype CliArgs = {\n extensionDir: string;\n vitePort: number;\n hostPort: number;\n bridgePort?: number;\n};\n\nfunction parseArgs(argv: string[]): CliArgs {\n let extensionDir = process.cwd();\n let vitePort = 5173;\n let hostPort = 1422;\n let bridgePort: number | undefined;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === '--dir') {\n extensionDir = resolve(argv[++i] ?? '');\n } else if (arg === '--vite-port') {\n vitePort = Number(argv[++i]);\n } else if (arg === '--host-port') {\n hostPort = Number(argv[++i]);\n } else if (arg === '--bridge-port') {\n bridgePort = Number(argv[++i]);\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n }\n }\n return { extensionDir, vitePort, hostPort, bridgePort };\n}\n\nfunction printHelp() {\n console.log(\n `Usage: branch-fiction-extension-dev [--dir <extension source dir>] [--vite-port <n>] [--host-port <n>] [--bridge-port <n>]\\n\\nSpawns Vite (UI HMR), tsdown --watch (worker rebuild), and a local\\ndev server. Open the printed URL in your browser to iterate.\\n\\nRequires the Branch Fiction app to be running for extension DB prep + book seeding.`\n );\n}\n\nfunction hasFile(dir: string, basename: string): boolean {\n for (const ext of ['.ts', '.js', '.mjs', '.cjs']) {\n if (existsSync(resolve(dir, basename + ext))) return true;\n }\n return false;\n}\n\nfunction writeGeneratedViteConfig(\n extensionDir: string,\n root: string,\n hostPort: number\n): string {\n const cacheDir = resolve(\n extensionDir,\n 'node_modules/.cache/branch-fiction-extension-dev'\n );\n mkdirSync(cacheDir, { recursive: true });\n const path = resolve(cacheDir, 'vite.config.mjs');\n const body = `import { branchFictionExtensionDev } from '@branch-fiction/extension-sdk/vite';\nexport default {\n root: ${JSON.stringify(resolve(extensionDir, root))},\n plugins: [branchFictionExtensionDev({ hostPort: ${hostPort} })]\n};\n`;\n writeFileSync(path, body);\n return path;\n}\n\nfunction denoTripleSuffix(): string | null {\n const ext = process.platform === 'win32' ? '.exe' : '';\n if (process.platform === 'darwin' && process.arch === 'arm64')\n return `aarch64-apple-darwin${ext}`;\n if (process.platform === 'darwin' && process.arch === 'x64')\n return `x86_64-apple-darwin${ext}`;\n if (process.platform === 'linux' && process.arch === 'x64')\n return `x86_64-unknown-linux-gnu${ext}`;\n if (process.platform === 'win32' && process.arch === 'x64')\n return 'x86_64-pc-windows-msvc.exe';\n return null;\n}\n\nfunction locateDeno(bridgeDeno?: string): string {\n if (bridgeDeno && existsSync(bridgeDeno)) return bridgeDeno;\n const here = dirname(fileURLToPath(import.meta.url));\n const triple = denoTripleSuffix();\n if (triple) {\n const candidates = [\n resolve(here, `deno-${triple}`),\n resolve(here, '..', '..', '..', '..', 'src-tauri', 'binaries', `deno-${triple}`)\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n }\n const onPath = spawnSync('deno', ['--version'], { stdio: 'ignore' });\n if (onPath.status === 0) {\n console.warn(\n `[dev] Branch Fiction app didn't report a bundled deno — falling back to \\`deno\\` on PATH. Update the app to a newer version to use the bundled sidecar.`\n );\n return 'deno';\n }\n throw new Error(\n `No deno binary found. The Branch Fiction app should ship one as a sidecar — make sure you're running an up-to-date app, or install Deno (https://deno.land) yourself.`\n );\n}\n\nfunction locateExtensionHostBundle(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const sibling = resolve(here, 'extension-host.bundle.js');\n if (existsSync(sibling)) return sibling;\n const workspaceCandidate = resolve(\n here,\n '..',\n '..',\n '..',\n '..',\n 'src-tauri',\n 'resources',\n 'extension-host.bundle.js'\n );\n if (existsSync(workspaceCandidate)) return workspaceCandidate;\n throw new Error(\n `extension-host.bundle.js not found — did \\`pnpm vendor:bundle\\` run during extension-sdk build?`\n );\n}\n\nasync function ensurePairedAndPrepare(\n bridge: Bridge,\n configPath: string,\n extensionId: string\n): Promise<{\n dbPath: string;\n dataDir: string;\n assetsDir: string;\n denoBin?: string;\n}> {\n for (let attempt = 0; attempt < 2; attempt++) {\n const config = readDevConfig(configPath);\n if (config.bridgeToken) bridge.setToken(config.bridgeToken);\n\n if (!config.bridgeToken) {\n const code = await promptPairingCode();\n const token = await bridge.pair(extensionId, code);\n writeDevConfig(configPath, { ...config, bridgeToken: token });\n console.log(`[dev] paired — token saved to ${configPath}`);\n }\n\n try {\n return await bridge.prepareDb(extensionId);\n } catch (e) {\n if (e instanceof UnpairedError) {\n console.warn('[dev] pairing token rejected — re-pairing');\n const fresh = readDevConfig(configPath);\n delete fresh.bridgeToken;\n writeDevConfig(configPath, fresh);\n bridge.setToken(undefined);\n continue;\n }\n throw e;\n }\n }\n throw new Error('failed to prepare extension DB after re-pair');\n}\n\nasync function promptPairingCode(): Promise<string> {\n console.log(\n '\\n[dev] No pairing yet. In the Branch Fiction app: Settings → Extensions → Enable extension dev mode → Pair new dev client.'\n );\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n while (true) {\n const answer = (await rl.question('[dev] Enter the pairing code: ')).trim();\n if (/^[0-9a-f]{32}$/i.test(answer)) return answer.toLowerCase();\n console.log('[dev] expected a 32-character pairing code');\n }\n } finally {\n rl.close();\n }\n}\n\nasync function main() {\n const args = parseArgs(process.argv.slice(2));\n\n const manifestPath = resolve(args.extensionDir, 'manifest.json');\n if (!existsSync(manifestPath)) {\n console.error(`No manifest.json at ${manifestPath}`);\n process.exit(1);\n }\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));\n validateManifest(manifest);\n\n const extensionHostBundle = locateExtensionHostBundle();\n\n const configPath = resolve(args.extensionDir, 'dev.config.json');\n const bridge = createBridge({ port: args.bridgePort });\n const prepared = await ensurePairedAndPrepare(bridge, configPath, manifest.id);\n const denoBin = locateDeno(prepared.denoBin);\n\n const viteOrigin = `http://localhost:${args.vitePort}`;\n const { app } = createDevServer({\n extensionDir: args.extensionDir,\n hostPort: args.hostPort,\n viteOrigin,\n extensionHostBundle,\n denoBin,\n dbPath: prepared.dbPath,\n dataDir: prepared.dataDir,\n assetsDir: prepared.assetsDir,\n configPath\n });\n\n serve({ fetch: app.fetch, port: args.hostPort });\n\n // if the extension has its own vite.config.*, use it\n // (we assume it imports `branchFictionExtensionDev` to get the proxy)\n // otherwise generate a minimal config with the proxy + root inferred from manifest.\n const viteArgs = ['exec', 'vite', '--port', String(args.vitePort), '--strictPort'];\n if (!hasFile(args.extensionDir, 'vite.config')) {\n const entry = manifest.path?.entry as string | undefined;\n const root = entry ? posix.dirname(entry.replace(/^\\.\\/+/, '')) : '.';\n const generated = writeGeneratedViteConfig(args.extensionDir, root, args.hostPort);\n viteArgs.push('--config', generated);\n }\n const vite = spawn('pnpm', viteArgs, { cwd: args.extensionDir, stdio: 'inherit' });\n vite.on('exit', (code) => {\n console.log(`[dev] vite exited (code=${code})`);\n process.exit(code ?? 0);\n });\n\n // tsdown is optional, some extensions might not need a worker\n let tsdown: ReturnType<typeof spawn> | null = null;\n if (hasFile(args.extensionDir, 'tsdown.config')) {\n tsdown = spawn('pnpm', ['exec', 'tsdown', '--watch'], {\n cwd: args.extensionDir,\n stdio: 'inherit'\n });\n tsdown.on('exit', (code) => {\n if (code !== 0) console.warn(`[dev] tsdown exited (code=${code})`);\n });\n } else {\n console.log('[dev] no tsdown.config.* found — skipping worker bundle step');\n }\n\n process.on('SIGINT', () => {\n vite.kill();\n tsdown?.kill();\n process.exit(0);\n });\n}\n\nvoid main().catch((e) => {\n console.error(e);\n process.exit(1);\n});\n"],"mappings":"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"}
package/dist/dev.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { t as ProviderAuthShape } from "./types-ZCFYu2MY.mjs";
2
+ import { Hono } from "hono";
3
+
4
+ //#region src/dev/types.d.ts
5
+ type DevProviderBindingOptions = {
6
+ kind: 'options';
7
+ useIndex: number;
8
+ fullURL?: string;
9
+ apiKey?: string;
10
+ };
11
+ type DevProviderBindingSlot = {
12
+ kind: 'useSlot';
13
+ providerType: string;
14
+ modelKey: string;
15
+ baseURL: string;
16
+ auth: ProviderAuthShape;
17
+ apiKey?: string;
18
+ reasoning?: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
19
+ };
20
+ type DevProviderBinding = DevProviderBindingOptions | DevProviderBindingSlot;
21
+ type DevConfig = {
22
+ bookId?: string;
23
+ bridgeToken?: string;
24
+ config?: Record<string, unknown>;
25
+ providers?: Record<string, DevProviderBinding>;
26
+ };
27
+ type DevRuntimeOptions = {
28
+ extensionDir: string;
29
+ hostPort: number;
30
+ viteOrigin: string;
31
+ extensionHostBundle: string;
32
+ denoBin: string;
33
+ dbPath: string;
34
+ dataDir: string;
35
+ assetsDir: string;
36
+ configPath?: string;
37
+ };
38
+ //#endregion
39
+ //#region src/dev/server.d.ts
40
+ type DevServer = {
41
+ app: Hono;
42
+ };
43
+ declare function createDevServer(opts: DevRuntimeOptions): DevServer;
44
+ //#endregion
45
+ export { type DevConfig, type DevProviderBinding, type DevRuntimeOptions, createDevServer };
46
+ //# sourceMappingURL=dev.d.ts.map
package/dist/dev.js ADDED
@@ -0,0 +1,2 @@
1
+ import { t as createDevServer } from "./server-BcwliPFy.mjs";
2
+ export { createDevServer };