skybridge 1.2.7 → 1.3.1

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.
Files changed (81) hide show
  1. package/README.md +1 -0
  2. package/dist/cli/build-helpers.d.ts +6 -2
  3. package/dist/cli/build-helpers.js +37 -12
  4. package/dist/cli/build-helpers.js.map +1 -1
  5. package/dist/cli/build-helpers.test.js +69 -3
  6. package/dist/cli/build-helpers.test.js.map +1 -1
  7. package/dist/cli/build-steps.d.ts +2 -0
  8. package/dist/cli/build-steps.js +73 -0
  9. package/dist/cli/build-steps.js.map +1 -0
  10. package/dist/cli/build-steps.test.d.ts +1 -0
  11. package/dist/cli/build-steps.test.js +52 -0
  12. package/dist/cli/build-steps.test.js.map +1 -0
  13. package/dist/cli/header.d.ts +1 -1
  14. package/dist/cli/resolve-skybridge-config.d.ts +5 -0
  15. package/dist/cli/{resolve-views-dir.js → resolve-skybridge-config.js} +4 -3
  16. package/dist/cli/resolve-skybridge-config.js.map +1 -0
  17. package/dist/cli/run-plain.d.ts +18 -0
  18. package/dist/cli/run-plain.js +89 -0
  19. package/dist/cli/run-plain.js.map +1 -0
  20. package/dist/cli/use-nodemon.d.ts +14 -0
  21. package/dist/cli/use-nodemon.js +71 -60
  22. package/dist/cli/use-nodemon.js.map +1 -1
  23. package/dist/cli/use-typescript-check.d.ts +8 -2
  24. package/dist/cli/use-typescript-check.js +70 -67
  25. package/dist/cli/use-typescript-check.js.map +1 -1
  26. package/dist/commands/build.d.ts +0 -2
  27. package/dist/commands/build.js +2 -61
  28. package/dist/commands/build.js.map +1 -1
  29. package/dist/commands/dev.d.ts +1 -0
  30. package/dist/commands/dev.js +34 -2
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/server/auth/security-schemes.d.ts +23 -0
  33. package/dist/server/auth/security-schemes.js +64 -0
  34. package/dist/server/auth/security-schemes.js.map +1 -0
  35. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  36. package/dist/server/auth/security-schemes.test.js +99 -0
  37. package/dist/server/auth/security-schemes.test.js.map +1 -0
  38. package/dist/server/auth/setup.d.ts +3 -1
  39. package/dist/server/auth/setup.js +62 -21
  40. package/dist/server/auth/setup.js.map +1 -1
  41. package/dist/server/auth/setup.test.js +291 -2
  42. package/dist/server/auth/setup.test.js.map +1 -1
  43. package/dist/server/host.d.ts +2 -0
  44. package/dist/server/host.js +11 -0
  45. package/dist/server/host.js.map +1 -0
  46. package/dist/server/host.test.d.ts +1 -0
  47. package/dist/server/host.test.js +13 -0
  48. package/dist/server/host.test.js.map +1 -0
  49. package/dist/server/index.d.ts +2 -1
  50. package/dist/server/index.js +2 -1
  51. package/dist/server/index.js.map +1 -1
  52. package/dist/server/server.d.ts +40 -4
  53. package/dist/server/server.js +114 -15
  54. package/dist/server/server.js.map +1 -1
  55. package/dist/server/skills-integration.test.d.ts +1 -0
  56. package/dist/server/skills-integration.test.js +63 -0
  57. package/dist/server/skills-integration.test.js.map +1 -0
  58. package/dist/server/skills.d.ts +23 -0
  59. package/dist/server/skills.js +178 -0
  60. package/dist/server/skills.js.map +1 -0
  61. package/dist/server/skills.test.d.ts +1 -0
  62. package/dist/server/skills.test.js +210 -0
  63. package/dist/server/skills.test.js.map +1 -0
  64. package/dist/server/viewsDevServer.js +4 -1
  65. package/dist/server/viewsDevServer.js.map +1 -1
  66. package/dist/test/view.test.js +3 -3
  67. package/dist/test/view.test.js.map +1 -1
  68. package/dist/web/bridges/adaptor.js +17 -0
  69. package/dist/web/bridges/adaptor.js.map +1 -1
  70. package/dist/web/bridges/adaptor.test.js +11 -0
  71. package/dist/web/bridges/adaptor.test.js.map +1 -1
  72. package/dist/web/components/modal-provider.d.ts +1 -1
  73. package/dist/web/data-llm.d.ts +1 -1
  74. package/dist/web/data-llm.test.js +8 -0
  75. package/dist/web/data-llm.test.js.map +1 -1
  76. package/dist/web/plugin/plugin.d.ts +6 -0
  77. package/dist/web/plugin/plugin.js +3 -3
  78. package/dist/web/plugin/plugin.js.map +1 -1
  79. package/package.json +21 -20
  80. package/dist/cli/resolve-views-dir.d.ts +0 -1
  81. package/dist/cli/resolve-views-dir.js.map +0 -1
package/README.md CHANGED
@@ -139,6 +139,7 @@ Explore all our example templates in the [Examples](https://docs.skybridge.tech/
139
139
 
140
140
  | Preview | Provider | Description | Code |
141
141
  | --- | --- | --- | --- |
142
+ | <img src="docs/images/descope.webp" alt="Auth Descope" width="160" /> | Descope | Full OAuth authentication with Descope and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-descope) |
142
143
  | <img src="docs/images/showcase-clerk.png" alt="Auth Clerk" width="160" /> | Clerk | Full OAuth authentication with Clerk and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-clerk) |
143
144
  | <img src="docs/images/showcase-workos.png" alt="Auth WorkOS AuthKit" width="160" /> | WorkOS AuthKit | Full OAuth authentication with WorkOS AuthKit and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-workos) |
144
145
  | <img src="docs/images/showcase-stytch.png" alt="Auth Stytch" width="160" /> | Stytch | Full OAuth authentication with Stytch and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-stytch) |
@@ -1,7 +1,11 @@
1
- export declare const ENTRY_WRAPPER_CONTENT = "import { __setBuildManifest } from \"skybridge/server\";\nimport manifest from \"./vite-manifest.js\";\n\n__setBuildManifest(manifest);\n\nconst userMod = await import(\"./server.js\");\nexport default userMod.default;\n";
1
+ export declare const ENTRY_WRAPPER_CONTENT = "import { __setBuildManifest, __setSkillsManifest } from \"skybridge/server\";\nimport manifest from \"./vite-manifest.js\";\nimport skills from \"./skills.js\";\n\n__setBuildManifest(manifest);\n__setSkillsManifest(skills);\n\nconst userMod = await import(\"./server.js\");\nexport default userMod.default;\n";
2
2
  export declare function emitEntryWrapper(distDir: string): void;
3
+ export declare function emitSkillsModule(skillsDir: string, outPath: string): void;
3
4
  export declare function emitManifestModule(manifestPath: string, outPath: string): void;
5
+ /** Emit an empty Vite manifest for view-less (headless) servers. */
6
+ export declare function emitEmptyManifestModule(outPath: string): void;
7
+ export declare function ensureAssetsDir(assetsDir: string): void;
4
8
  export declare const VERCEL_FUNCTION_NAME = "mcp";
5
9
  export declare const VERCEL_CONFIG: unknown;
6
10
  export declare const VERCEL_VC_CONFIG: unknown;
7
- export declare function emitVercelBuildOutput(root: string): Promise<void>;
11
+ export declare function emitVercelBuildOutput(root: string, serverExternal?: string[]): Promise<void>;
@@ -1,14 +1,19 @@
1
- import { cpSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
1
+ import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
2
2
  import path from "node:path";
3
- // Primes the manifest in skybridge's module scope, then dynamically imports
4
- // `./server.js` so user code runs *after* the side channel is set. The
5
- // dynamic import is load-bearing: a static `export { default } from ...` is
6
- // hoisted with the rest of the static graph and would evaluate `server.js`
7
- // before `__setBuildManifest` runs.
8
- export const ENTRY_WRAPPER_CONTENT = `import { __setBuildManifest } from "skybridge/server";
3
+ import { discoverSkills } from "../server/skills.js";
4
+ // Primes the manifest and skills snapshot in skybridge's module scope, then
5
+ // dynamically imports `./server.js` so user code runs *after* the side channels
6
+ // are set. The dynamic import is load-bearing: a static
7
+ // `export { default } from ...` is hoisted with the rest of the static graph
8
+ // and would evaluate `server.js` before the primers run. Both `skills.js` and
9
+ // the manifest are imported (not read from disk) so they ride the bundle on
10
+ // filesystem-less targets like Cloudflare Workers.
11
+ export const ENTRY_WRAPPER_CONTENT = `import { __setBuildManifest, __setSkillsManifest } from "skybridge/server";
9
12
  import manifest from "./vite-manifest.js";
13
+ import skills from "./skills.js";
10
14
 
11
15
  __setBuildManifest(manifest);
16
+ __setSkillsManifest(skills);
12
17
 
13
18
  const userMod = await import("./server.js");
14
19
  export default userMod.default;
@@ -16,10 +21,21 @@ export default userMod.default;
16
21
  export function emitEntryWrapper(distDir) {
17
22
  writeFileSync(path.join(distDir, "__entry.js"), ENTRY_WRAPPER_CONTENT);
18
23
  }
24
+ export function emitSkillsModule(skillsDir, outPath) {
25
+ const skills = discoverSkills(skillsDir);
26
+ writeFileSync(outPath, `export default ${JSON.stringify(skills)};\n`);
27
+ }
19
28
  export function emitManifestModule(manifestPath, outPath) {
20
29
  const manifest = readFileSync(manifestPath, "utf-8");
21
30
  writeFileSync(outPath, `export default ${manifest};\n`);
22
31
  }
32
+ /** Emit an empty Vite manifest for view-less (headless) servers. */
33
+ export function emitEmptyManifestModule(outPath) {
34
+ writeFileSync(outPath, "export default {};\n");
35
+ }
36
+ export function ensureAssetsDir(assetsDir) {
37
+ mkdirSync(assetsDir, { recursive: true });
38
+ }
23
39
  export const VERCEL_FUNCTION_NAME = "mcp";
24
40
  export const VERCEL_CONFIG = {
25
41
  version: 3,
@@ -48,7 +64,7 @@ export const VERCEL_VC_CONFIG = {
48
64
  // directly would skip that priming and 500 on view resource reads when the
49
65
  // function falls back to `readFileSync('dist/assets/.vite/manifest.json')`
50
66
  // (Vercel functions don't ship `dist/`).
51
- export async function emitVercelBuildOutput(root) {
67
+ export async function emitVercelBuildOutput(root, serverExternal = []) {
52
68
  const outputDir = path.join(root, ".vercel", "output");
53
69
  const funcDir = path.join(outputDir, "functions", `${VERCEL_FUNCTION_NAME}.func`);
54
70
  const staticAssetsDir = path.join(outputDir, "static", "assets");
@@ -66,7 +82,12 @@ export async function emitVercelBuildOutput(root) {
66
82
  define: { "process.env.NODE_ENV": '"production"' },
67
83
  // Dev-only deps reachable from re-exports; safe to leave unresolved since
68
84
  // the code paths that touch them are eliminated by the NODE_ENV define.
69
- external: ["vite", "@skybridge/devtools"],
85
+ // `serverExternal` comes from the vite plugin, for deps esbuild can't
86
+ // resolve or load (e.g. optional native modules pulled in transitively).
87
+ external: ["vite", "@skybridge/devtools", ...serverExternal],
88
+ // Native addons can't be inlined into the bundle; copy them next to the
89
+ // function and rewrite the require path to point at the copy.
90
+ loader: { ".node": "file" },
70
91
  banner: {
71
92
  // ESM bundles miss CJS interop globals that some deps reach for.
72
93
  js: "import{createRequire}from'node:module';const require=createRequire(import.meta.url);",
@@ -74,9 +95,13 @@ export async function emitVercelBuildOutput(root) {
74
95
  });
75
96
  writeFileSync(path.join(funcDir, ".vc-config.json"), `${JSON.stringify(VERCEL_VC_CONFIG, null, 2)}\n`);
76
97
  writeFileSync(path.join(funcDir, "package.json"), `${JSON.stringify({ type: "module" }, null, 2)}\n`);
77
- cpSync(path.join(root, "dist", "assets"), staticAssetsDir, {
78
- recursive: true,
79
- });
98
+ const assetsSrc = path.join(root, "dist", "assets");
99
+ if (existsSync(assetsSrc)) {
100
+ cpSync(assetsSrc, staticAssetsDir, { recursive: true });
101
+ }
102
+ else {
103
+ mkdirSync(staticAssetsDir, { recursive: true });
104
+ }
80
105
  writeFileSync(path.join(outputDir, "config.json"), `${JSON.stringify(VERCEL_CONFIG, null, 2)}\n`);
81
106
  }
82
107
  //# sourceMappingURL=build-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-helpers.js","sourceRoot":"","sources":["../../src/cli/build-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n cpSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\n\n// Primes the manifest in skybridge's module scope, then dynamically imports\n// `./server.js` so user code runs *after* the side channel is set. The\n// dynamic import is load-bearing: a static `export { default } from ...` is\n// hoisted with the rest of the static graph and would evaluate `server.js`\n// before `__setBuildManifest` runs.\nexport const ENTRY_WRAPPER_CONTENT = `import { __setBuildManifest } from \"skybridge/server\";\nimport manifest from \"./vite-manifest.js\";\n\n__setBuildManifest(manifest);\n\nconst userMod = await import(\"./server.js\");\nexport default userMod.default;\n`;\n\nexport function emitEntryWrapper(distDir: string): void {\n writeFileSync(path.join(distDir, \"__entry.js\"), ENTRY_WRAPPER_CONTENT);\n}\n\nexport function emitManifestModule(\n manifestPath: string,\n outPath: string,\n): void {\n const manifest = readFileSync(manifestPath, \"utf-8\");\n writeFileSync(outPath, `export default ${manifest};\\n`);\n}\n\nexport const VERCEL_FUNCTION_NAME = \"mcp\";\n\nexport const VERCEL_CONFIG: unknown = {\n version: 3,\n routes: [\n {\n src: \"/assets/(.*)\",\n headers: { \"Access-Control-Allow-Origin\": \"*\" },\n continue: true,\n },\n { handle: \"filesystem\" },\n { src: \"/(.*)\", dest: `/${VERCEL_FUNCTION_NAME}` },\n ],\n};\n\nexport const VERCEL_VC_CONFIG: unknown = {\n runtime: \"nodejs22.x\",\n handler: \"index.js\",\n launcherType: \"Nodejs\",\n shouldAddHelpers: true,\n};\n\n// Emit a Build Output API tree under `.vercel/output/`. Bundling the server\n// with esbuild produces a self-contained function bundle, so we don't ship\n// `node_modules` and don't touch tracked paths like `api/` or `public/`.\n//\n// Entry is `dist/__entry.js` — the wrapper that primes the Vite manifest via\n// `__setBuildManifest` before importing user code. Bundling `dist/server.js`\n// directly would skip that priming and 500 on view resource reads when the\n// function falls back to `readFileSync('dist/assets/.vite/manifest.json')`\n// (Vercel functions don't ship `dist/`).\nexport async function emitVercelBuildOutput(root: string): Promise<void> {\n const outputDir = path.join(root, \".vercel\", \"output\");\n const funcDir = path.join(\n outputDir,\n \"functions\",\n `${VERCEL_FUNCTION_NAME}.func`,\n );\n const staticAssetsDir = path.join(outputDir, \"static\", \"assets\");\n\n rmSync(outputDir, { recursive: true, force: true });\n mkdirSync(funcDir, { recursive: true });\n\n const { build } = await import(\"esbuild\");\n await build({\n entryPoints: [path.join(root, \"dist\", \"__entry.js\")],\n bundle: true,\n platform: \"node\",\n target: \"node22\",\n format: \"esm\",\n outfile: path.join(funcDir, \"index.js\"),\n // Lets esbuild DCE dev-only branches that pull in vite/devtools.\n define: { \"process.env.NODE_ENV\": '\"production\"' },\n // Dev-only deps reachable from re-exports; safe to leave unresolved since\n // the code paths that touch them are eliminated by the NODE_ENV define.\n external: [\"vite\", \"@skybridge/devtools\"],\n banner: {\n // ESM bundles miss CJS interop globals that some deps reach for.\n js: \"import{createRequire}from'node:module';const require=createRequire(import.meta.url);\",\n },\n });\n\n writeFileSync(\n path.join(funcDir, \".vc-config.json\"),\n `${JSON.stringify(VERCEL_VC_CONFIG, null, 2)}\\n`,\n );\n writeFileSync(\n path.join(funcDir, \"package.json\"),\n `${JSON.stringify({ type: \"module\" }, null, 2)}\\n`,\n );\n\n cpSync(path.join(root, \"dist\", \"assets\"), staticAssetsDir, {\n recursive: true,\n });\n\n writeFileSync(\n path.join(outputDir, \"config.json\"),\n `${JSON.stringify(VERCEL_CONFIG, null, 2)}\\n`,\n );\n}\n"]}
1
+ {"version":3,"file":"build-helpers.js","sourceRoot":"","sources":["../../src/cli/build-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { discoverSkills } from \"../server/skills.js\";\n\n// Primes the manifest and skills snapshot in skybridge's module scope, then\n// dynamically imports `./server.js` so user code runs *after* the side channels\n// are set. The dynamic import is load-bearing: a static\n// `export { default } from ...` is hoisted with the rest of the static graph\n// and would evaluate `server.js` before the primers run. Both `skills.js` and\n// the manifest are imported (not read from disk) so they ride the bundle on\n// filesystem-less targets like Cloudflare Workers.\nexport const ENTRY_WRAPPER_CONTENT = `import { __setBuildManifest, __setSkillsManifest } from \"skybridge/server\";\nimport manifest from \"./vite-manifest.js\";\nimport skills from \"./skills.js\";\n\n__setBuildManifest(manifest);\n__setSkillsManifest(skills);\n\nconst userMod = await import(\"./server.js\");\nexport default userMod.default;\n`;\n\nexport function emitEntryWrapper(distDir: string): void {\n writeFileSync(path.join(distDir, \"__entry.js\"), ENTRY_WRAPPER_CONTENT);\n}\n\nexport function emitSkillsModule(skillsDir: string, outPath: string): void {\n const skills = discoverSkills(skillsDir);\n writeFileSync(outPath, `export default ${JSON.stringify(skills)};\\n`);\n}\n\nexport function emitManifestModule(\n manifestPath: string,\n outPath: string,\n): void {\n const manifest = readFileSync(manifestPath, \"utf-8\");\n writeFileSync(outPath, `export default ${manifest};\\n`);\n}\n\n/** Emit an empty Vite manifest for view-less (headless) servers. */\nexport function emitEmptyManifestModule(outPath: string): void {\n writeFileSync(outPath, \"export default {};\\n\");\n}\n\nexport function ensureAssetsDir(assetsDir: string): void {\n mkdirSync(assetsDir, { recursive: true });\n}\n\nexport const VERCEL_FUNCTION_NAME = \"mcp\";\n\nexport const VERCEL_CONFIG: unknown = {\n version: 3,\n routes: [\n {\n src: \"/assets/(.*)\",\n headers: { \"Access-Control-Allow-Origin\": \"*\" },\n continue: true,\n },\n { handle: \"filesystem\" },\n { src: \"/(.*)\", dest: `/${VERCEL_FUNCTION_NAME}` },\n ],\n};\n\nexport const VERCEL_VC_CONFIG: unknown = {\n runtime: \"nodejs22.x\",\n handler: \"index.js\",\n launcherType: \"Nodejs\",\n shouldAddHelpers: true,\n};\n\n// Emit a Build Output API tree under `.vercel/output/`. Bundling the server\n// with esbuild produces a self-contained function bundle, so we don't ship\n// `node_modules` and don't touch tracked paths like `api/` or `public/`.\n//\n// Entry is `dist/__entry.js` — the wrapper that primes the Vite manifest via\n// `__setBuildManifest` before importing user code. Bundling `dist/server.js`\n// directly would skip that priming and 500 on view resource reads when the\n// function falls back to `readFileSync('dist/assets/.vite/manifest.json')`\n// (Vercel functions don't ship `dist/`).\nexport async function emitVercelBuildOutput(\n root: string,\n serverExternal: string[] = [],\n): Promise<void> {\n const outputDir = path.join(root, \".vercel\", \"output\");\n const funcDir = path.join(\n outputDir,\n \"functions\",\n `${VERCEL_FUNCTION_NAME}.func`,\n );\n const staticAssetsDir = path.join(outputDir, \"static\", \"assets\");\n\n rmSync(outputDir, { recursive: true, force: true });\n mkdirSync(funcDir, { recursive: true });\n\n const { build } = await import(\"esbuild\");\n await build({\n entryPoints: [path.join(root, \"dist\", \"__entry.js\")],\n bundle: true,\n platform: \"node\",\n target: \"node22\",\n format: \"esm\",\n outfile: path.join(funcDir, \"index.js\"),\n // Lets esbuild DCE dev-only branches that pull in vite/devtools.\n define: { \"process.env.NODE_ENV\": '\"production\"' },\n // Dev-only deps reachable from re-exports; safe to leave unresolved since\n // the code paths that touch them are eliminated by the NODE_ENV define.\n // `serverExternal` comes from the vite plugin, for deps esbuild can't\n // resolve or load (e.g. optional native modules pulled in transitively).\n external: [\"vite\", \"@skybridge/devtools\", ...serverExternal],\n // Native addons can't be inlined into the bundle; copy them next to the\n // function and rewrite the require path to point at the copy.\n loader: { \".node\": \"file\" },\n banner: {\n // ESM bundles miss CJS interop globals that some deps reach for.\n js: \"import{createRequire}from'node:module';const require=createRequire(import.meta.url);\",\n },\n });\n\n writeFileSync(\n path.join(funcDir, \".vc-config.json\"),\n `${JSON.stringify(VERCEL_VC_CONFIG, null, 2)}\\n`,\n );\n writeFileSync(\n path.join(funcDir, \"package.json\"),\n `${JSON.stringify({ type: \"module\" }, null, 2)}\\n`,\n );\n\n const assetsSrc = path.join(root, \"dist\", \"assets\");\n if (existsSync(assetsSrc)) {\n cpSync(assetsSrc, staticAssetsDir, { recursive: true });\n } else {\n mkdirSync(staticAssetsDir, { recursive: true });\n }\n\n writeFileSync(\n path.join(outputDir, \"config.json\"),\n `${JSON.stringify(VERCEL_CONFIG, null, 2)}\\n`,\n );\n}\n"]}
@@ -4,7 +4,7 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync, } from
4
4
  import { tmpdir } from "node:os";
5
5
  import path from "node:path";
6
6
  import { describe, expect, it } from "vitest";
7
- import { ENTRY_WRAPPER_CONTENT, emitEntryWrapper, emitManifestModule, emitVercelBuildOutput, VERCEL_CONFIG, VERCEL_VC_CONFIG, } from "./build-helpers.js";
7
+ import { ENTRY_WRAPPER_CONTENT, emitEmptyManifestModule, emitEntryWrapper, emitManifestModule, emitSkillsModule, emitVercelBuildOutput, ensureAssetsDir, VERCEL_CONFIG, VERCEL_VC_CONFIG, } from "./build-helpers.js";
8
8
  function mkTmp(prefix = "skybridge-build-helpers-") {
9
9
  return mkdtempSync(path.join(tmpdir(), prefix));
10
10
  }
@@ -14,15 +14,45 @@ describe("emitEntryWrapper", () => {
14
14
  emitEntryWrapper(dir);
15
15
  const out = readFileSync(path.join(dir, "__entry.js"), "utf-8");
16
16
  expect(out).toBe(ENTRY_WRAPPER_CONTENT);
17
- expect(out).toContain('import { __setBuildManifest } from "skybridge/server"');
17
+ expect(out).toContain('import { __setBuildManifest, __setSkillsManifest } from "skybridge/server"');
18
18
  expect(out).toContain('import manifest from "./vite-manifest.js"');
19
+ expect(out).toContain('import skills from "./skills.js"');
19
20
  expect(out).toContain("__setBuildManifest(manifest)");
21
+ expect(out).toContain("__setSkillsManifest(skills)");
20
22
  // Dynamic import is load-bearing: `server.js` must evaluate after the
21
- // setter runs, so a static re-export wouldn't work.
23
+ // setters run, so a static re-export wouldn't work.
22
24
  expect(out).toContain('await import("./server.js")');
23
25
  expect(out).toContain("export default userMod.default");
24
26
  });
25
27
  });
28
+ describe("emitSkillsModule", () => {
29
+ it("inlines discovered skills as an ESM default export", () => {
30
+ const dir = mkTmp();
31
+ const skillsDir = path.join(dir, "src", "skills");
32
+ mkdirSync(path.join(skillsDir, "refunds"), { recursive: true });
33
+ writeFileSync(path.join(skillsDir, "refunds", "SKILL.md"), "---\nname: refunds\ndescription: Process refunds\n---\nBody");
34
+ const outPath = path.join(dir, "skills.js");
35
+ emitSkillsModule(skillsDir, outPath);
36
+ const out = readFileSync(outPath, "utf-8");
37
+ const literal = out
38
+ .slice("export default ".length)
39
+ .trim()
40
+ .replace(/;$/, "");
41
+ const skills = JSON.parse(literal);
42
+ expect(skills).toHaveLength(1);
43
+ expect(skills[0]).toMatchObject({
44
+ name: "refunds",
45
+ frontmatter: { name: "refunds", description: "Process refunds" },
46
+ });
47
+ expect(skills[0].digest).toMatch(/^sha256:[a-f0-9]{64}$/);
48
+ });
49
+ it("emits an empty array when the skills directory is absent", () => {
50
+ const dir = mkTmp();
51
+ const outPath = path.join(dir, "skills.js");
52
+ emitSkillsModule(path.join(dir, "nope"), outPath);
53
+ expect(readFileSync(outPath, "utf-8")).toBe("export default [];\n");
54
+ });
55
+ });
26
56
  describe("emitManifestModule", () => {
27
57
  it("inlines the JSON manifest as an ESM default export", () => {
28
58
  const dir = mkTmp();
@@ -40,6 +70,22 @@ describe("emitManifestModule", () => {
40
70
  expect(JSON.parse(literal)).toEqual(manifest);
41
71
  });
42
72
  });
73
+ describe("emitEmptyManifestModule", () => {
74
+ it("writes an empty object default export", () => {
75
+ const dir = mkTmp();
76
+ const outPath = path.join(dir, "vite-manifest.js");
77
+ emitEmptyManifestModule(outPath);
78
+ expect(readFileSync(outPath, "utf-8")).toBe("export default {};\n");
79
+ });
80
+ });
81
+ describe("ensureAssetsDir", () => {
82
+ it("creates the assets directory when missing", () => {
83
+ const dir = mkTmp();
84
+ const assetsDir = path.join(dir, "dist", "assets");
85
+ ensureAssetsDir(assetsDir);
86
+ expect(existsSync(assetsDir)).toBe(true);
87
+ });
88
+ });
43
89
  describe("emitVercelBuildOutput", () => {
44
90
  it("emits a Build Output API tree with bundled function and static assets", async () => {
45
91
  const root = mkTmp();
@@ -60,5 +106,25 @@ describe("emitVercelBuildOutput", () => {
60
106
  expect(JSON.parse(readFileSync(path.join(outputDir, "config.json"), "utf-8"))).toEqual(VERCEL_CONFIG);
61
107
  expect(readFileSync(path.join(outputDir, "static", "assets", "view-abc.js"), "utf-8")).toContain("bundled view");
62
108
  });
109
+ it("leaves serverExternal packages unbundled", async () => {
110
+ const root = mkTmp();
111
+ mkdirSync(path.join(root, "dist"), { recursive: true });
112
+ // Unresolvable from the tmp root, so esbuild fails the build unless the
113
+ // package is marked external (bunyan → optional `dtrace-provider`).
114
+ writeFileSync(path.join(root, "dist", "__entry.js"), 'import "dtrace-provider";\nexport default null;\n');
115
+ await expect(emitVercelBuildOutput(root)).rejects.toThrow(/Could not resolve "dtrace-provider"/);
116
+ await emitVercelBuildOutput(root, ["dtrace-provider"]);
117
+ const bundle = readFileSync(path.join(root, ".vercel", "output", "functions", "mcp.func", "index.js"), "utf-8");
118
+ expect(bundle).toContain('import "dtrace-provider"');
119
+ });
120
+ it("emits static assets directory when dist/assets is missing", async () => {
121
+ const root = mkTmp();
122
+ mkdirSync(path.join(root, "dist"), { recursive: true });
123
+ writeFileSync(path.join(root, "dist", "__entry.js"), "const userMod = await import('./server.js');\nexport default userMod.default;\n");
124
+ writeFileSync(path.join(root, "dist", "server.js"), "export default function handler(_req, res) { res.end('ok'); }\n");
125
+ await emitVercelBuildOutput(root);
126
+ expect(existsSync(path.join(root, ".vercel", "output", "static", "assets"))).toBe(true);
127
+ expect(existsSync(path.join(root, ".vercel", "output", "functions", "mcp.func", "index.js"))).toBe(true);
128
+ });
63
129
  });
64
130
  //# sourceMappingURL=build-helpers.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-helpers.test.js","sourceRoot":"","sources":["../../src/cli/build-helpers.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\n// esbuild's invariant check on TextEncoder/Uint8Array trips jsdom's polyfill.\nimport {\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { describe, expect, it } from \"vitest\";\nimport {\n ENTRY_WRAPPER_CONTENT,\n emitEntryWrapper,\n emitManifestModule,\n emitVercelBuildOutput,\n VERCEL_CONFIG,\n VERCEL_VC_CONFIG,\n} from \"./build-helpers.js\";\n\nfunction mkTmp(prefix = \"skybridge-build-helpers-\") {\n return mkdtempSync(path.join(tmpdir(), prefix));\n}\n\ndescribe(\"emitEntryWrapper\", () => {\n it(\"writes dist/__entry.js that primes the manifest before importing user code\", () => {\n const dir = mkTmp();\n emitEntryWrapper(dir);\n const out = readFileSync(path.join(dir, \"__entry.js\"), \"utf-8\");\n expect(out).toBe(ENTRY_WRAPPER_CONTENT);\n expect(out).toContain(\n 'import { __setBuildManifest } from \"skybridge/server\"',\n );\n expect(out).toContain('import manifest from \"./vite-manifest.js\"');\n expect(out).toContain(\"__setBuildManifest(manifest)\");\n // Dynamic import is load-bearing: `server.js` must evaluate after the\n // setter runs, so a static re-export wouldn't work.\n expect(out).toContain('await import(\"./server.js\")');\n expect(out).toContain(\"export default userMod.default\");\n });\n});\n\ndescribe(\"emitManifestModule\", () => {\n it(\"inlines the JSON manifest as an ESM default export\", () => {\n const dir = mkTmp();\n const inPath = path.join(dir, \"manifest.json\");\n const outPath = path.join(dir, \"vite-manifest.js\");\n const manifest = { \"src/views/foo.tsx\": { file: \"assets/foo-abc.js\" } };\n writeFileSync(inPath, JSON.stringify(manifest));\n emitManifestModule(inPath, outPath);\n const out = readFileSync(outPath, \"utf-8\");\n expect(out.startsWith(\"export default \")).toBe(true);\n const literal = out\n .slice(\"export default \".length)\n .trim()\n .replace(/;$/, \"\");\n expect(JSON.parse(literal)).toEqual(manifest);\n });\n});\n\ndescribe(\"emitVercelBuildOutput\", () => {\n it(\"emits a Build Output API tree with bundled function and static assets\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\", \"assets\"), { recursive: true });\n writeFileSync(\n path.join(root, \"dist\", \"server.js\"),\n \"export default function handler(_req, res) { res.end('ok'); }\\n\",\n );\n // Minimal `dist/__entry.js` (the real wrapper imports `skybridge/server`,\n // which isn't resolvable in this test's tmp dir — the function-bundling\n // contract we care about here is just \"bundle whatever `__entry.js`\n // imports into a single function file\").\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n \"const userMod = await import('./server.js');\\nexport default userMod.default;\\n\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"view-abc.js\"),\n \"/* bundled view */\\n\",\n );\n\n await emitVercelBuildOutput(root);\n\n const outputDir = path.join(root, \".vercel\", \"output\");\n const funcDir = path.join(outputDir, \"functions\", \"mcp.func\");\n\n expect(existsSync(path.join(funcDir, \"index.js\"))).toBe(true);\n expect(\n JSON.parse(readFileSync(path.join(funcDir, \".vc-config.json\"), \"utf-8\")),\n ).toEqual(VERCEL_VC_CONFIG);\n expect(\n JSON.parse(readFileSync(path.join(funcDir, \"package.json\"), \"utf-8\")),\n ).toEqual({ type: \"module\" });\n expect(\n JSON.parse(readFileSync(path.join(outputDir, \"config.json\"), \"utf-8\")),\n ).toEqual(VERCEL_CONFIG);\n expect(\n readFileSync(\n path.join(outputDir, \"static\", \"assets\", \"view-abc.js\"),\n \"utf-8\",\n ),\n ).toContain(\"bundled view\");\n });\n});\n"]}
1
+ {"version":3,"file":"build-helpers.test.js","sourceRoot":"","sources":["../../src/cli/build-helpers.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\n// esbuild's invariant check on TextEncoder/Uint8Array trips jsdom's polyfill.\nimport {\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { describe, expect, it } from \"vitest\";\nimport {\n ENTRY_WRAPPER_CONTENT,\n emitEmptyManifestModule,\n emitEntryWrapper,\n emitManifestModule,\n emitSkillsModule,\n emitVercelBuildOutput,\n ensureAssetsDir,\n VERCEL_CONFIG,\n VERCEL_VC_CONFIG,\n} from \"./build-helpers.js\";\n\nfunction mkTmp(prefix = \"skybridge-build-helpers-\") {\n return mkdtempSync(path.join(tmpdir(), prefix));\n}\n\ndescribe(\"emitEntryWrapper\", () => {\n it(\"writes dist/__entry.js that primes the manifest before importing user code\", () => {\n const dir = mkTmp();\n emitEntryWrapper(dir);\n const out = readFileSync(path.join(dir, \"__entry.js\"), \"utf-8\");\n expect(out).toBe(ENTRY_WRAPPER_CONTENT);\n expect(out).toContain(\n 'import { __setBuildManifest, __setSkillsManifest } from \"skybridge/server\"',\n );\n expect(out).toContain('import manifest from \"./vite-manifest.js\"');\n expect(out).toContain('import skills from \"./skills.js\"');\n expect(out).toContain(\"__setBuildManifest(manifest)\");\n expect(out).toContain(\"__setSkillsManifest(skills)\");\n // Dynamic import is load-bearing: `server.js` must evaluate after the\n // setters run, so a static re-export wouldn't work.\n expect(out).toContain('await import(\"./server.js\")');\n expect(out).toContain(\"export default userMod.default\");\n });\n});\n\ndescribe(\"emitSkillsModule\", () => {\n it(\"inlines discovered skills as an ESM default export\", () => {\n const dir = mkTmp();\n const skillsDir = path.join(dir, \"src\", \"skills\");\n mkdirSync(path.join(skillsDir, \"refunds\"), { recursive: true });\n writeFileSync(\n path.join(skillsDir, \"refunds\", \"SKILL.md\"),\n \"---\\nname: refunds\\ndescription: Process refunds\\n---\\nBody\",\n );\n const outPath = path.join(dir, \"skills.js\");\n emitSkillsModule(skillsDir, outPath);\n const out = readFileSync(outPath, \"utf-8\");\n const literal = out\n .slice(\"export default \".length)\n .trim()\n .replace(/;$/, \"\");\n const skills = JSON.parse(literal);\n expect(skills).toHaveLength(1);\n expect(skills[0]).toMatchObject({\n name: \"refunds\",\n frontmatter: { name: \"refunds\", description: \"Process refunds\" },\n });\n expect(skills[0].digest).toMatch(/^sha256:[a-f0-9]{64}$/);\n });\n\n it(\"emits an empty array when the skills directory is absent\", () => {\n const dir = mkTmp();\n const outPath = path.join(dir, \"skills.js\");\n emitSkillsModule(path.join(dir, \"nope\"), outPath);\n expect(readFileSync(outPath, \"utf-8\")).toBe(\"export default [];\\n\");\n });\n});\n\ndescribe(\"emitManifestModule\", () => {\n it(\"inlines the JSON manifest as an ESM default export\", () => {\n const dir = mkTmp();\n const inPath = path.join(dir, \"manifest.json\");\n const outPath = path.join(dir, \"vite-manifest.js\");\n const manifest = { \"src/views/foo.tsx\": { file: \"assets/foo-abc.js\" } };\n writeFileSync(inPath, JSON.stringify(manifest));\n emitManifestModule(inPath, outPath);\n const out = readFileSync(outPath, \"utf-8\");\n expect(out.startsWith(\"export default \")).toBe(true);\n const literal = out\n .slice(\"export default \".length)\n .trim()\n .replace(/;$/, \"\");\n expect(JSON.parse(literal)).toEqual(manifest);\n });\n});\n\ndescribe(\"emitEmptyManifestModule\", () => {\n it(\"writes an empty object default export\", () => {\n const dir = mkTmp();\n const outPath = path.join(dir, \"vite-manifest.js\");\n emitEmptyManifestModule(outPath);\n expect(readFileSync(outPath, \"utf-8\")).toBe(\"export default {};\\n\");\n });\n});\n\ndescribe(\"ensureAssetsDir\", () => {\n it(\"creates the assets directory when missing\", () => {\n const dir = mkTmp();\n const assetsDir = path.join(dir, \"dist\", \"assets\");\n ensureAssetsDir(assetsDir);\n expect(existsSync(assetsDir)).toBe(true);\n });\n});\n\ndescribe(\"emitVercelBuildOutput\", () => {\n it(\"emits a Build Output API tree with bundled function and static assets\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\", \"assets\"), { recursive: true });\n writeFileSync(\n path.join(root, \"dist\", \"server.js\"),\n \"export default function handler(_req, res) { res.end('ok'); }\\n\",\n );\n // Minimal `dist/__entry.js` (the real wrapper imports `skybridge/server`,\n // which isn't resolvable in this test's tmp dir — the function-bundling\n // contract we care about here is just \"bundle whatever `__entry.js`\n // imports into a single function file\").\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n \"const userMod = await import('./server.js');\\nexport default userMod.default;\\n\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"view-abc.js\"),\n \"/* bundled view */\\n\",\n );\n\n await emitVercelBuildOutput(root);\n\n const outputDir = path.join(root, \".vercel\", \"output\");\n const funcDir = path.join(outputDir, \"functions\", \"mcp.func\");\n\n expect(existsSync(path.join(funcDir, \"index.js\"))).toBe(true);\n expect(\n JSON.parse(readFileSync(path.join(funcDir, \".vc-config.json\"), \"utf-8\")),\n ).toEqual(VERCEL_VC_CONFIG);\n expect(\n JSON.parse(readFileSync(path.join(funcDir, \"package.json\"), \"utf-8\")),\n ).toEqual({ type: \"module\" });\n expect(\n JSON.parse(readFileSync(path.join(outputDir, \"config.json\"), \"utf-8\")),\n ).toEqual(VERCEL_CONFIG);\n expect(\n readFileSync(\n path.join(outputDir, \"static\", \"assets\", \"view-abc.js\"),\n \"utf-8\",\n ),\n ).toContain(\"bundled view\");\n });\n\n it(\"leaves serverExternal packages unbundled\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\"), { recursive: true });\n // Unresolvable from the tmp root, so esbuild fails the build unless the\n // package is marked external (bunyan → optional `dtrace-provider`).\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n 'import \"dtrace-provider\";\\nexport default null;\\n',\n );\n\n await expect(emitVercelBuildOutput(root)).rejects.toThrow(\n /Could not resolve \"dtrace-provider\"/,\n );\n await emitVercelBuildOutput(root, [\"dtrace-provider\"]);\n\n const bundle = readFileSync(\n path.join(root, \".vercel\", \"output\", \"functions\", \"mcp.func\", \"index.js\"),\n \"utf-8\",\n );\n expect(bundle).toContain('import \"dtrace-provider\"');\n });\n\n it(\"emits static assets directory when dist/assets is missing\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\"), { recursive: true });\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n \"const userMod = await import('./server.js');\\nexport default userMod.default;\\n\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"server.js\"),\n \"export default function handler(_req, res) { res.end('ok'); }\\n\",\n );\n\n await emitVercelBuildOutput(root);\n\n expect(\n existsSync(path.join(root, \".vercel\", \"output\", \"static\", \"assets\")),\n ).toBe(true);\n expect(\n existsSync(\n path.join(\n root,\n \".vercel\",\n \"output\",\n \"functions\",\n \"mcp.func\",\n \"index.js\",\n ),\n ),\n ).toBe(true);\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ import type { CommandStep } from "./use-execute-steps.js";
2
+ export declare function getCommandSteps(root?: string): Promise<CommandStep[]>;
@@ -0,0 +1,73 @@
1
+ import { rmSync, writeFileSync } from "node:fs";
2
+ import path, { isAbsolute, resolve } from "node:path";
3
+ import { scanAndWriteViewsDts, scanViewsSync, } from "../web/plugin/scan-views.js";
4
+ import { emitEmptyManifestModule, emitEntryWrapper, emitManifestModule, emitSkillsModule, emitVercelBuildOutput, ensureAssetsDir, } from "./build-helpers.js";
5
+ import { resolveSkybridgeConfig } from "./resolve-skybridge-config.js";
6
+ export async function getCommandSteps(root = process.cwd()) {
7
+ const { viewsDir, serverExternal } = await resolveSkybridgeConfig(root);
8
+ const rawDir = viewsDir ?? "src/views";
9
+ const resolvedDir = isAbsolute(rawDir) ? rawDir : resolve(root, rawDir);
10
+ // Non-throwing pre-check: duplicate view names are validated in the
11
+ // "Scanning views" step so useExecuteSteps can render a styled error.
12
+ const hasViews = scanViewsSync(resolvedDir).valid.length > 0;
13
+ const steps = [
14
+ {
15
+ label: "Scanning views",
16
+ run: async () => {
17
+ scanAndWriteViewsDts(root, viewsDir);
18
+ },
19
+ },
20
+ {
21
+ label: "Compiling server",
22
+ run: () => rmSync(path.join(root, "dist"), { recursive: true, force: true }),
23
+ command: "tsc -b --force",
24
+ },
25
+ ];
26
+ if (hasViews) {
27
+ steps.push({
28
+ label: "Building views",
29
+ command: "vite build",
30
+ });
31
+ }
32
+ steps.push({
33
+ label: "Emitting manifest module",
34
+ run: () => {
35
+ const manifestOut = path.join(root, "dist", "vite-manifest.js");
36
+ if (hasViews) {
37
+ emitManifestModule(path.join(root, "dist", "assets", ".vite", "manifest.json"), manifestOut);
38
+ }
39
+ else {
40
+ emitEmptyManifestModule(manifestOut);
41
+ }
42
+ },
43
+ }, {
44
+ label: "Emitting skills module",
45
+ run: () => {
46
+ emitSkillsModule(resolve(root, "src/skills"), path.join(root, "dist", "skills.js"));
47
+ },
48
+ }, {
49
+ label: "Emitting entry wrapper",
50
+ run: () => {
51
+ emitEntryWrapper(path.join(root, "dist"));
52
+ },
53
+ }, {
54
+ label: "Emitting Cloudflare redirects",
55
+ run: () => {
56
+ const assetsDir = path.join(root, "dist", "assets");
57
+ ensureAssetsDir(assetsDir);
58
+ writeFileSync(path.join(assetsDir, "_redirects"), "/assets/assets/* /assets/:splat 200\n");
59
+ },
60
+ }, {
61
+ label: "Emitting Cloudflare headers",
62
+ run: () => {
63
+ const assetsDir = path.join(root, "dist", "assets");
64
+ ensureAssetsDir(assetsDir);
65
+ writeFileSync(path.join(assetsDir, "_headers"), "/assets/*\n Access-Control-Allow-Origin: *\n");
66
+ },
67
+ }, {
68
+ label: "Emitting Vercel build output",
69
+ run: () => emitVercelBuildOutput(root, serverExternal),
70
+ });
71
+ return steps;
72
+ }
73
+ //# sourceMappingURL=build-steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-steps.js","sourceRoot":"","sources":["../../src/cli/build-steps.ts"],"names":[],"mappings":"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","sourcesContent":["import { rmSync, writeFileSync } from \"node:fs\";\nimport path, { isAbsolute, resolve } from \"node:path\";\nimport {\n scanAndWriteViewsDts,\n scanViewsSync,\n} from \"../web/plugin/scan-views.js\";\nimport {\n emitEmptyManifestModule,\n emitEntryWrapper,\n emitManifestModule,\n emitSkillsModule,\n emitVercelBuildOutput,\n ensureAssetsDir,\n} from \"./build-helpers.js\";\nimport { resolveSkybridgeConfig } from \"./resolve-skybridge-config.js\";\nimport type { CommandStep } from \"./use-execute-steps.js\";\n\nexport async function getCommandSteps(\n root = process.cwd(),\n): Promise<CommandStep[]> {\n const { viewsDir, serverExternal } = await resolveSkybridgeConfig(root);\n const rawDir = viewsDir ?? \"src/views\";\n const resolvedDir = isAbsolute(rawDir) ? rawDir : resolve(root, rawDir);\n // Non-throwing pre-check: duplicate view names are validated in the\n // \"Scanning views\" step so useExecuteSteps can render a styled error.\n const hasViews = scanViewsSync(resolvedDir).valid.length > 0;\n\n const steps: CommandStep[] = [\n {\n label: \"Scanning views\",\n run: async () => {\n scanAndWriteViewsDts(root, viewsDir);\n },\n },\n {\n label: \"Compiling server\",\n run: () =>\n rmSync(path.join(root, \"dist\"), { recursive: true, force: true }),\n command: \"tsc -b --force\",\n },\n ];\n\n if (hasViews) {\n steps.push({\n label: \"Building views\",\n command: \"vite build\",\n });\n }\n\n steps.push(\n {\n label: \"Emitting manifest module\",\n run: () => {\n const manifestOut = path.join(root, \"dist\", \"vite-manifest.js\");\n if (hasViews) {\n emitManifestModule(\n path.join(root, \"dist\", \"assets\", \".vite\", \"manifest.json\"),\n manifestOut,\n );\n } else {\n emitEmptyManifestModule(manifestOut);\n }\n },\n },\n {\n label: \"Emitting skills module\",\n run: () => {\n emitSkillsModule(\n resolve(root, \"src/skills\"),\n path.join(root, \"dist\", \"skills.js\"),\n );\n },\n },\n {\n label: \"Emitting entry wrapper\",\n run: () => {\n emitEntryWrapper(path.join(root, \"dist\"));\n },\n },\n {\n label: \"Emitting Cloudflare redirects\",\n run: () => {\n const assetsDir = path.join(root, \"dist\", \"assets\");\n ensureAssetsDir(assetsDir);\n writeFileSync(\n path.join(assetsDir, \"_redirects\"),\n \"/assets/assets/* /assets/:splat 200\\n\",\n );\n },\n },\n {\n label: \"Emitting Cloudflare headers\",\n run: () => {\n const assetsDir = path.join(root, \"dist\", \"assets\");\n ensureAssetsDir(assetsDir);\n writeFileSync(\n path.join(assetsDir, \"_headers\"),\n \"/assets/*\\n Access-Control-Allow-Origin: *\\n\",\n );\n },\n },\n {\n label: \"Emitting Vercel build output\",\n run: () => emitVercelBuildOutput(root, serverExternal),\n },\n );\n\n return steps;\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ // @vitest-environment node
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync, } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import { describe, expect, it } from "vitest";
6
+ import { getCommandSteps } from "./build-steps.js";
7
+ function mkTmp(prefix = "skybridge-build-steps-") {
8
+ return mkdtempSync(path.join(tmpdir(), prefix));
9
+ }
10
+ describe("getCommandSteps", () => {
11
+ it("omits the vite build step when no views are registered", async () => {
12
+ const root = mkTmp();
13
+ mkdirSync(path.join(root, "src"), { recursive: true });
14
+ writeFileSync(path.join(root, "vite.config.ts"), `import { defineConfig } from "vite";
15
+ import { skybridge } from "skybridge/vite";
16
+ export default defineConfig({ plugins: [skybridge()] });`);
17
+ writeFileSync(path.join(root, "tsconfig.json"), JSON.stringify({ compilerOptions: { strict: true } }));
18
+ const steps = await getCommandSteps(root);
19
+ const labels = steps.map((step) => step.label);
20
+ expect(labels).not.toContain("Building views");
21
+ expect(labels).toContain("Emitting manifest module");
22
+ expect(labels.at(-1)).toBe("Emitting Vercel build output");
23
+ });
24
+ it("includes the vite build step when views are present", async () => {
25
+ const root = mkTmp();
26
+ mkdirSync(path.join(root, "src", "views"), { recursive: true });
27
+ writeFileSync(path.join(root, "src", "views", "hello.tsx"), "export default function Hello() { return null; }\n");
28
+ writeFileSync(path.join(root, "vite.config.ts"), `import { defineConfig } from "vite";
29
+ import { skybridge } from "skybridge/vite";
30
+ export default defineConfig({ plugins: [skybridge()] });`);
31
+ writeFileSync(path.join(root, "tsconfig.json"), JSON.stringify({ compilerOptions: { strict: true } }));
32
+ const steps = await getCommandSteps(root);
33
+ const labels = steps.map((step) => step.label);
34
+ expect(labels).toContain("Building views");
35
+ });
36
+ it("emits an empty manifest module when there are no views", async () => {
37
+ const root = mkTmp();
38
+ mkdirSync(path.join(root, "src"), { recursive: true });
39
+ writeFileSync(path.join(root, "vite.config.ts"), `import { defineConfig } from "vite";
40
+ import { skybridge } from "skybridge/vite";
41
+ export default defineConfig({ plugins: [skybridge()] });`);
42
+ const steps = await getCommandSteps(root);
43
+ const emitManifest = steps.find((step) => step.label === "Emitting manifest module");
44
+ expect(emitManifest?.run).toBeDefined();
45
+ mkdirSync(path.join(root, "dist"), { recursive: true });
46
+ await emitManifest?.run?.();
47
+ const manifestOut = path.join(root, "dist", "vite-manifest.js");
48
+ expect(existsSync(manifestOut)).toBe(true);
49
+ expect(readFileSync(manifestOut, "utf-8")).toBe("export default {};\n");
50
+ });
51
+ });
52
+ //# sourceMappingURL=build-steps.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-steps.test.js","sourceRoot":"","sources":["../../src/cli/build-steps.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport {\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { describe, expect, it } from \"vitest\";\nimport { getCommandSteps } from \"./build-steps.js\";\n\nfunction mkTmp(prefix = \"skybridge-build-steps-\") {\n return mkdtempSync(path.join(tmpdir(), prefix));\n}\n\ndescribe(\"getCommandSteps\", () => {\n it(\"omits the vite build step when no views are registered\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"src\"), { recursive: true });\n writeFileSync(\n path.join(root, \"vite.config.ts\"),\n `import { defineConfig } from \"vite\";\nimport { skybridge } from \"skybridge/vite\";\nexport default defineConfig({ plugins: [skybridge()] });`,\n );\n writeFileSync(\n path.join(root, \"tsconfig.json\"),\n JSON.stringify({ compilerOptions: { strict: true } }),\n );\n\n const steps = await getCommandSteps(root);\n const labels = steps.map((step) => step.label);\n\n expect(labels).not.toContain(\"Building views\");\n expect(labels).toContain(\"Emitting manifest module\");\n expect(labels.at(-1)).toBe(\"Emitting Vercel build output\");\n });\n\n it(\"includes the vite build step when views are present\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"src\", \"views\"), { recursive: true });\n writeFileSync(\n path.join(root, \"src\", \"views\", \"hello.tsx\"),\n \"export default function Hello() { return null; }\\n\",\n );\n writeFileSync(\n path.join(root, \"vite.config.ts\"),\n `import { defineConfig } from \"vite\";\nimport { skybridge } from \"skybridge/vite\";\nexport default defineConfig({ plugins: [skybridge()] });`,\n );\n writeFileSync(\n path.join(root, \"tsconfig.json\"),\n JSON.stringify({ compilerOptions: { strict: true } }),\n );\n\n const steps = await getCommandSteps(root);\n const labels = steps.map((step) => step.label);\n\n expect(labels).toContain(\"Building views\");\n });\n\n it(\"emits an empty manifest module when there are no views\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"src\"), { recursive: true });\n writeFileSync(\n path.join(root, \"vite.config.ts\"),\n `import { defineConfig } from \"vite\";\nimport { skybridge } from \"skybridge/vite\";\nexport default defineConfig({ plugins: [skybridge()] });`,\n );\n\n const steps = await getCommandSteps(root);\n const emitManifest = steps.find(\n (step) => step.label === \"Emitting manifest module\",\n );\n expect(emitManifest?.run).toBeDefined();\n\n mkdirSync(path.join(root, \"dist\"), { recursive: true });\n await emitManifest?.run?.();\n\n const manifestOut = path.join(root, \"dist\", \"vite-manifest.js\");\n expect(existsSync(manifestOut)).toBe(true);\n expect(readFileSync(manifestOut, \"utf-8\")).toBe(\"export default {};\\n\");\n });\n});\n"]}
@@ -1,4 +1,4 @@
1
1
  export declare const Header: ({ version, children, }: {
2
2
  version: string;
3
3
  children?: React.ReactNode;
4
- }) => import("react/jsx-runtime").JSX.Element;
4
+ }) => import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ export interface SkybridgeConfig {
2
+ viewsDir?: string;
3
+ serverExternal?: string[];
4
+ }
5
+ export declare function resolveSkybridgeConfig(root: string): Promise<SkybridgeConfig>;
@@ -1,4 +1,4 @@
1
- export async function resolveViewsDir(root) {
1
+ export async function resolveSkybridgeConfig(root) {
2
2
  const { loadConfigFromFile } = await import("vite");
3
3
  const loaded = await loadConfigFromFile({ command: "build", mode: "production" }, undefined, root);
4
4
  const isPluginCandidate = (value) => typeof value === "object" && value !== null;
@@ -12,6 +12,7 @@ export async function resolveViewsDir(root) {
12
12
  }
13
13
  };
14
14
  walk(loaded?.config.plugins ?? []);
15
- return plugins.find((p) => p.name === "skybridge")?.api?.viewsDir;
15
+ const api = plugins.find((p) => p.name === "skybridge")?.api;
16
+ return { viewsDir: api?.viewsDir, serverExternal: api?.serverExternal };
16
17
  }
17
- //# sourceMappingURL=resolve-views-dir.js.map
18
+ //# sourceMappingURL=resolve-skybridge-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-skybridge-config.js","sourceRoot":"","sources":["../../src/cli/resolve-skybridge-config.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAY;IAEZ,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EACxC,SAAS,EACT,IAAI,CACL,CAAC;IAEF,MAAM,iBAAiB,GAAG,CACxB,KAAc,EACqC,EAAE,CACrD,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;IAE9C,MAAM,OAAO,GAAoD,EAAE,CAAC;IACpE,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE;QAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,GAAG,CAAC;IAC7D,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC;AAC1E,CAAC","sourcesContent":["export interface SkybridgeConfig {\n viewsDir?: string;\n serverExternal?: string[];\n}\n\nexport async function resolveSkybridgeConfig(\n root: string,\n): Promise<SkybridgeConfig> {\n const { loadConfigFromFile } = await import(\"vite\");\n const loaded = await loadConfigFromFile(\n { command: \"build\", mode: \"production\" },\n undefined,\n root,\n );\n\n const isPluginCandidate = (\n value: unknown,\n ): value is { name?: string; api?: SkybridgeConfig } =>\n typeof value === \"object\" && value !== null;\n\n const plugins: Array<{ name?: string; api?: SkybridgeConfig }> = [];\n const walk = (value: unknown) => {\n if (Array.isArray(value)) {\n value.forEach(walk);\n } else if (isPluginCandidate(value)) {\n plugins.push(value);\n }\n };\n walk(loaded?.config.plugins ?? []);\n const api = plugins.find((p) => p.name === \"skybridge\")?.api;\n return { viewsDir: api?.viewsDir, serverExternal: api?.serverExternal };\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { TunnelManager } from "./tunnel.js";
2
+ export interface RunPlainOptions {
3
+ env: NodeJS.ProcessEnv;
4
+ port: number;
5
+ fallback: boolean;
6
+ version: string;
7
+ tunnel: boolean;
8
+ tunnelManager: TunnelManager;
9
+ }
10
+ /**
11
+ * Run `skybridge dev` without the Ink UI. The server's stdout is forwarded
12
+ * verbatim to the real stdout so it can be piped through a formatter
13
+ * (`skybridge dev --plain | bunyan`); everything else (banner, URLs, tunnel
14
+ * state, TypeScript errors, restarts) is written to stderr.
15
+ *
16
+ * Returns a cleanup function that tears down nodemon and the tsc watcher.
17
+ */
18
+ export declare function runPlain(options: RunPlainOptions): () => void;