@parlor/cli 0.0.0-stage → 0.2.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -2
  3. package/dist/agent-host-process.d.ts +22 -0
  4. package/dist/agent-host-process.d.ts.map +1 -0
  5. package/dist/agent-host-process.js +35 -0
  6. package/dist/agent-host-process.js.map +1 -0
  7. package/dist/chat-warning.d.ts +8 -0
  8. package/dist/chat-warning.d.ts.map +1 -0
  9. package/dist/chat-warning.js +13 -0
  10. package/dist/chat-warning.js.map +1 -0
  11. package/dist/cloudflare/loader.d.ts +4 -0
  12. package/dist/cloudflare/loader.d.ts.map +1 -0
  13. package/dist/cloudflare/loader.js +11 -0
  14. package/dist/cloudflare/loader.js.map +1 -0
  15. package/dist/cloudflare/platform.d.ts +16 -0
  16. package/dist/cloudflare/platform.d.ts.map +1 -0
  17. package/dist/cloudflare/platform.js +52 -0
  18. package/dist/cloudflare/platform.js.map +1 -0
  19. package/dist/cloudflare/prepare.d.ts +9 -0
  20. package/dist/cloudflare/prepare.d.ts.map +1 -0
  21. package/dist/cloudflare/prepare.js +29 -0
  22. package/dist/cloudflare/prepare.js.map +1 -0
  23. package/dist/cloudflare/workers.d.ts +12 -0
  24. package/dist/cloudflare/workers.d.ts.map +1 -0
  25. package/dist/cloudflare/workers.js +18 -0
  26. package/dist/cloudflare/workers.js.map +1 -0
  27. package/dist/cloudflare/wrangler.d.ts +4 -0
  28. package/dist/cloudflare/wrangler.d.ts.map +1 -0
  29. package/dist/cloudflare/wrangler.js +16 -0
  30. package/dist/cloudflare/wrangler.js.map +1 -0
  31. package/dist/commands/add.d.ts +32 -0
  32. package/dist/commands/add.d.ts.map +1 -0
  33. package/dist/commands/add.js +79 -0
  34. package/dist/commands/add.js.map +1 -0
  35. package/dist/commands/build.d.ts +12 -0
  36. package/dist/commands/build.d.ts.map +1 -0
  37. package/dist/commands/build.js +27 -0
  38. package/dist/commands/build.js.map +1 -0
  39. package/dist/commands/db.d.ts +12 -0
  40. package/dist/commands/db.d.ts.map +1 -0
  41. package/dist/commands/db.js +131 -0
  42. package/dist/commands/db.js.map +1 -0
  43. package/dist/commands/deploy.d.ts +8 -0
  44. package/dist/commands/deploy.d.ts.map +1 -0
  45. package/dist/commands/deploy.js +45 -0
  46. package/dist/commands/deploy.js.map +1 -0
  47. package/dist/commands/dev.d.ts +22 -0
  48. package/dist/commands/dev.d.ts.map +1 -0
  49. package/dist/commands/dev.js +85 -0
  50. package/dist/commands/dev.js.map +1 -0
  51. package/dist/commands/typegen.d.ts +2 -0
  52. package/dist/commands/typegen.d.ts.map +1 -0
  53. package/dist/commands/typegen.js +13 -0
  54. package/dist/commands/typegen.js.map +1 -0
  55. package/dist/desktop-target.d.ts +4 -0
  56. package/dist/desktop-target.d.ts.map +1 -0
  57. package/dist/desktop-target.js +45 -0
  58. package/dist/desktop-target.js.map +1 -0
  59. package/dist/dev-session.d.ts +16 -0
  60. package/dist/dev-session.d.ts.map +1 -0
  61. package/dist/dev-session.js +66 -0
  62. package/dist/dev-session.js.map +1 -0
  63. package/dist/errors.d.ts +4 -0
  64. package/dist/errors.d.ts.map +1 -0
  65. package/dist/errors.js +4 -0
  66. package/dist/errors.js.map +1 -0
  67. package/dist/handlers.d.ts +3 -0
  68. package/dist/handlers.d.ts.map +1 -0
  69. package/dist/handlers.js +12 -0
  70. package/dist/handlers.js.map +1 -0
  71. package/dist/index.d.ts +2 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +2 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/main.d.ts +3 -0
  76. package/dist/main.d.ts.map +1 -0
  77. package/dist/main.js +37 -0
  78. package/dist/main.js.map +1 -0
  79. package/dist/mobile-target.d.ts +5 -0
  80. package/dist/mobile-target.d.ts.map +1 -0
  81. package/dist/mobile-target.js +40 -0
  82. package/dist/mobile-target.js.map +1 -0
  83. package/dist/ports.d.ts +14 -0
  84. package/dist/ports.d.ts.map +1 -0
  85. package/dist/ports.js +45 -0
  86. package/dist/ports.js.map +1 -0
  87. package/dist/serve-entry.d.ts +2 -0
  88. package/dist/serve-entry.d.ts.map +1 -0
  89. package/dist/serve-entry.js +35 -0
  90. package/dist/serve-entry.js.map +1 -0
  91. package/dist/server-process.d.ts +14 -0
  92. package/dist/server-process.d.ts.map +1 -0
  93. package/dist/server-process.js +75 -0
  94. package/dist/server-process.js.map +1 -0
  95. package/dist/serverless-warning.d.ts +4 -0
  96. package/dist/serverless-warning.d.ts.map +1 -0
  97. package/dist/serverless-warning.js +6 -0
  98. package/dist/serverless-warning.js.map +1 -0
  99. package/dist/shutdown.d.ts +6 -0
  100. package/dist/shutdown.d.ts.map +1 -0
  101. package/dist/shutdown.js +22 -0
  102. package/dist/shutdown.js.map +1 -0
  103. package/dist/targets.d.ts +27 -0
  104. package/dist/targets.d.ts.map +1 -0
  105. package/dist/targets.js +31 -0
  106. package/dist/targets.js.map +1 -0
  107. package/dist/web-target.d.ts +6 -0
  108. package/dist/web-target.d.ts.map +1 -0
  109. package/dist/web-target.js +50 -0
  110. package/dist/web-target.js.map +1 -0
  111. package/package.json +60 -3
package/dist/main.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { ConfigError } from "@parlor/config";
3
+ import { defineCommand, runCommand, runMain } from "citty";
4
+ import { add } from "./commands/add.js";
5
+ import { build } from "./commands/build.js";
6
+ import { db } from "./commands/db.js";
7
+ import { deploy } from "./commands/deploy.js";
8
+ import { dev } from "./commands/dev.js";
9
+ import { typegen } from "./commands/typegen.js";
10
+ import { CliError } from "./errors.js";
11
+ import { shutdownAfterError } from "./shutdown.js";
12
+ const main = defineCommand({
13
+ meta: { name: "parlor", description: "Parlor CLI" },
14
+ subCommands: { dev, build, deploy, db, add, typegen },
15
+ });
16
+ function isHelp(arg) {
17
+ return arg === "--help" || arg === "-h";
18
+ }
19
+ async function run(rawArgs) {
20
+ if (rawArgs.some(isHelp)) {
21
+ await runMain(main, { rawArgs });
22
+ return;
23
+ }
24
+ await runCommand(main, { rawArgs });
25
+ }
26
+ try {
27
+ await run(process.argv.slice(2));
28
+ }
29
+ catch (error) {
30
+ await shutdownAfterError();
31
+ if (error instanceof CliError || error instanceof ConfigError) {
32
+ console.error(error.message);
33
+ process.exit(1);
34
+ }
35
+ throw error;
36
+ }
37
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,IAAI,GAAG,aAAa,CAAC;IACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE;IACnD,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;CACtD,CAAC,CAAC;AAEH,SAAS,MAAM,CAAC,GAAW;IACzB,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC;AAC1C,CAAC;AAED,KAAK,UAAU,GAAG,CAAC,OAAiB;IAClC,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IACD,MAAM,UAAU,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,IAAI,CAAC;IACH,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,MAAM,kBAAkB,EAAE,CAAC;IAC3B,IAAI,KAAK,YAAY,QAAQ,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;QAC9D,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { ConfigError } from \"@parlor/config\";\nimport { defineCommand, runCommand, runMain } from \"citty\";\n\nimport { add } from \"./commands/add.js\";\nimport { build } from \"./commands/build.js\";\nimport { db } from \"./commands/db.js\";\nimport { deploy } from \"./commands/deploy.js\";\nimport { dev } from \"./commands/dev.js\";\nimport { typegen } from \"./commands/typegen.js\";\nimport { CliError } from \"./errors.js\";\nimport { shutdownAfterError } from \"./shutdown.js\";\n\nconst main = defineCommand({\n meta: { name: \"parlor\", description: \"Parlor CLI\" },\n subCommands: { dev, build, deploy, db, add, typegen },\n});\n\nfunction isHelp(arg: string): boolean {\n return arg === \"--help\" || arg === \"-h\";\n}\n\nasync function run(rawArgs: string[]): Promise<void> {\n if (rawArgs.some(isHelp)) {\n await runMain(main, { rawArgs });\n return;\n }\n await runCommand(main, { rawArgs });\n}\n\ntry {\n await run(process.argv.slice(2));\n} catch (error) {\n await shutdownAfterError();\n if (error instanceof CliError || error instanceof ConfigError) {\n console.error(error.message);\n process.exit(1);\n }\n throw error;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { type MobilePlatform } from "@parlor/shell-mobile";
2
+ import type { TargetHandler } from "./targets.js";
3
+ export declare function assertIosHost(platform: MobilePlatform, host?: NodeJS.Platform): void;
4
+ export declare function mobileTarget(platform: MobilePlatform): TargetHandler;
5
+ //# sourceMappingURL=mobile-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-target.d.ts","sourceRoot":"","sources":["../src/mobile-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAEpB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,cAAc,EACxB,IAAI,GAAE,MAAM,CAAC,QAA2B,QAKzC;AAYD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,cAAc,GAAG,aAAa,CAsBpE"}
@@ -0,0 +1,40 @@
1
+ import { buildMobile, CapacitorCliError, startMobileDev, } from "@parlor/shell-mobile";
2
+ import { CliError } from "./errors.js";
3
+ import { onShutdown } from "./shutdown.js";
4
+ export function assertIosHost(platform, host = process.platform) {
5
+ if (platform === "ios" && host !== "darwin") {
6
+ throw new CliError('Target "ios" can only be built on macOS.');
7
+ }
8
+ }
9
+ async function surfaceCapacitorErrors(run) {
10
+ try {
11
+ return await run();
12
+ }
13
+ catch (error) {
14
+ if (error instanceof CapacitorCliError)
15
+ throw new CliError(error.message, { cause: error });
16
+ throw error;
17
+ }
18
+ }
19
+ export function mobileTarget(platform) {
20
+ return {
21
+ assertHost: () => assertIosHost(platform),
22
+ async dev(loaded, options) {
23
+ await surfaceCapacitorErrors(async () => {
24
+ const controller = new AbortController();
25
+ onShutdown(() => controller.abort());
26
+ await startMobileDev(loaded, platform, {
27
+ device: options.device,
28
+ signal: controller.signal,
29
+ appOrigin: options.appOrigin,
30
+ });
31
+ });
32
+ },
33
+ async build(loaded, _options) {
34
+ assertIosHost(platform);
35
+ const projectDir = await surfaceCapacitorErrors(() => buildMobile(loaded, platform));
36
+ console.log(`${platform} project synced at ${projectDir}`);
37
+ },
38
+ };
39
+ }
40
+ //# sourceMappingURL=mobile-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-target.js","sourceRoot":"","sources":["../src/mobile-target.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,iBAAiB,EAEjB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,MAAM,UAAU,aAAa,CAC3B,QAAwB,EACxB,IAAI,GAAoB,OAAO,CAAC,QAAQ;IAExC,IAAI,QAAQ,KAAK,KAAK,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,QAAQ,CAAC,0CAA0C,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAI,GAAqB;IAC5D,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB;YACpC,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAwB;IACnD,OAAO;QACL,UAAU,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC;QACzC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO;YACvB,MAAM,sBAAsB,CAAC,KAAK,IAAI,EAAE;gBACtC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;gBACzC,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;gBACrC,MAAM,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE;oBACrC,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,SAAS,EAAE,OAAO,CAAC,SAAS;iBAC7B,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ;YAC1B,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxB,MAAM,UAAU,GAAG,MAAM,sBAAsB,CAAC,GAAG,EAAE,CACnD,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAC9B,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,sBAAsB,UAAU,EAAE,CAAC,CAAC;QAC7D,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import {\n buildMobile,\n CapacitorCliError,\n type MobilePlatform,\n startMobileDev,\n} from \"@parlor/shell-mobile\";\n\nimport { CliError } from \"./errors.js\";\nimport { onShutdown } from \"./shutdown.js\";\nimport type { TargetHandler } from \"./targets.js\";\n\nexport function assertIosHost(\n platform: MobilePlatform,\n host: NodeJS.Platform = process.platform,\n) {\n if (platform === \"ios\" && host !== \"darwin\") {\n throw new CliError('Target \"ios\" can only be built on macOS.');\n }\n}\n\nasync function surfaceCapacitorErrors<T>(run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (error) {\n if (error instanceof CapacitorCliError)\n throw new CliError(error.message, { cause: error });\n throw error;\n }\n}\n\nexport function mobileTarget(platform: MobilePlatform): TargetHandler {\n return {\n assertHost: () => assertIosHost(platform),\n async dev(loaded, options) {\n await surfaceCapacitorErrors(async () => {\n const controller = new AbortController();\n onShutdown(() => controller.abort());\n await startMobileDev(loaded, platform, {\n device: options.device,\n signal: controller.signal,\n appOrigin: options.appOrigin,\n });\n });\n },\n async build(loaded, _options) {\n assertIosHost(platform);\n const projectDir = await surfaceCapacitorErrors(() =>\n buildMobile(loaded, platform),\n );\n console.log(`${platform} project synced at ${projectDir}`);\n },\n };\n}\n"]}
@@ -0,0 +1,14 @@
1
+ export declare function isPortFree(port: number): Promise<boolean>;
2
+ export declare function firstFreePort(from: number, taken?: ReadonlySet<number>): Promise<number>;
3
+ export interface DevPorts {
4
+ readonly server: number;
5
+ /** One per target, in the order asked; the first starts at the wanted web port. */
6
+ readonly web: readonly number[];
7
+ }
8
+ /** Moves each busy port to the next free one, saying so, as Vite and Next.js do. */
9
+ export declare function resolveDevPorts(wanted: {
10
+ readonly server: number;
11
+ readonly web: number;
12
+ readonly targets?: number;
13
+ }, log?: (line: string) => void): Promise<DevPorts>;
14
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAqBA,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK/D;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,WAAW,CAAC,MAAM,CAAa,GACrC,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CACnC,MAAM,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACpF,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,GACxC,OAAO,CAAC,QAAQ,CAAC,CAenB"}
package/dist/ports.js ADDED
@@ -0,0 +1,45 @@
1
+ import { createServer } from "node:net";
2
+ import { CliError } from "./errors.js";
3
+ // The server binds every interface and Vite binds `localhost`, so a holder on any of these takes the port.
4
+ const HOSTS = ["0.0.0.0", "::", "127.0.0.1", "::1"];
5
+ const NO_SUCH_ADDRESS = new Set(["EADDRNOTAVAIL", "EAFNOSUPPORT"]);
6
+ const ATTEMPTS = 100;
7
+ function bindable(port, host) {
8
+ return new Promise((resolve) => {
9
+ const server = createServer();
10
+ server.once("error", (error) => resolve(NO_SUCH_ADDRESS.has(error.code ?? "")));
11
+ server.listen({ port, host, exclusive: true }, () => server.close(() => resolve(true)));
12
+ });
13
+ }
14
+ export async function isPortFree(port) {
15
+ for (const host of HOSTS) {
16
+ if (!(await bindable(port, host)))
17
+ return false;
18
+ }
19
+ return true;
20
+ }
21
+ export async function firstFreePort(from, taken = new Set()) {
22
+ for (let port = from; port < from + ATTEMPTS && port <= 65_535; port += 1) {
23
+ if (!taken.has(port) && (await isPortFree(port)))
24
+ return port;
25
+ }
26
+ throw new CliError(`No free port from ${from} to ${from + ATTEMPTS - 1}.`);
27
+ }
28
+ /** Moves each busy port to the next free one, saying so, as Vite and Next.js do. */
29
+ export async function resolveDevPorts(wanted, log = console.log) {
30
+ const server = await firstFreePort(wanted.server);
31
+ const taken = new Set([server]);
32
+ const web = [];
33
+ for (let index = 0; index < (wanted.targets ?? 1); index += 1) {
34
+ const port = await firstFreePort(web.at(-1) ?? wanted.web, taken);
35
+ taken.add(port);
36
+ web.push(port);
37
+ }
38
+ if (server !== wanted.server) {
39
+ log(`Port ${wanted.server} is in use, so the server uses ${server}.`);
40
+ }
41
+ if (web[0] !== wanted.web)
42
+ log(`Port ${wanted.web} is in use, so the page uses ${web[0]}.`);
43
+ return { server, web };
44
+ }
45
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from \"node:net\";\n\nimport { CliError } from \"./errors.js\";\n\n// The server binds every interface and Vite binds `localhost`, so a holder on any of these takes the port.\nconst HOSTS = [\"0.0.0.0\", \"::\", \"127.0.0.1\", \"::1\"] as const;\nconst NO_SUCH_ADDRESS = new Set([\"EADDRNOTAVAIL\", \"EAFNOSUPPORT\"]);\nconst ATTEMPTS = 100;\n\nfunction bindable(port: number, host: string): Promise<boolean> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once(\"error\", (error: NodeJS.ErrnoException) =>\n resolve(NO_SUCH_ADDRESS.has(error.code ?? \"\")),\n );\n server.listen({ port, host, exclusive: true }, () =>\n server.close(() => resolve(true)),\n );\n });\n}\n\nexport async function isPortFree(port: number): Promise<boolean> {\n for (const host of HOSTS) {\n if (!(await bindable(port, host))) return false;\n }\n return true;\n}\n\nexport async function firstFreePort(\n from: number,\n taken: ReadonlySet<number> = new Set(),\n): Promise<number> {\n for (let port = from; port < from + ATTEMPTS && port <= 65_535; port += 1) {\n if (!taken.has(port) && (await isPortFree(port))) return port;\n }\n throw new CliError(`No free port from ${from} to ${from + ATTEMPTS - 1}.`);\n}\n\nexport interface DevPorts {\n readonly server: number;\n /** One per target, in the order asked; the first starts at the wanted web port. */\n readonly web: readonly number[];\n}\n\n/** Moves each busy port to the next free one, saying so, as Vite and Next.js do. */\nexport async function resolveDevPorts(\n wanted: { readonly server: number; readonly web: number; readonly targets?: number },\n log: (line: string) => void = console.log,\n): Promise<DevPorts> {\n const server = await firstFreePort(wanted.server);\n const taken = new Set([server]);\n const web: number[] = [];\n for (let index = 0; index < (wanted.targets ?? 1); index += 1) {\n const port = await firstFreePort(web.at(-1) ?? wanted.web, taken);\n taken.add(port);\n web.push(port);\n }\n if (server !== wanted.server) {\n log(`Port ${wanted.server} is in use, so the server uses ${server}.`);\n }\n if (web[0] !== wanted.web)\n log(`Port ${wanted.web} is in use, so the page uses ${web[0]}.`);\n return { server, web };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serve-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-entry.d.ts","sourceRoot":"","sources":["../src/serve-entry.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { join } from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+ import { parseArgs } from "node:util";
4
+ import { UI_MANIFEST_FILE } from "@parlor/agent-ui";
5
+ import { fileManifestSource } from "@parlor/agent-ui/server";
6
+ import { generatedDir } from "@parlor/config";
7
+ import { serve } from "srvx";
8
+ import { registerCloudflareWorkers } from "./cloudflare/loader.js";
9
+ import { startCloudflarePlatform } from "./cloudflare/platform.js";
10
+ import { onShutdown } from "./shutdown.js";
11
+ const { positionals: [entry], values: options, } = parseArgs({ allowPositionals: true, options: { cloudflare: { type: "string" } } });
12
+ if (!entry)
13
+ throw new Error("serve-entry needs the server entry path");
14
+ const cloudflare = options.cloudflare
15
+ ? await startCloudflarePlatform(process.cwd(), JSON.parse(options.cloudflare))
16
+ : undefined;
17
+ registerCloudflareWorkers();
18
+ const loaded = (await import(pathToFileURL(entry).href).catch(async (error) => {
19
+ await cloudflare?.dispose();
20
+ throw error;
21
+ }));
22
+ const handler = loaded.default?.fetch;
23
+ loaded.default?.installUiManifest?.(fileManifestSource(join(generatedDir(process.cwd()), UI_MANIFEST_FILE)));
24
+ if (handler || cloudflare) {
25
+ onShutdown(async () => {
26
+ await loaded.default?.close?.();
27
+ await cloudflare?.dispose();
28
+ });
29
+ }
30
+ if (handler) {
31
+ const port = Number(process.env.PORT ?? 3000);
32
+ serve({ fetch: (request) => handler(request), port, silent: true });
33
+ console.log(`parlor server listening on http://localhost:${port}`);
34
+ }
35
+ //# sourceMappingURL=serve-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-entry.js","sourceRoot":"","sources":["../src/serve-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAyB,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,EACJ,WAAW,EAAE,CAAC,KAAK,CAAC,EACpB,MAAM,EAAE,OAAO,GAChB,GAAG,SAAS,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;AACvF,IAAI,CAAC,KAAK;IAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;AAEvE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU;IACnC,CAAC,CAAC,MAAM,uBAAuB,CAC3B,OAAO,CAAC,GAAG,EAAE,EACb,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAkB,CAChD;IACH,CAAC,CAAC,SAAS,CAAC;AACd,yBAAyB,EAAE,CAAC;AAE5B,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAc,EAAE,EAAE;IACrF,MAAM,UAAU,EAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,KAAK,CAAC;AACd,CAAC,CAAC,CAMD,CAAC;AACF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;AACtC,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,CACjC,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC,CACxE,CAAC;AAEF,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;IAC1B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;QAChC,MAAM,UAAU,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AACD,IAAI,OAAO,EAAE,CAAC;IACZ,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,OAAO,CAAC,GAAG,CAAC,+CAA+C,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC","sourcesContent":["import { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { parseArgs } from \"node:util\";\n\nimport { UI_MANIFEST_FILE } from \"@parlor/agent-ui\";\nimport { fileManifestSource, type UiManifestSource } from \"@parlor/agent-ui/server\";\nimport { generatedDir } from \"@parlor/config\";\nimport { serve } from \"srvx\";\n\nimport { registerCloudflareWorkers } from \"./cloudflare/loader.js\";\nimport { startCloudflarePlatform } from \"./cloudflare/platform.js\";\nimport type { CloudflareDev } from \"./cloudflare/prepare.js\";\nimport { onShutdown } from \"./shutdown.js\";\n\nconst {\n positionals: [entry],\n values: options,\n} = parseArgs({ allowPositionals: true, options: { cloudflare: { type: \"string\" } } });\nif (!entry) throw new Error(\"serve-entry needs the server entry path\");\n\nconst cloudflare = options.cloudflare\n ? await startCloudflarePlatform(\n process.cwd(),\n JSON.parse(options.cloudflare) as CloudflareDev,\n )\n : undefined;\nregisterCloudflareWorkers();\n\nconst loaded = (await import(pathToFileURL(entry).href).catch(async (error: unknown) => {\n await cloudflare?.dispose();\n throw error;\n})) as {\n default?: {\n fetch?: (request: Request) => Promise<Response> | Response;\n installUiManifest?: (source: UiManifestSource) => void;\n close?: () => Promise<void>;\n };\n};\nconst handler = loaded.default?.fetch;\nloaded.default?.installUiManifest?.(\n fileManifestSource(join(generatedDir(process.cwd()), UI_MANIFEST_FILE)),\n);\n\nif (handler || cloudflare) {\n onShutdown(async () => {\n await loaded.default?.close?.();\n await cloudflare?.dispose();\n });\n}\nif (handler) {\n const port = Number(process.env.PORT ?? 3000);\n serve({ fetch: (request) => handler(request), port, silent: true });\n console.log(`parlor server listening on http://localhost:${port}`);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import { type ChildProcess } from "node:child_process";
2
+ import { type LoadedConfig, type ParlorConfig } from "@parlor/config";
3
+ import type { CloudflareDev } from "./cloudflare/prepare.js";
4
+ /**
5
+ * The first of `frontends` is the app's origin; the others are the other targets of the same
6
+ * `parlor dev`, which the server trusts too.
7
+ */
8
+ export declare function serverEnv(config: ParlorConfig, base?: NodeJS.ProcessEnv, frontends?: readonly string[]): NodeJS.ProcessEnv;
9
+ export declare const SERVER_SELF_EXIT_GRACE_MS = 2000;
10
+ export declare function stopServerProcess(child: ChildProcess, graceMs?: number): Promise<void>;
11
+ /** Lets the page's first requests reach the server instead of failing in Vite's proxy. */
12
+ export declare function untilListening(child: ChildProcess, port: number, intervalMs?: number): Promise<void>;
13
+ export declare function startServerProcess({ config, rootDir }: LoadedConfig, frontends?: readonly string[], cloudflare?: CloudflareDev): ChildProcess;
14
+ //# sourceMappingURL=server-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-process.d.ts","sourceRoot":"","sources":["../src/server-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAM9D,OAAO,EAAgB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAK7D;;;GAGG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,MAAM,CAAC,UAAwB,EACrC,SAAS,GAAE,SAAS,MAAM,EAA4C,GACrE,MAAM,CAAC,UAAU,CAUnB;AAED,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAG9C,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,MAAkC,GAC1C,OAAO,CAAC,IAAI,CAAC,CASf;AAaD,0FAA0F;AAC1F,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,UAAU,SAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAgB,kBAAkB,CAChC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,YAAY,EACjC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,EAC7B,UAAU,CAAC,EAAE,aAAa,GACzB,YAAY,CAyBd"}
@@ -0,0 +1,75 @@
1
+ import { spawn } from "node:child_process";
2
+ import { once } from "node:events";
3
+ import { createRequire } from "node:module";
4
+ import { connect } from "node:net";
5
+ import { join } from "node:path";
6
+ import { appServerEnv } from "@parlor/config";
7
+ import { onShutdown } from "./shutdown.js";
8
+ const require = createRequire(import.meta.url);
9
+ /**
10
+ * The first of `frontends` is the app's origin; the others are the other targets of the same
11
+ * `parlor dev`, which the server trusts too.
12
+ */
13
+ export function serverEnv(config, base = process.env, frontends = [`http://localhost:${config.web.port}`]) {
14
+ const [origin, ...others] = frontends;
15
+ return {
16
+ ...base,
17
+ PORT: String(config.server.port),
18
+ PARLOR_WEB_ORIGIN: origin,
19
+ ...(others.length > 0 ? { PARLOR_DEV_ORIGINS: others.join(",") } : {}),
20
+ PARLOR_DEV: "1",
21
+ ...appServerEnv(config),
22
+ };
23
+ }
24
+ export const SERVER_SELF_EXIT_GRACE_MS = 2000;
25
+ // A second signal makes tsx watch SIGKILL its child, and the process group usually delivered the first.
26
+ export async function stopServerProcess(child, graceMs = SERVER_SELF_EXIT_GRACE_MS) {
27
+ if (child.exitCode !== null || child.signalCode !== null)
28
+ return;
29
+ const exited = once(child, "exit");
30
+ const nudge = setTimeout(() => child.kill("SIGTERM"), graceMs);
31
+ try {
32
+ await exited;
33
+ }
34
+ finally {
35
+ clearTimeout(nudge);
36
+ }
37
+ }
38
+ function accepts(port) {
39
+ return new Promise((resolve) => {
40
+ const socket = connect(port, "localhost");
41
+ socket.once("connect", () => {
42
+ socket.destroy();
43
+ resolve(true);
44
+ });
45
+ socket.once("error", () => resolve(false));
46
+ });
47
+ }
48
+ /** Lets the page's first requests reach the server instead of failing in Vite's proxy. */
49
+ export async function untilListening(child, port, intervalMs = 100) {
50
+ while (child.exitCode === null && child.signalCode === null) {
51
+ if (await accepts(port))
52
+ return;
53
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
54
+ }
55
+ }
56
+ export function startServerProcess({ config, rootDir }, frontends, cloudflare) {
57
+ const tsxBin = require.resolve("tsx/cli");
58
+ const runner = new URL("./serve-entry.ts", import.meta.url).pathname.replace(/\.ts$/, import.meta.url.endsWith(".js") ? ".js" : ".ts");
59
+ const entry = join(rootDir, config.server.entry);
60
+ const child = spawn(process.execPath, [
61
+ tsxBin,
62
+ "watch",
63
+ "--clear-screen=false",
64
+ runner,
65
+ entry,
66
+ ...(cloudflare ? [`--cloudflare=${JSON.stringify(cloudflare)}`] : []),
67
+ ], {
68
+ cwd: rootDir,
69
+ stdio: "inherit",
70
+ env: serverEnv(config, process.env, frontends),
71
+ });
72
+ onShutdown((signaled) => stopServerProcess(child, signaled ? undefined : 0));
73
+ return child;
74
+ }
75
+ //# sourceMappingURL=server-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-process.js","sourceRoot":"","sources":["../src/server-process.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { once } from \"node:events\";\nimport { createRequire } from \"node:module\";\nimport { connect } from \"node:net\";\nimport { join } from \"node:path\";\n\nimport { appServerEnv, type LoadedConfig, type ParlorConfig } from \"@parlor/config\";\n\nimport type { CloudflareDev } from \"./cloudflare/prepare.js\";\nimport { onShutdown } from \"./shutdown.js\";\n\nconst require = createRequire(import.meta.url);\n\n/**\n * The first of `frontends` is the app's origin; the others are the other targets of the same\n * `parlor dev`, which the server trusts too.\n */\nexport function serverEnv(\n config: ParlorConfig,\n base: NodeJS.ProcessEnv = process.env,\n frontends: readonly string[] = [`http://localhost:${config.web.port}`],\n): NodeJS.ProcessEnv {\n const [origin, ...others] = frontends;\n return {\n ...base,\n PORT: String(config.server.port),\n PARLOR_WEB_ORIGIN: origin,\n ...(others.length > 0 ? { PARLOR_DEV_ORIGINS: others.join(\",\") } : {}),\n PARLOR_DEV: \"1\",\n ...appServerEnv(config),\n };\n}\n\nexport const SERVER_SELF_EXIT_GRACE_MS = 2000;\n\n// A second signal makes tsx watch SIGKILL its child, and the process group usually delivered the first.\nexport async function stopServerProcess(\n child: ChildProcess,\n graceMs: number = SERVER_SELF_EXIT_GRACE_MS,\n): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return;\n const exited = once(child, \"exit\");\n const nudge = setTimeout(() => child.kill(\"SIGTERM\"), graceMs);\n try {\n await exited;\n } finally {\n clearTimeout(nudge);\n }\n}\n\nfunction accepts(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const socket = connect(port, \"localhost\");\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"error\", () => resolve(false));\n });\n}\n\n/** Lets the page's first requests reach the server instead of failing in Vite's proxy. */\nexport async function untilListening(\n child: ChildProcess,\n port: number,\n intervalMs = 100,\n): Promise<void> {\n while (child.exitCode === null && child.signalCode === null) {\n if (await accepts(port)) return;\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function startServerProcess(\n { config, rootDir }: LoadedConfig,\n frontends?: readonly string[],\n cloudflare?: CloudflareDev,\n): ChildProcess {\n const tsxBin = require.resolve(\"tsx/cli\");\n const runner = new URL(\"./serve-entry.ts\", import.meta.url).pathname.replace(\n /\\.ts$/,\n import.meta.url.endsWith(\".js\") ? \".js\" : \".ts\",\n );\n const entry = join(rootDir, config.server.entry);\n const child = spawn(\n process.execPath,\n [\n tsxBin,\n \"watch\",\n \"--clear-screen=false\",\n runner,\n entry,\n ...(cloudflare ? [`--cloudflare=${JSON.stringify(cloudflare)}`] : []),\n ],\n {\n cwd: rootDir,\n stdio: \"inherit\",\n env: serverEnv(config, process.env, frontends),\n },\n );\n onShutdown((signaled) => stopServerProcess(child, signaled ? undefined : 0));\n return child;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export declare const SERVERLESS_UNSUPPORTED_WARNING = "Warning: serverless presets are not supported yet. Pages and actions work, but agents cannot see or use the screen (ui_* tools), actions that need the user's approval always fail when an agent calls them, and the agent sidebar replies without streaming. Deploy with node-server or bun for now.";
2
+ /** A build whose sync polls is a serverless one: its functions cannot hold a stream. */
3
+ export declare function serverlessWarning(syncTransport: "sse" | "poll"): string | undefined;
4
+ //# sourceMappingURL=serverless-warning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverless-warning.d.ts","sourceRoot":"","sources":["../src/serverless-warning.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,0SAC8P,CAAC;AAE1S,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnF"}
@@ -0,0 +1,6 @@
1
+ export const SERVERLESS_UNSUPPORTED_WARNING = "Warning: serverless presets are not supported yet. Pages and actions work, but agents cannot see or use the screen (ui_* tools), actions that need the user's approval always fail when an agent calls them, and the agent sidebar replies without streaming. Deploy with node-server or bun for now.";
2
+ /** A build whose sync polls is a serverless one: its functions cannot hold a stream. */
3
+ export function serverlessWarning(syncTransport) {
4
+ return syncTransport === "poll" ? SERVERLESS_UNSUPPORTED_WARNING : undefined;
5
+ }
6
+ //# sourceMappingURL=serverless-warning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverless-warning.js","sourceRoot":"","sources":["../src/serverless-warning.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,8BAA8B,GACzC,uSAAuS,CAAC;AAE1S,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAAC,aAA6B;IAC7D,OAAO,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,CAAC","sourcesContent":["export const SERVERLESS_UNSUPPORTED_WARNING =\n \"Warning: serverless presets are not supported yet. Pages and actions work, but agents cannot see or use the screen (ui_* tools), actions that need the user's approval always fail when an agent calls them, and the agent sidebar replies without streaming. Deploy with node-server or bun for now.\";\n\n/** A build whose sync polls is a serverless one: its functions cannot hold a stream. */\nexport function serverlessWarning(syncTransport: \"sse\" | \"poll\"): string | undefined {\n return syncTransport === \"poll\" ? SERVERLESS_UNSUPPORTED_WARNING : undefined;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /** `signaled` is false when the CLI stops on its own error, so no signal reached its children. */
2
+ type Cleanup = (signaled: boolean) => void | Promise<void>;
3
+ export declare function shutdownAfterError(): Promise<void>;
4
+ export declare function onShutdown(cleanup: Cleanup): void;
5
+ export {};
6
+ //# sourceMappingURL=shutdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,KAAK,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAc3D,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAElD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAOjD"}
@@ -0,0 +1,22 @@
1
+ const cleanups = [];
2
+ let installed = false;
3
+ async function runCleanups(signaled) {
4
+ await Promise.allSettled(cleanups.splice(0).map((cleanup) => cleanup(signaled)));
5
+ }
6
+ async function shutdown(signal) {
7
+ await runCleanups(true);
8
+ process.kill(process.pid, signal);
9
+ }
10
+ export function shutdownAfterError() {
11
+ return runCleanups(false);
12
+ }
13
+ export function onShutdown(cleanup) {
14
+ cleanups.push(cleanup);
15
+ if (installed)
16
+ return;
17
+ installed = true;
18
+ for (const signal of ["SIGINT", "SIGTERM"]) {
19
+ process.once(signal, () => void shutdown(signal));
20
+ }
21
+ }
22
+ //# sourceMappingURL=shutdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"AAGA,MAAM,QAAQ,GAAc,EAAE,CAAC;AAC/B,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB,KAAK,UAAU,WAAW,CAAC,QAAiB;IAC1C,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,MAAsB;IAC5C,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,IAAI,SAAS;QAAE,OAAO;IACtB,SAAS,GAAG,IAAI,CAAC;IACjB,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC","sourcesContent":["/** `signaled` is false when the CLI stops on its own error, so no signal reached its children. */\ntype Cleanup = (signaled: boolean) => void | Promise<void>;\n\nconst cleanups: Cleanup[] = [];\nlet installed = false;\n\nasync function runCleanups(signaled: boolean): Promise<void> {\n await Promise.allSettled(cleanups.splice(0).map((cleanup) => cleanup(signaled)));\n}\n\nasync function shutdown(signal: NodeJS.Signals): Promise<void> {\n await runCleanups(true);\n process.kill(process.pid, signal);\n}\n\nexport function shutdownAfterError(): Promise<void> {\n return runCleanups(false);\n}\n\nexport function onShutdown(cleanup: Cleanup): void {\n cleanups.push(cleanup);\n if (installed) return;\n installed = true;\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const) {\n process.once(signal, () => void shutdown(signal));\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { LoadedConfig } from "@parlor/config";
2
+ import { CliError } from "./errors.js";
3
+ export declare const TARGETS: readonly ["web", "ios", "android", "mac", "windows", "linux"];
4
+ export type Target = (typeof TARGETS)[number];
5
+ export interface DevOptions {
6
+ readonly device?: string | undefined;
7
+ /** The web target's origin when this target runs next to it, so both pair once. */
8
+ readonly appOrigin?: string | undefined;
9
+ }
10
+ export interface BuildOptions {
11
+ readonly preset?: string | undefined;
12
+ }
13
+ export interface TargetHandler {
14
+ /** Throws when this machine cannot run the target, before `parlor dev` starts anything. */
15
+ assertHost?(): void;
16
+ /** Starts this target's frontend; `parlor dev` already runs the server it talks to. */
17
+ dev(loaded: LoadedConfig, options: DevOptions): Promise<void>;
18
+ build(loaded: LoadedConfig, options: BuildOptions): Promise<void>;
19
+ }
20
+ export declare class TargetError extends CliError {
21
+ readonly name = "TargetError";
22
+ }
23
+ export declare function parseTarget(value: unknown): Target;
24
+ /** `web,mac` runs both against one server; the first is the default `web`. */
25
+ export declare function parseTargets(value: unknown): readonly Target[];
26
+ export declare function resolveHandler(handlers: Partial<Record<Target, TargetHandler>>, target: Target): TargetHandler;
27
+ //# sourceMappingURL=targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,eAAO,MAAM,OAAO,YAAI,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAU,CAAC;AACrF,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,2FAA2F;IAC3F,UAAU,CAAC,IAAI,IAAI,CAAC;IACpB,uFAAuF;IACvF,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AAED,qBAAa,WAAY,SAAQ,QAAQ;IACvC,SAAkB,IAAI,iBAAiB;CACxC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQlD;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAQ9D;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,EAChD,MAAM,EAAE,MAAM,GACb,aAAa,CAMf"}
@@ -0,0 +1,31 @@
1
+ import { CliError } from "./errors.js";
2
+ export const TARGETS = ["web", "ios", "android", "mac", "windows", "linux"];
3
+ export class TargetError extends CliError {
4
+ name = "TargetError";
5
+ }
6
+ export function parseTarget(value) {
7
+ const target = value === undefined ? "web" : String(value);
8
+ if (!TARGETS.includes(target)) {
9
+ throw new TargetError(`Unknown target "${target}". Expected one of: ${TARGETS.join(", ")}`);
10
+ }
11
+ return target;
12
+ }
13
+ /** `web,mac` runs both against one server; the first is the default `web`. */
14
+ export function parseTargets(value) {
15
+ const names = (value === undefined ? "web" : String(value))
16
+ .split(",")
17
+ .map((name) => name.trim())
18
+ .filter(Boolean);
19
+ const targets = [...new Set(names.map(parseTarget))];
20
+ if (targets.length === 0)
21
+ return ["web"];
22
+ return targets;
23
+ }
24
+ export function resolveHandler(handlers, target) {
25
+ const handler = handlers[target];
26
+ if (!handler) {
27
+ throw new TargetError(`Target "${target}" is not supported yet`);
28
+ }
29
+ return handler;
30
+ }
31
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAU,CAAC;AAqBrF,MAAM,OAAO,WAAY,SAAQ,QAAQ;IACrB,IAAI,GAAG,aAAa,CAAC;CACxC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3D,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAgB,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,WAAW,CACnB,mBAAmB,MAAM,uBAAuB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,MAAgB,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,KAAK,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;SACxD,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,QAAgD,EAChD,MAAc;IAEd,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,WAAW,MAAM,wBAAwB,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import type { LoadedConfig } from \"@parlor/config\";\n\nimport { CliError } from \"./errors.js\";\n\nexport const TARGETS = [\"web\", \"ios\", \"android\", \"mac\", \"windows\", \"linux\"] as const;\nexport type Target = (typeof TARGETS)[number];\n\nexport interface DevOptions {\n readonly device?: string | undefined;\n /** The web target's origin when this target runs next to it, so both pair once. */\n readonly appOrigin?: string | undefined;\n}\n\nexport interface BuildOptions {\n readonly preset?: string | undefined;\n}\n\nexport interface TargetHandler {\n /** Throws when this machine cannot run the target, before `parlor dev` starts anything. */\n assertHost?(): void;\n /** Starts this target's frontend; `parlor dev` already runs the server it talks to. */\n dev(loaded: LoadedConfig, options: DevOptions): Promise<void>;\n build(loaded: LoadedConfig, options: BuildOptions): Promise<void>;\n}\n\nexport class TargetError extends CliError {\n override readonly name = \"TargetError\";\n}\n\nexport function parseTarget(value: unknown): Target {\n const target = value === undefined ? \"web\" : String(value);\n if (!TARGETS.includes(target as Target)) {\n throw new TargetError(\n `Unknown target \"${target}\". Expected one of: ${TARGETS.join(\", \")}`,\n );\n }\n return target as Target;\n}\n\n/** `web,mac` runs both against one server; the first is the default `web`. */\nexport function parseTargets(value: unknown): readonly Target[] {\n const names = (value === undefined ? \"web\" : String(value))\n .split(\",\")\n .map((name) => name.trim())\n .filter(Boolean);\n const targets = [...new Set(names.map(parseTarget))];\n if (targets.length === 0) return [\"web\"];\n return targets;\n}\n\nexport function resolveHandler(\n handlers: Partial<Record<Target, TargetHandler>>,\n target: Target,\n): TargetHandler {\n const handler = handlers[target];\n if (!handler) {\n throw new TargetError(`Target \"${target}\" is not supported yet`);\n }\n return handler;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { LoadedConfig } from "@parlor/config";
2
+ import { type ServerBuildResult } from "@parlor/host";
3
+ import type { BuildOptions, TargetHandler } from "./targets.js";
4
+ export declare function buildWebAndServer(loaded: LoadedConfig, options: BuildOptions): Promise<ServerBuildResult>;
5
+ export declare const webTarget: TargetHandler;
6
+ //# sourceMappingURL=web-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-target.d.ts","sourceRoot":"","sources":["../src/web-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAMtB,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAMhE,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAqB5B;AAED,eAAO,MAAM,SAAS,EAAE,aAkBvB,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { buildServer, PresetError, renderConditionsFor, resolvePreset, } from "@parlor/host";
2
+ import { buildRender, buildWeb, startWebDev } from "@parlor/shell-web";
3
+ import { CliError } from "./errors.js";
4
+ import { serverlessWarning } from "./serverless-warning.js";
5
+ import { onShutdown } from "./shutdown.js";
6
+ function isPortInUse(error) {
7
+ return error instanceof Error && /already in use/.test(error.message);
8
+ }
9
+ export async function buildWebAndServer(loaded, options) {
10
+ const outDir = await buildWeb(loaded);
11
+ console.log(`web build written to ${outDir}`);
12
+ try {
13
+ const { preset, reason } = resolvePreset(options.preset, loaded.config.deploy.preset);
14
+ const render = loaded.config.web.ssr
15
+ ? await buildRender(loaded, { conditions: renderConditionsFor(preset) })
16
+ : undefined;
17
+ if (render)
18
+ console.log(`server render build written to ${render}`);
19
+ console.log(`building server for ${preset} (${reason})`);
20
+ const result = await buildServer(loaded, { preset, render });
21
+ console.log(`server build written to ${result.outputDir} (sync: ${result.syncTransport})`);
22
+ const warning = serverlessWarning(result.syncTransport);
23
+ if (warning)
24
+ console.warn(warning);
25
+ return result;
26
+ }
27
+ catch (error) {
28
+ if (error instanceof PresetError)
29
+ throw new CliError(error.message, { cause: error });
30
+ throw error;
31
+ }
32
+ }
33
+ export const webTarget = {
34
+ async dev(loaded, options) {
35
+ try {
36
+ const server = await startWebDev(loaded, { appOrigin: options.appOrigin });
37
+ onShutdown(() => server.close());
38
+ }
39
+ catch (error) {
40
+ if (isPortInUse(error)) {
41
+ throw new CliError(`Port ${loaded.config.web.port} was taken while parlor dev was starting. Run it again.`, { cause: error });
42
+ }
43
+ throw error;
44
+ }
45
+ },
46
+ async build(loaded, options) {
47
+ await buildWebAndServer(loaded, options);
48
+ },
49
+ };
50
+ //# sourceMappingURL=web-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-target.js","sourceRoot":"","sources":["../src/web-target.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LoadedConfig } from \"@parlor/config\";\nimport {\n buildServer,\n PresetError,\n renderConditionsFor,\n resolvePreset,\n type ServerBuildResult,\n} from \"@parlor/host\";\nimport { buildRender, buildWeb, startWebDev } from \"@parlor/shell-web\";\n\nimport { CliError } from \"./errors.js\";\nimport { serverlessWarning } from \"./serverless-warning.js\";\nimport { onShutdown } from \"./shutdown.js\";\nimport type { BuildOptions, TargetHandler } from \"./targets.js\";\n\nfunction isPortInUse(error: unknown): boolean {\n return error instanceof Error && /already in use/.test(error.message);\n}\n\nexport async function buildWebAndServer(\n loaded: LoadedConfig,\n options: BuildOptions,\n): Promise<ServerBuildResult> {\n const outDir = await buildWeb(loaded);\n console.log(`web build written to ${outDir}`);\n try {\n const { preset, reason } = resolvePreset(options.preset, loaded.config.deploy.preset);\n const render = loaded.config.web.ssr\n ? await buildRender(loaded, { conditions: renderConditionsFor(preset) })\n : undefined;\n if (render) console.log(`server render build written to ${render}`);\n console.log(`building server for ${preset} (${reason})`);\n const result = await buildServer(loaded, { preset, render });\n console.log(\n `server build written to ${result.outputDir} (sync: ${result.syncTransport})`,\n );\n const warning = serverlessWarning(result.syncTransport);\n if (warning) console.warn(warning);\n return result;\n } catch (error) {\n if (error instanceof PresetError) throw new CliError(error.message, { cause: error });\n throw error;\n }\n}\n\nexport const webTarget: TargetHandler = {\n async dev(loaded, options) {\n try {\n const server = await startWebDev(loaded, { appOrigin: options.appOrigin });\n onShutdown(() => server.close());\n } catch (error) {\n if (isPortInUse(error)) {\n throw new CliError(\n `Port ${loaded.config.web.port} was taken while parlor dev was starting. Run it again.`,\n { cause: error },\n );\n }\n throw error;\n }\n },\n async build(loaded, options) {\n await buildWebAndServer(loaded, options);\n },\n};\n"]}