@cedarjs/cli 5.0.0-canary.13899 → 5.0.0-canary.13902

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 (2) hide show
  1. package/dist/commands/serve.js +215 -20
  2. package/package.json +13 -13
@@ -1,5 +1,6 @@
1
1
  import { fork } from "node:child_process";
2
2
  import fs from "node:fs";
3
+ import net from "node:net";
3
4
  import path from "node:path";
4
5
  import { terminalLink } from "termi-link";
5
6
  import * as apiServerCLIConfig from "@cedarjs/api-server/apiCliConfig";
@@ -18,7 +19,14 @@ const builder = async (yargs) => {
18
19
  yargs.command({
19
20
  command: "$0",
20
21
  description: bothServerCLIConfig.description,
21
- builder: bothServerCLIConfig.builder,
22
+ builder: (yargs2) => {
23
+ bothServerCLIConfig.builder(yargs2);
24
+ return yargs2.option("ud", {
25
+ description: "Use the Universal Deploy server (srvx) for the API side. The web side is served by the existing static file server. Pass --ud to opt in; the default is Fastify for both sides.",
26
+ type: "boolean",
27
+ default: false
28
+ });
29
+ },
22
30
  handler: async (argv) => {
23
31
  recordTelemetryAttributes({
24
32
  command: "serve",
@@ -26,6 +34,110 @@ const builder = async (yargs) => {
26
34
  host: argv.host,
27
35
  socket: argv.socket
28
36
  });
37
+ if (argv.ud) {
38
+ if (argv.port) {
39
+ console.error(
40
+ c.error(
41
+ "\n The --port flag is not supported with --ud. Use --web-port and --api-port instead.\n"
42
+ )
43
+ );
44
+ process.exit(1);
45
+ }
46
+ const udEntryPath = path.join(getPaths().api.dist, "ud", "index.js");
47
+ if (!fs.existsSync(udEntryPath)) {
48
+ console.error(
49
+ c.error(
50
+ `
51
+ Universal Deploy server entry not found at ${udEntryPath}.
52
+ Please run \`yarn cedar build --ud\` before serving.
53
+ `
54
+ )
55
+ );
56
+ process.exit(1);
57
+ }
58
+ const webDistIndexHtml = path.join(getPaths().web.dist, "index.html");
59
+ if (!fs.existsSync(webDistIndexHtml)) {
60
+ console.error(
61
+ c.error(
62
+ "\n Web build artifacts not found.\n Please run `yarn cedar build` before serving.\n"
63
+ )
64
+ );
65
+ process.exit(1);
66
+ }
67
+ if (serverFileExists()) {
68
+ console.warn(
69
+ c.warning(
70
+ "\n Note: api/src/server.ts was detected. This file is a Fastify concept and will be ignored when using --ud. You are testing the experimental UD support, so the behavior will not match your production Fastify setup.\n"
71
+ )
72
+ );
73
+ }
74
+ const { getAPIHost, getAPIPort, getWebHost, getWebPort } = await import("@cedarjs/api-server/cliHelpers");
75
+ const apiPort = argv.apiPort ?? getAPIPort();
76
+ const apiHost = argv.apiHost ?? getAPIHost();
77
+ const webPort = argv.webPort ?? getWebPort();
78
+ const webHost = argv.webHost ?? getWebHost();
79
+ const apiRootPath = argv.apiRootPath ?? "/";
80
+ const apiProxyTarget = [
81
+ "http://",
82
+ apiHost.includes(":") ? `[${apiHost}]` : apiHost,
83
+ ":",
84
+ apiPort,
85
+ apiRootPath
86
+ ].join("");
87
+ const { redwoodFastifyWeb } = await import("@cedarjs/fastify-web");
88
+ const { createFastifyInstance } = await import("@cedarjs/api-server/fastify");
89
+ const webFastify = await createFastifyInstance();
90
+ webFastify.register(redwoodFastifyWeb, {
91
+ redwood: {
92
+ apiProxyTarget
93
+ }
94
+ });
95
+ await webFastify.listen({
96
+ port: webPort,
97
+ host: webHost
98
+ });
99
+ const child = fork(
100
+ udEntryPath,
101
+ ["--port", String(apiPort), "--host", apiHost],
102
+ {
103
+ execArgv: process.execArgv,
104
+ env: {
105
+ ...process.env,
106
+ NODE_ENV: process.env.NODE_ENV ?? "production",
107
+ PORT: String(apiPort),
108
+ HOST: apiHost
109
+ }
110
+ }
111
+ );
112
+ child.on("error", (err) => {
113
+ console.error(
114
+ c.error(`
115
+ Failed to start UD API server: ${err.message}
116
+ `)
117
+ );
118
+ process.exit(1);
119
+ });
120
+ child.on("exit", (code) => {
121
+ if (code !== 0) {
122
+ console.error(
123
+ c.error(`
124
+ UD API server exited with code ${code}
125
+ `)
126
+ );
127
+ process.exit(1);
128
+ }
129
+ });
130
+ console.log(`Web server listening at http://${webHost}:${webPort}`);
131
+ process.stdout.write(
132
+ `API server starting at http://${apiHost}:${apiPort}...`
133
+ );
134
+ await waitForPort(apiHost, apiPort);
135
+ process.stdout.write(
136
+ `\rAPI server listening at http://${apiHost}:${apiPort}
137
+ `
138
+ );
139
+ return;
140
+ }
29
141
  if (serverFileExists()) {
30
142
  const serveBothHandlers = await import("./serveBothHandler.js");
31
143
  await serveBothHandlers.bothServerFileHandler(argv);
@@ -71,7 +183,7 @@ const builder = async (yargs) => {
71
183
  c.error(
72
184
  `
73
185
  Universal Deploy server entry not found at ${udEntryPath}.
74
- Please run \`yarn cedar build api\` before serving.
186
+ Please run \`yarn cedar build --ud\` before serving.
75
187
  `
76
188
  )
77
189
  );
@@ -84,17 +196,34 @@ const builder = async (yargs) => {
84
196
  if (argv.host) {
85
197
  udArgs.push("--host", argv.host);
86
198
  }
199
+ const child = fork(udEntryPath, udArgs, {
200
+ execArgv: process.execArgv,
201
+ env: {
202
+ ...process.env,
203
+ NODE_ENV: process.env.NODE_ENV ?? "production",
204
+ PORT: argv.port ? String(argv.port) : process.env.PORT,
205
+ HOST: argv.host ?? process.env.HOST
206
+ }
207
+ });
208
+ child.on("error", (err) => {
209
+ console.error(
210
+ c.error(`
211
+ Failed to start UD server: ${err.message}
212
+ `)
213
+ );
214
+ process.exit(1);
215
+ });
216
+ const apiPort = argv.port ?? parseInt(process.env.PORT ?? "8911", 10);
217
+ const apiHost = argv.host ?? process.env.HOST ?? "localhost";
218
+ process.stdout.write(
219
+ `API server starting at http://${apiHost}:${apiPort}...`
220
+ );
221
+ await waitForPort(apiHost, apiPort);
222
+ process.stdout.write(
223
+ `\rAPI server listening at http://${apiHost}:${apiPort}
224
+ `
225
+ );
87
226
  await new Promise((resolve, reject) => {
88
- const child = fork(udEntryPath, udArgs, {
89
- execArgv: process.execArgv,
90
- env: {
91
- ...process.env,
92
- NODE_ENV: process.env.NODE_ENV ?? "production",
93
- PORT: argv.port ? String(argv.port) : process.env.PORT,
94
- HOST: argv.host ?? process.env.HOST
95
- }
96
- });
97
- child.on("error", reject);
98
227
  child.on("exit", (code) => {
99
228
  if (code !== 0) {
100
229
  reject(new Error(`UD server exited with code ${code}`));
@@ -166,6 +295,20 @@ const builder = async (yargs) => {
166
295
  );
167
296
  process.exit(1);
168
297
  }
298
+ if (argv.ud) {
299
+ const udEntryPath = path.join(getPaths().api.dist, "ud", "index.js");
300
+ if (!fs.existsSync(udEntryPath)) {
301
+ console.error(
302
+ c.error(
303
+ `
304
+ Universal Deploy server entry not found at ${udEntryPath}.
305
+ Please run \`yarn cedar build --ud\` before serving.
306
+ `
307
+ )
308
+ );
309
+ process.exit(1);
310
+ }
311
+ }
169
312
  }
170
313
  if (positionalArgs.length === 1) {
171
314
  if (!apiSideExists && !rscEnabled) {
@@ -176,14 +319,38 @@ const builder = async (yargs) => {
176
319
  );
177
320
  process.exit(1);
178
321
  }
179
- const apiExistsButIsNotBuilt = apiSideExists && !fs.existsSync(getPaths().api.dist);
180
- if (apiExistsButIsNotBuilt || !webSideIsBuilt(streamingEnabled || rscEnabled)) {
181
- console.error(
182
- c.error(
183
- "\nPlease run `yarn cedar build` before trying to serve your Cedar app.\n"
184
- )
185
- );
186
- process.exit(1);
322
+ if (argv.ud) {
323
+ const udEntryPath = path.join(getPaths().api.dist, "ud", "index.js");
324
+ if (!fs.existsSync(udEntryPath)) {
325
+ console.error(
326
+ c.error(
327
+ `
328
+ Universal Deploy server entry not found at ${udEntryPath}.
329
+ Please run \`yarn cedar build --ud\` before serving.
330
+ `
331
+ )
332
+ );
333
+ process.exit(1);
334
+ }
335
+ const webDistIndexHtml = path.join(getPaths().web.dist, "index.html");
336
+ if (!fs.existsSync(webDistIndexHtml)) {
337
+ console.error(
338
+ c.error(
339
+ "\n Web build artifacts not found.\n Please run `yarn cedar build` before serving.\n"
340
+ )
341
+ );
342
+ process.exit(1);
343
+ }
344
+ } else {
345
+ const apiExistsButIsNotBuilt = apiSideExists && !fs.existsSync(getPaths().api.dist);
346
+ if (apiExistsButIsNotBuilt || !webSideIsBuilt(streamingEnabled || rscEnabled)) {
347
+ console.error(
348
+ c.error(
349
+ "\nPlease run `yarn cedar build` before trying to serve your Cedar app.\n"
350
+ )
351
+ );
352
+ process.exit(1);
353
+ }
187
354
  }
188
355
  }
189
356
  if (!process.env.NODE_ENV) {
@@ -205,6 +372,34 @@ function webSideIsBuilt(isStreamingOrRSC) {
205
372
  return fs.existsSync(path.join(getPaths().web.dist, "index.html"));
206
373
  }
207
374
  }
375
+ function waitForPort(host, port) {
376
+ const maxAttempts = 50;
377
+ const intervalMs = 200;
378
+ return new Promise((resolve, reject) => {
379
+ let attempts = 0;
380
+ const tryConnect = () => {
381
+ attempts++;
382
+ const socket = net.createConnection({ host, port });
383
+ socket.on("connect", () => {
384
+ socket.destroy();
385
+ resolve();
386
+ });
387
+ socket.on("error", () => {
388
+ socket.destroy();
389
+ if (attempts >= maxAttempts) {
390
+ reject(
391
+ new Error(
392
+ `API server did not become ready on port ${port} after ${maxAttempts * intervalMs}ms`
393
+ )
394
+ );
395
+ } else {
396
+ setTimeout(tryConnect, intervalMs);
397
+ }
398
+ });
399
+ };
400
+ tryConnect();
401
+ });
402
+ }
208
403
  export {
209
404
  builder,
210
405
  command,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cedarjs/cli",
3
- "version": "5.0.0-canary.13899+eb6c2b0f35",
3
+ "version": "5.0.0-canary.13902+61d1e11811",
4
4
  "description": "The CedarJS Command Line",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,17 +33,17 @@
33
33
  "dependencies": {
34
34
  "@babel/parser": "7.29.2",
35
35
  "@babel/preset-typescript": "7.28.5",
36
- "@cedarjs/api-server": "5.0.0-canary.13899",
37
- "@cedarjs/cli-helpers": "5.0.0-canary.13899",
38
- "@cedarjs/fastify-web": "5.0.0-canary.13899",
39
- "@cedarjs/internal": "5.0.0-canary.13899",
40
- "@cedarjs/prerender": "5.0.0-canary.13899",
41
- "@cedarjs/project-config": "5.0.0-canary.13899",
42
- "@cedarjs/structure": "5.0.0-canary.13899",
43
- "@cedarjs/telemetry": "5.0.0-canary.13899",
44
- "@cedarjs/utils": "5.0.0-canary.13899",
45
- "@cedarjs/vite": "5.0.0-canary.13899",
46
- "@cedarjs/web-server": "5.0.0-canary.13899",
36
+ "@cedarjs/api-server": "5.0.0-canary.13902",
37
+ "@cedarjs/cli-helpers": "5.0.0-canary.13902",
38
+ "@cedarjs/fastify-web": "5.0.0-canary.13902",
39
+ "@cedarjs/internal": "5.0.0-canary.13902",
40
+ "@cedarjs/prerender": "5.0.0-canary.13902",
41
+ "@cedarjs/project-config": "5.0.0-canary.13902",
42
+ "@cedarjs/structure": "5.0.0-canary.13902",
43
+ "@cedarjs/telemetry": "5.0.0-canary.13902",
44
+ "@cedarjs/utils": "5.0.0-canary.13902",
45
+ "@cedarjs/vite": "5.0.0-canary.13902",
46
+ "@cedarjs/web-server": "5.0.0-canary.13902",
47
47
  "@listr2/prompt-adapter-enquirer": "4.2.1",
48
48
  "@opentelemetry/api": "1.9.0",
49
49
  "@opentelemetry/core": "1.30.1",
@@ -108,5 +108,5 @@
108
108
  "publishConfig": {
109
109
  "access": "public"
110
110
  },
111
- "gitHead": "eb6c2b0f35122011e47445b41d8b10d640ebc03e"
111
+ "gitHead": "61d1e11811bdc624db5b3aab49346430cfc963bc"
112
112
  }