@cedarjs/cli 5.0.0-canary.0 → 5.0.0-canary.13874
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.
- package/dist/cfw.js +1 -1
- package/dist/commands/build/buildHandler.js +21 -11
- package/dist/commands/build.js +4 -0
- package/dist/commands/dev/devHandler.js +112 -69
- package/dist/commands/dev.js +4 -0
- package/dist/commands/experimental/live-queries/templates/migration.sql.template +3 -1
- package/dist/commands/experimental/templates/opentelemetry.ts.template +2 -1
- package/dist/commands/jobsHandler.js +2 -2
- package/dist/commands/serve.js +55 -2
- package/dist/commands/serveBothHandler.js +3 -3
- package/dist/commands/serveWebHandler.js +1 -1
- package/dist/commands/setup/deploy/helpers/index.js +5 -1
- package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +14 -10
- package/dist/commands/setup/deploy/providers/renderHandler.js +1 -2
- package/dist/commands/setup/deploy/templates/flightcontrol.js +8 -8
- package/dist/commands/setup/deploy/templates/render.js +3 -1
- package/dist/commands/setup/docker/templates/Dockerfile +1 -1
- package/dist/index.js +0 -1
- package/package.json +16 -16
package/dist/cfw.js
CHANGED
|
@@ -6,7 +6,7 @@ import execa from "execa";
|
|
|
6
6
|
import { terminalLink } from "termi-link";
|
|
7
7
|
import { getConfigPath } from "@cedarjs/project-config";
|
|
8
8
|
const config = new Configstore("@cedarjs/cli");
|
|
9
|
-
const CFW_PATH = process.env.CFW_PATH ||
|
|
9
|
+
const CFW_PATH = process.env.CFW_PATH || config.get("CFW_PATH");
|
|
10
10
|
if (!CFW_PATH) {
|
|
11
11
|
console.error("Error: You must specify the path to Cedar Framework");
|
|
12
12
|
console.error("Usage: `CFW_PATH=~/gh/cedarjs/cedar yarn cfw <command>");
|
|
@@ -10,13 +10,17 @@ import {
|
|
|
10
10
|
formatRunWorkspaceScriptCommand
|
|
11
11
|
} from "@cedarjs/cli-helpers/packageManager/display";
|
|
12
12
|
import { runBin } from "@cedarjs/cli-helpers/packageManager/exec";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
buildApiWithVite,
|
|
15
|
+
cleanApiBuild
|
|
16
|
+
} from "@cedarjs/internal/dist/build/api";
|
|
14
17
|
import { generate } from "@cedarjs/internal/dist/generate/generate";
|
|
15
18
|
import { generateGqlormArtifacts } from "@cedarjs/internal/dist/generate/gqlormSchema";
|
|
16
19
|
import { loadAndValidateSdls } from "@cedarjs/internal/dist/validateSchema";
|
|
17
20
|
import { detectPrerenderRoutes } from "@cedarjs/prerender/detection";
|
|
18
21
|
import {} from "@cedarjs/project-config";
|
|
19
22
|
import { timedTelemetry } from "@cedarjs/telemetry";
|
|
23
|
+
import { buildUDApiServer } from "@cedarjs/vite/buildUDApiServer";
|
|
20
24
|
import { generatePrismaCommand } from "../../lib/generatePrismaClient.js";
|
|
21
25
|
import { getPaths, getConfig } from "../../lib/index.js";
|
|
22
26
|
import { buildPackagesTask } from "./buildPackagesTask.js";
|
|
@@ -73,7 +77,8 @@ const handler = async ({
|
|
|
73
77
|
workspace = ["api", "web", "packages/*"],
|
|
74
78
|
verbose = false,
|
|
75
79
|
prisma = true,
|
|
76
|
-
prerender = true
|
|
80
|
+
prerender = true,
|
|
81
|
+
ud = false
|
|
77
82
|
}) => {
|
|
78
83
|
recordTelemetryAttributes({
|
|
79
84
|
command: "build",
|
|
@@ -158,17 +163,22 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
|
|
|
158
163
|
title: "Verifying graphql schema...",
|
|
159
164
|
task: loadAndValidateSdls
|
|
160
165
|
},
|
|
166
|
+
// The API build has two sequential steps:
|
|
167
|
+
// 1. esbuild compiles api/src/** → api/dist/ (functions, services, etc.)
|
|
168
|
+
// 2. Vite wraps api/dist/functions/ into a self-contained UD Node server
|
|
169
|
+
// entry at api/dist/ud/index.js for `cedar serve api`
|
|
170
|
+
// Step 2 depends on step 1 having completed.
|
|
161
171
|
workspace.includes("api") && {
|
|
162
172
|
title: "Building API...",
|
|
163
173
|
task: async () => {
|
|
164
174
|
await cleanApiBuild();
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
175
|
+
await buildApiWithVite();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
ud && workspace.includes("api") && {
|
|
179
|
+
title: "Bundling API server entry (Universal Deploy)...",
|
|
180
|
+
task: async () => {
|
|
181
|
+
await buildUDApiServer({ verbose });
|
|
172
182
|
}
|
|
173
183
|
},
|
|
174
184
|
workspace.includes("web") && {
|
|
@@ -177,14 +187,14 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
|
|
|
177
187
|
process.env.VITE_CJS_IGNORE_WARNING = "true";
|
|
178
188
|
const createdRequire = createRequire(import.meta.url);
|
|
179
189
|
const buildBinPath = createdRequire.resolve(
|
|
180
|
-
"@cedarjs/vite/bins/
|
|
190
|
+
"@cedarjs/vite/bins/cedar-vite-build.mjs"
|
|
181
191
|
);
|
|
182
192
|
await execa(
|
|
183
193
|
`node ${buildBinPath} --webDir="${cedarPaths.web.base}" --verbose=${verbose}`,
|
|
184
194
|
{
|
|
185
195
|
stdio: verbose ? "inherit" : "pipe",
|
|
186
196
|
shell: true,
|
|
187
|
-
// `cwd` is needed for yarn to find the
|
|
197
|
+
// `cwd` is needed for yarn to find the cedar-vite-build binary
|
|
188
198
|
// It won't change process.cwd for anything else here, in this
|
|
189
199
|
// process
|
|
190
200
|
cwd: cedarPaths.web.base
|
package/dist/commands/build.js
CHANGED
|
@@ -25,6 +25,10 @@ const builder = (yargs) => {
|
|
|
25
25
|
alias: "db",
|
|
26
26
|
default: true,
|
|
27
27
|
description: "Generate the Prisma client"
|
|
28
|
+
}).option("ud", {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
default: false,
|
|
31
|
+
description: "Build the Universal Deploy server entry (api/dist/ud/index.js)."
|
|
28
32
|
}).middleware(() => {
|
|
29
33
|
const check = checkNodeVersion();
|
|
30
34
|
if (check.ok) {
|
|
@@ -18,7 +18,8 @@ const handler = async ({
|
|
|
18
18
|
workspace = ["api", "web", "packages/*"],
|
|
19
19
|
forward = "",
|
|
20
20
|
generate = true,
|
|
21
|
-
apiDebugPort
|
|
21
|
+
apiDebugPort,
|
|
22
|
+
ud = false
|
|
22
23
|
}) => {
|
|
23
24
|
recordTelemetryAttributes({
|
|
24
25
|
command: "dev",
|
|
@@ -28,22 +29,26 @@ const handler = async ({
|
|
|
28
29
|
const cedarPaths = getPaths();
|
|
29
30
|
const serverFile = serverFileExists();
|
|
30
31
|
const apiPreferredPort = parseInt(String(getConfig().api.port));
|
|
32
|
+
let apiAvailablePort;
|
|
33
|
+
let apiPortChangeNeeded = false;
|
|
34
|
+
if (workspace.includes("api")) {
|
|
35
|
+
if (!serverFile) {
|
|
36
|
+
apiAvailablePort = await getFreePort(apiPreferredPort);
|
|
37
|
+
if (apiAvailablePort === -1) {
|
|
38
|
+
exitWithError(void 0, {
|
|
39
|
+
message: `Could not determine a free port for the api server`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
apiPortChangeNeeded = apiAvailablePort !== apiPreferredPort;
|
|
43
|
+
} else {
|
|
44
|
+
apiAvailablePort = apiPreferredPort;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
let webPreferredPort = parseInt(
|
|
32
48
|
String(getConfig().web.port)
|
|
33
49
|
);
|
|
34
|
-
let apiAvailablePort = apiPreferredPort;
|
|
35
|
-
let apiPortChangeNeeded = false;
|
|
36
50
|
let webAvailablePort = webPreferredPort;
|
|
37
51
|
let webPortChangeNeeded = false;
|
|
38
|
-
if (workspace.includes("api") && !serverFile) {
|
|
39
|
-
apiAvailablePort = await getFreePort(apiPreferredPort);
|
|
40
|
-
if (apiAvailablePort === -1) {
|
|
41
|
-
exitWithError(void 0, {
|
|
42
|
-
message: `Could not determine a free port for the api server`
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
apiPortChangeNeeded = apiAvailablePort !== apiPreferredPort;
|
|
46
|
-
}
|
|
47
52
|
if (workspace.includes("web")) {
|
|
48
53
|
const forwardedPortMatches = [
|
|
49
54
|
...forward.matchAll(/\-\-port(\=|\s)(?<port>[^\s]*)/g)
|
|
@@ -52,10 +57,10 @@ const handler = async ({
|
|
|
52
57
|
const port = forwardedPortMatches.pop()?.groups?.port;
|
|
53
58
|
webPreferredPort = port ? parseInt(port, 10) : void 0;
|
|
54
59
|
}
|
|
55
|
-
webAvailablePort = await getFreePort(
|
|
56
|
-
|
|
57
|
-
apiAvailablePort
|
|
58
|
-
|
|
60
|
+
webAvailablePort = await getFreePort(
|
|
61
|
+
webPreferredPort,
|
|
62
|
+
apiAvailablePort !== void 0 ? [apiPreferredPort, apiAvailablePort] : [apiPreferredPort]
|
|
63
|
+
);
|
|
59
64
|
if (webAvailablePort === -1) {
|
|
60
65
|
exitWithError(void 0, {
|
|
61
66
|
message: `Could not determine a free port for the web server`
|
|
@@ -63,21 +68,17 @@ const handler = async ({
|
|
|
63
68
|
}
|
|
64
69
|
webPortChangeNeeded = webAvailablePort !== webPreferredPort;
|
|
65
70
|
}
|
|
66
|
-
if (
|
|
71
|
+
if (webPortChangeNeeded) {
|
|
67
72
|
const message = [
|
|
68
|
-
"The currently configured
|
|
69
|
-
"unavailable. Suggested
|
|
70
|
-
"cedar.toml (or redwood.toml)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`,
|
|
75
|
-
webPortChangeNeeded && ` - Web to use port ${webAvailablePort} instead`,
|
|
76
|
-
webPortChangeNeeded && "of your currently configured",
|
|
77
|
-
webPortChangeNeeded && `${webPreferredPort}
|
|
73
|
+
"The currently configured port for the development server is",
|
|
74
|
+
"unavailable. Suggested change to your port, which can be changed in",
|
|
75
|
+
"cedar.toml (or redwood.toml):\n",
|
|
76
|
+
` - Web to use port ${webAvailablePort} instead`,
|
|
77
|
+
"of your currently configured",
|
|
78
|
+
`${webPreferredPort}
|
|
78
79
|
`,
|
|
79
|
-
"\nCannot run the development server until your configured
|
|
80
|
-
"changed or
|
|
80
|
+
"\nCannot run the development server until your configured port is",
|
|
81
|
+
"changed or becomes available."
|
|
81
82
|
].filter(Boolean).join(" ");
|
|
82
83
|
exitWithError(void 0, { message });
|
|
83
84
|
}
|
|
@@ -89,19 +90,8 @@ const handler = async ({
|
|
|
89
90
|
errorTelemetry(process.argv, `Error generating prisma client: ${message}`);
|
|
90
91
|
console.error(c.error(message));
|
|
91
92
|
}
|
|
92
|
-
if (!serverFile) {
|
|
93
|
-
try {
|
|
94
|
-
await shutdownPort(apiAvailablePort);
|
|
95
|
-
} catch (e) {
|
|
96
|
-
const message = getErrorMessage(e);
|
|
97
|
-
errorTelemetry(process.argv, `Error shutting down "api": ${message}`);
|
|
98
|
-
console.error(
|
|
99
|
-
`Error whilst shutting down "api" port: ${c.error(message)}`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
93
|
}
|
|
104
|
-
if (workspace.includes("web")) {
|
|
94
|
+
if (workspace.includes("web") && webAvailablePort !== void 0) {
|
|
105
95
|
try {
|
|
106
96
|
await shutdownPort(webAvailablePort);
|
|
107
97
|
} catch (e) {
|
|
@@ -120,48 +110,101 @@ const handler = async ({
|
|
|
120
110
|
console.error(c.error(`Error generating gqlorm schema: ${message}`));
|
|
121
111
|
}
|
|
122
112
|
}
|
|
123
|
-
const cedarConfigPath = getConfigPath();
|
|
124
113
|
const streamingSsrEnabled = getConfig().experimental?.streamingSsr?.enabled;
|
|
125
114
|
process.env.VITE_CJS_IGNORE_WARNING = "true";
|
|
126
|
-
let webCommand = `yarn cross-env NODE_ENV=development rw-vite-dev ${forward}`;
|
|
127
|
-
if (streamingSsrEnabled) {
|
|
128
|
-
webCommand = `yarn cross-env NODE_ENV=development rw-dev-fe ${forward}`;
|
|
129
|
-
}
|
|
130
115
|
const rootPackageJsonPath = path.join(cedarPaths.base, "package.json");
|
|
131
116
|
const rootPackageJson = JSON.parse(
|
|
132
117
|
fs.readFileSync(rootPackageJsonPath, "utf8")
|
|
133
118
|
);
|
|
134
|
-
const
|
|
135
|
-
|
|
119
|
+
const buildUnifiedDevCommand = () => {
|
|
120
|
+
if (!ud) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
if (streamingSsrEnabled) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
if (!workspace.includes("api") || !workspace.includes("web")) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (serverFile) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
if (!fs.existsSync(cedarPaths.api.src) || !fs.existsSync(cedarPaths.web.src)) {
|
|
133
|
+
console.log("api.src or web.src does not exist");
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return [
|
|
137
|
+
`yarn cross-env NODE_ENV=development cedar-unified-dev`,
|
|
138
|
+
` --port ${webAvailablePort}`,
|
|
139
|
+
` --apiPort ${apiAvailablePort}`,
|
|
140
|
+
getApiDebugFlag(apiDebugPort, apiAvailablePort),
|
|
141
|
+
forward
|
|
142
|
+
].join(" ").replace(/\s+/g, " ").trim();
|
|
143
|
+
};
|
|
144
|
+
const unifiedDevCommand = buildUnifiedDevCommand();
|
|
145
|
+
if (!unifiedDevCommand && apiPortChangeNeeded) {
|
|
146
|
+
const message = [
|
|
147
|
+
"The currently configured port for the development server is",
|
|
148
|
+
"unavailable. Suggested change to your port, which can be changed in",
|
|
149
|
+
"cedar.toml (or redwood.toml):\n",
|
|
150
|
+
` - API to use port ${apiAvailablePort} instead`,
|
|
151
|
+
"of your currently configured",
|
|
152
|
+
`${apiPreferredPort}
|
|
153
|
+
`,
|
|
154
|
+
"\nCannot run the development server until your configured port is",
|
|
155
|
+
"changed or becomes available."
|
|
156
|
+
].filter(Boolean).join(" ");
|
|
157
|
+
exitWithError(void 0, { message });
|
|
158
|
+
}
|
|
136
159
|
const jobs = [];
|
|
137
|
-
if (
|
|
160
|
+
if (unifiedDevCommand) {
|
|
138
161
|
jobs.push({
|
|
139
|
-
name: "
|
|
140
|
-
command:
|
|
141
|
-
"yarn nodemon",
|
|
142
|
-
" --quiet",
|
|
143
|
-
` --watch "${cedarConfigPath}"`,
|
|
144
|
-
` --exec "yarn ${serverWatchCommand}`,
|
|
145
|
-
` --port ${apiAvailablePort}`,
|
|
146
|
-
` ${getApiDebugFlag(apiDebugPort, apiAvailablePort)}`,
|
|
147
|
-
' | rw-log-formatter"'
|
|
148
|
-
].join(" ").replace(/\s+/g, " "),
|
|
162
|
+
name: "dev",
|
|
163
|
+
command: unifiedDevCommand,
|
|
149
164
|
env: {
|
|
150
165
|
NODE_ENV: "development",
|
|
151
166
|
NODE_OPTIONS: getDevNodeOptions()
|
|
152
167
|
},
|
|
153
168
|
prefixColor: "cyan",
|
|
154
|
-
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
if (workspace.includes("web")) {
|
|
158
|
-
jobs.push({
|
|
159
|
-
name: "web",
|
|
160
|
-
command: webCommand,
|
|
161
|
-
prefixColor: "blue",
|
|
162
|
-
cwd: cedarPaths.web.base,
|
|
163
|
-
runWhen: () => fs.existsSync(cedarPaths.web.src)
|
|
169
|
+
cwd: cedarPaths.web.base
|
|
164
170
|
});
|
|
171
|
+
} else {
|
|
172
|
+
if (workspace.includes("api")) {
|
|
173
|
+
const isEsm = rootPackageJson.type === "module";
|
|
174
|
+
const serverWatchCommand = isEsm ? `cedarjs-api-server-watch` : `cedar-api-server-watch`;
|
|
175
|
+
const cedarConfigPath = getConfigPath();
|
|
176
|
+
jobs.push({
|
|
177
|
+
name: "api",
|
|
178
|
+
command: [
|
|
179
|
+
"yarn nodemon",
|
|
180
|
+
" --quiet",
|
|
181
|
+
` --watch "${cedarConfigPath}"`,
|
|
182
|
+
` --exec "yarn ${serverWatchCommand}`,
|
|
183
|
+
` --port ${apiAvailablePort}`,
|
|
184
|
+
` ${getApiDebugFlag(apiDebugPort, apiAvailablePort)}`,
|
|
185
|
+
` | cedar-log-formatter"`
|
|
186
|
+
].join(" ").replace(/\s+/g, " "),
|
|
187
|
+
env: {
|
|
188
|
+
NODE_ENV: "development",
|
|
189
|
+
NODE_OPTIONS: getDevNodeOptions()
|
|
190
|
+
},
|
|
191
|
+
prefixColor: "cyan",
|
|
192
|
+
runWhen: () => fs.existsSync(cedarPaths.api.src)
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (workspace.includes("web")) {
|
|
196
|
+
let webCommand = `yarn cross-env NODE_ENV=development cedar-vite-dev ${forward}`;
|
|
197
|
+
if (streamingSsrEnabled) {
|
|
198
|
+
webCommand = `yarn cross-env NODE_ENV=development cedar-dev-fe ${forward}`;
|
|
199
|
+
}
|
|
200
|
+
jobs.push({
|
|
201
|
+
name: "web",
|
|
202
|
+
command: webCommand,
|
|
203
|
+
prefixColor: "blue",
|
|
204
|
+
cwd: cedarPaths.web.base,
|
|
205
|
+
runWhen: () => fs.existsSync(cedarPaths.web.src)
|
|
206
|
+
});
|
|
207
|
+
}
|
|
165
208
|
}
|
|
166
209
|
if (generate) {
|
|
167
210
|
jobs.push({
|
package/dist/commands/dev.js
CHANGED
|
@@ -24,6 +24,10 @@ const builder = (yargs) => {
|
|
|
24
24
|
}).option("apiDebugPort", {
|
|
25
25
|
type: "number",
|
|
26
26
|
description: "Port on which to expose API server debugger. If you supply the flag with no value it defaults to 1 prepended to the api port (e.g. api port 8913 -> debug port 18913)."
|
|
27
|
+
}).option("ud", {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
default: false,
|
|
30
|
+
description: "Use the unified Vite dev server that handles both web and API in a single process (experimental)."
|
|
27
31
|
}).middleware(() => {
|
|
28
32
|
const check = checkNodeVersion();
|
|
29
33
|
if (check.ok) {
|
|
@@ -56,11 +56,13 @@ DECLARE
|
|
|
56
56
|
cmd record;
|
|
57
57
|
schema_name text;
|
|
58
58
|
table_name text;
|
|
59
|
+
ident_parts text[];
|
|
59
60
|
BEGIN
|
|
60
61
|
FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
|
|
61
62
|
IF cmd.object_type = 'table' THEN
|
|
62
63
|
schema_name := cmd.schema_name;
|
|
63
|
-
|
|
64
|
+
ident_parts := parse_ident(cmd.object_identity);
|
|
65
|
+
table_name := ident_parts[array_length(ident_parts, 1)];
|
|
64
66
|
|
|
65
67
|
IF schema_name IS NULL THEN
|
|
66
68
|
CONTINUE;
|
|
@@ -24,11 +24,12 @@ const resource = Resource.default().merge(
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
const studioPort = getConfig().studio.basePort
|
|
27
|
+
const apiUrl = getConfig().web.apiUrl.replace(/\/$/, '')
|
|
27
28
|
const exporter = new OTLPTraceExporter({
|
|
28
29
|
// Update this URL to point to where your OTLP compatible collector is listening
|
|
29
30
|
// The redwood development studio (`yarn cedar exp studio`) can collect your
|
|
30
31
|
// telemetry at `http://127.0.0.1:<PORT>/v1/traces` (default PORT is 4318)
|
|
31
|
-
url: `http://127.0.0.1:${studioPort}
|
|
32
|
+
url: `http://127.0.0.1:${studioPort}${apiUrl}/otel-trace`,
|
|
32
33
|
concurrencyLimit: 64,
|
|
33
34
|
})
|
|
34
35
|
|
|
@@ -13,11 +13,11 @@ const handler = async ({
|
|
|
13
13
|
args.push(name.length > 1 ? `--${name}` : `-${name}`);
|
|
14
14
|
args.push(String(value));
|
|
15
15
|
}
|
|
16
|
-
let command = `yarn
|
|
16
|
+
let command = `yarn cedar-jobs ${args.join(" ")}`;
|
|
17
17
|
const originalLogLevel = process.env.LOG_LEVEL;
|
|
18
18
|
process.env.LOG_LEVEL = originalLogLevel || "warn";
|
|
19
19
|
if (process.env.NODE_ENV !== "production") {
|
|
20
|
-
command += " | yarn
|
|
20
|
+
command += " | yarn cedar-log-formatter";
|
|
21
21
|
process.env.LOG_LEVEL = originalLogLevel || "debug";
|
|
22
22
|
}
|
|
23
23
|
execa.commandSync(command, {
|
package/dist/commands/serve.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { fork } from "node:child_process";
|
|
1
2
|
import fs from "node:fs";
|
|
2
|
-
import path from "path";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { terminalLink } from "termi-link";
|
|
4
5
|
import * as apiServerCLIConfig from "@cedarjs/api-server/apiCliConfig";
|
|
5
6
|
import * as bothServerCLIConfig from "@cedarjs/api-server/bothCliConfig";
|
|
@@ -43,7 +44,18 @@ const builder = async (yargs) => {
|
|
|
43
44
|
}).command({
|
|
44
45
|
command: "api",
|
|
45
46
|
description: apiServerCLIConfig.description,
|
|
46
|
-
builder:
|
|
47
|
+
builder: (yargs2) => {
|
|
48
|
+
if (typeof apiServerCLIConfig.builder === "function") {
|
|
49
|
+
apiServerCLIConfig.builder(yargs2);
|
|
50
|
+
}
|
|
51
|
+
return yargs2.option("ud", {
|
|
52
|
+
// UD serving is opt-in. Pass --ud to use the new srvx server instead
|
|
53
|
+
// of the legacy Fastify server.
|
|
54
|
+
description: "Use the Universal Deploy server (srvx). Pass --ud to opt in; the default is Fastify.",
|
|
55
|
+
type: "boolean",
|
|
56
|
+
default: false
|
|
57
|
+
});
|
|
58
|
+
},
|
|
47
59
|
handler: async (argv) => {
|
|
48
60
|
recordTelemetryAttributes({
|
|
49
61
|
command: "serve",
|
|
@@ -52,6 +64,47 @@ const builder = async (yargs) => {
|
|
|
52
64
|
socket: argv.socket,
|
|
53
65
|
apiRootPath: argv.apiRootPath
|
|
54
66
|
});
|
|
67
|
+
if (argv.ud) {
|
|
68
|
+
const udEntryPath = path.join(getPaths().api.dist, "ud", "index.js");
|
|
69
|
+
if (!fs.existsSync(udEntryPath)) {
|
|
70
|
+
console.error(
|
|
71
|
+
c.error(
|
|
72
|
+
`
|
|
73
|
+
Universal Deploy server entry not found at ${udEntryPath}.
|
|
74
|
+
Please run \`yarn cedar build api\` before serving.
|
|
75
|
+
`
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
const udArgs = [];
|
|
81
|
+
if (argv.port) {
|
|
82
|
+
udArgs.push("--port", String(argv.port));
|
|
83
|
+
}
|
|
84
|
+
if (argv.host) {
|
|
85
|
+
udArgs.push("--host", argv.host);
|
|
86
|
+
}
|
|
87
|
+
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
|
+
child.on("exit", (code) => {
|
|
99
|
+
if (code !== 0) {
|
|
100
|
+
reject(new Error(`UD server exited with code ${code}`));
|
|
101
|
+
} else {
|
|
102
|
+
resolve();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
55
108
|
if (serverFileExists()) {
|
|
56
109
|
const { apiServerFileHandler } = await import("./serveApiHandler.js");
|
|
57
110
|
await apiServerFileHandler(argv);
|
|
@@ -18,7 +18,7 @@ const hasStringMessage = (error) => {
|
|
|
18
18
|
const bothServerFileHandler = async (argv) => {
|
|
19
19
|
if (getConfig().experimental?.rsc?.enabled || getConfig().experimental?.streamingSsr?.enabled) {
|
|
20
20
|
logSkippingFastifyWebServer();
|
|
21
|
-
await execa("yarn", ["
|
|
21
|
+
await execa("yarn", ["cedar-serve-fe"], {
|
|
22
22
|
cwd: getPaths().web.base,
|
|
23
23
|
stdio: "inherit"
|
|
24
24
|
});
|
|
@@ -44,7 +44,7 @@ const bothServerFileHandler = async (argv) => {
|
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
name: "web",
|
|
47
|
-
command: `yarn
|
|
47
|
+
command: `yarn cedar-web-server --port ${argv.webPort} --host ${argv.webHost} --api-proxy-target ${apiProxyTarget}`,
|
|
48
48
|
cwd: getPaths().base,
|
|
49
49
|
prefixColor: "blue"
|
|
50
50
|
}
|
|
@@ -75,7 +75,7 @@ const bothSsrRscServerHandler = async (argv, rscEnabled) => {
|
|
|
75
75
|
host: argv.apiHost,
|
|
76
76
|
port: argv.apiPort
|
|
77
77
|
});
|
|
78
|
-
const fePromise = execa("yarn", ["
|
|
78
|
+
const fePromise = execa("yarn", ["cedar-serve-fe"], {
|
|
79
79
|
cwd: getPaths().web.base,
|
|
80
80
|
stdio: "inherit",
|
|
81
81
|
env: rscEnabled ? {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import execa from "execa";
|
|
2
2
|
import { getPaths } from "@cedarjs/project-config";
|
|
3
3
|
const webSsrServerHandler = async (rscEnabled) => {
|
|
4
|
-
await execa("yarn", ["
|
|
4
|
+
await execa("yarn", ["cedar-serve-fe"], {
|
|
5
5
|
cwd: getPaths().web.base,
|
|
6
6
|
stdio: "inherit",
|
|
7
7
|
env: rscEnabled ? {
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import execa from "execa";
|
|
4
4
|
import { Listr } from "listr2";
|
|
5
|
-
import { getConfigPath } from "@cedarjs/project-config";
|
|
5
|
+
import { getConfigPath, getConfig } from "@cedarjs/project-config";
|
|
6
6
|
import { getPaths, writeFilesTask } from "../../../../lib/index.js";
|
|
7
7
|
const updateApiURLTask = (apiUrl) => {
|
|
8
8
|
const configTomlPath = getConfigPath();
|
|
@@ -25,6 +25,9 @@ const updateApiURLTask = (apiUrl) => {
|
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
|
+
function getUserApiUrl() {
|
|
29
|
+
return getConfig().web.apiUrl;
|
|
30
|
+
}
|
|
28
31
|
const preRequisiteCheckTask = (preRequisites) => {
|
|
29
32
|
return {
|
|
30
33
|
title: "Checking pre-requisites",
|
|
@@ -101,6 +104,7 @@ export {
|
|
|
101
104
|
addFilesTask,
|
|
102
105
|
addToDotEnvTask,
|
|
103
106
|
addToGitIgnoreTask,
|
|
107
|
+
getUserApiUrl,
|
|
104
108
|
preRequisiteCheckTask,
|
|
105
109
|
updateApiURLTask
|
|
106
110
|
};
|
|
@@ -7,7 +7,7 @@ import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
|
7
7
|
import { getPaths, getPrismaSchemas } from "@cedarjs/project-config";
|
|
8
8
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
9
9
|
import { writeFilesTask, printSetupNotes } from "../../../../lib/index.js";
|
|
10
|
-
import { updateApiURLTask } from "../helpers/index.js";
|
|
10
|
+
import { getUserApiUrl, updateApiURLTask } from "../helpers/index.js";
|
|
11
11
|
import {
|
|
12
12
|
flightcontrolConfig,
|
|
13
13
|
databaseEnvVariables,
|
|
@@ -47,7 +47,7 @@ const getFlightcontrolJson = async (database) => {
|
|
|
47
47
|
...flightcontrolConfig.environments[0],
|
|
48
48
|
services: [
|
|
49
49
|
...flightcontrolConfig.environments[0].services.map((service) => {
|
|
50
|
-
if (service.id === "
|
|
50
|
+
if (service.id === "cedar-api") {
|
|
51
51
|
return {
|
|
52
52
|
...service,
|
|
53
53
|
envVariables: {
|
|
@@ -96,7 +96,7 @@ const updateGraphQLFunction = () => {
|
|
|
96
96
|
Couldn't find graphql handler in api/src/functions/graphql.js.
|
|
97
97
|
You'll have to add the following cors config manually:
|
|
98
98
|
|
|
99
|
-
cors: { origin: process.env.
|
|
99
|
+
cors: { origin: process.env.CEDAR_WEB_URL, credentials: true}
|
|
100
100
|
`);
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
@@ -109,14 +109,14 @@ const updateGraphQLFunction = () => {
|
|
|
109
109
|
Couldn't find graphql handler in api/src/functions/graphql.js.
|
|
110
110
|
You'll have to add the following cors config manually:
|
|
111
111
|
|
|
112
|
-
cors: { origin: process.env.
|
|
112
|
+
cors: { origin: process.env.CEDAR_WEB_URL, credentials: true}
|
|
113
113
|
`);
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
graphqlContent.splice(
|
|
117
117
|
graphqlHanderIndex + 1,
|
|
118
118
|
0,
|
|
119
|
-
" cors: { origin: process.env.
|
|
119
|
+
" cors: { origin: process.env.CEDAR_WEB_URL, credentials: true },"
|
|
120
120
|
);
|
|
121
121
|
fs.writeFileSync(graphqlFunctionsPath, graphqlContent.join(EOL));
|
|
122
122
|
}
|
|
@@ -158,14 +158,14 @@ const updateDbAuth = () => {
|
|
|
158
158
|
Couldn't find DbAuthHandler in api/src/functions/auth.js.
|
|
159
159
|
You'll have to add the following cors config manually:
|
|
160
160
|
|
|
161
|
-
cors: { origin: process.env.
|
|
161
|
+
cors: { origin: process.env.CEDAR_WEB_URL, credentials: true}
|
|
162
162
|
`);
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
authContent.splice(
|
|
166
166
|
dbHandlerIndex + 1,
|
|
167
167
|
0,
|
|
168
|
-
" cors: { origin: process.env.
|
|
168
|
+
" cors: { origin: process.env.CEDAR_WEB_URL, credentials: true },"
|
|
169
169
|
);
|
|
170
170
|
fs.writeFileSync(authFnPath, authContent.join(EOL));
|
|
171
171
|
}
|
|
@@ -229,13 +229,17 @@ const addToDotEnvDefaultTask = () => {
|
|
|
229
229
|
|
|
230
230
|
You'll have to add the following env var manually:
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
CEDAR_API_URL=${getUserApiUrl()}
|
|
233
233
|
`;
|
|
234
234
|
}
|
|
235
235
|
},
|
|
236
236
|
task: async (_ctx) => {
|
|
237
237
|
const env = path.resolve(getPaths().base, ".env.defaults");
|
|
238
|
-
const
|
|
238
|
+
const apiUrl = getUserApiUrl();
|
|
239
|
+
const line = `
|
|
240
|
+
|
|
241
|
+
CEDAR_API_URL=${apiUrl}
|
|
242
|
+
`;
|
|
239
243
|
fs.appendFileSync(env, line);
|
|
240
244
|
}
|
|
241
245
|
};
|
|
@@ -266,7 +270,7 @@ const handler = async ({ force, database }) => {
|
|
|
266
270
|
updateGraphQLFunction(),
|
|
267
271
|
updateDbAuth(),
|
|
268
272
|
updateApp(),
|
|
269
|
-
updateApiURLTask("${
|
|
273
|
+
updateApiURLTask("${CEDAR_API_URL}"),
|
|
270
274
|
addToDotEnvDefaultTask(),
|
|
271
275
|
printSetupNotes(notes)
|
|
272
276
|
],
|
|
@@ -5,7 +5,7 @@ import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
|
5
5
|
import { getPaths, getPrismaSchemas } from "@cedarjs/project-config";
|
|
6
6
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
7
7
|
import { writeFilesTask, printSetupNotes } from "../../../../lib/index.js";
|
|
8
|
-
import { addFilesTask
|
|
8
|
+
import { addFilesTask } from "../helpers/index.js";
|
|
9
9
|
import {
|
|
10
10
|
POSTGRES_YAML,
|
|
11
11
|
RENDER_HEALTH_CHECK,
|
|
@@ -80,7 +80,6 @@ const handler = async ({ force, database }) => {
|
|
|
80
80
|
return writeFilesTask(files, { overwriteExisting: force });
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
-
updateApiURLTask("/.redwood/functions"),
|
|
84
83
|
// Add health check api function
|
|
85
84
|
addFilesTask({
|
|
86
85
|
files: additionalFiles,
|
|
@@ -10,8 +10,8 @@ const flightcontrolConfig = {
|
|
|
10
10
|
},
|
|
11
11
|
services: [
|
|
12
12
|
{
|
|
13
|
-
id: "
|
|
14
|
-
name: "
|
|
13
|
+
id: "cedar-api",
|
|
14
|
+
name: "Cedar API",
|
|
15
15
|
type: "web",
|
|
16
16
|
buildType: "nixpacks",
|
|
17
17
|
cpu: 0.5,
|
|
@@ -24,14 +24,14 @@ const flightcontrolConfig = {
|
|
|
24
24
|
type: "ec2"
|
|
25
25
|
},
|
|
26
26
|
envVariables: {
|
|
27
|
-
|
|
28
|
-
fromService: { id: "
|
|
27
|
+
CEDAR_WEB_URL: {
|
|
28
|
+
fromService: { id: "cedar-web", value: "origin" }
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
|
-
id: "
|
|
34
|
-
name: "
|
|
33
|
+
id: "cedar-web",
|
|
34
|
+
name: "Cedar Web",
|
|
35
35
|
type: "static",
|
|
36
36
|
buildType: "nixpacks",
|
|
37
37
|
singlePageApp: true,
|
|
@@ -41,8 +41,8 @@ const flightcontrolConfig = {
|
|
|
41
41
|
type: "ec2"
|
|
42
42
|
},
|
|
43
43
|
envVariables: {
|
|
44
|
-
|
|
45
|
-
fromService: { id: "
|
|
44
|
+
CEDAR_API_URL: {
|
|
45
|
+
fromService: { id: "cedar-api", value: "origin" }
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { getPaths } from "../../../../lib/index.js";
|
|
3
|
+
import { getUserApiUrl } from "../helpers/index.js";
|
|
3
4
|
const PROJECT_NAME = path.basename(getPaths().base);
|
|
4
5
|
const RENDER_YAML = (database) => {
|
|
6
|
+
const apiUrl = getUserApiUrl().replace(/\/$/, "");
|
|
5
7
|
return `# Quick links to the docs:
|
|
6
8
|
# - Redwood on Render: https://render.com/docs/deploy-redwood
|
|
7
9
|
# - Render's Blueprint spec: https://render.com/docs/yaml-spec
|
|
@@ -19,7 +21,7 @@ services:
|
|
|
19
21
|
|
|
20
22
|
routes:
|
|
21
23
|
- type: rewrite
|
|
22
|
-
source:
|
|
24
|
+
source: ${apiUrl}/*
|
|
23
25
|
# Replace \`destination\` here after your first deploy:
|
|
24
26
|
#
|
|
25
27
|
# \`\`\`
|
|
@@ -99,7 +99,7 @@ ENV NODE_ENV=production
|
|
|
99
99
|
# This is important if you intend to configure GraphQL to use Realtime.
|
|
100
100
|
#
|
|
101
101
|
# CMD [ "./api/dist/server.js" ]
|
|
102
|
-
CMD [
|
|
102
|
+
CMD ["node_modules/.bin/cedarjs-server", "api"]
|
|
103
103
|
|
|
104
104
|
# web serve
|
|
105
105
|
# ---------
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,6 @@ cwd ??= process.env.CEDAR_CWD;
|
|
|
51
51
|
cwd ??= process.env.RWJS_CWD;
|
|
52
52
|
cwd = getTomlDir(cwd);
|
|
53
53
|
process.env.CEDAR_CWD = cwd;
|
|
54
|
-
process.env.RWJS_CWD = cwd;
|
|
55
54
|
if (process.argv[1]?.endsWith("redwood.js")) {
|
|
56
55
|
const tomlPath = getConfigPath(cwd);
|
|
57
56
|
const toml = fs.readFileSync(tomlPath, "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarjs/cli",
|
|
3
|
-
"version": "5.0.0-canary.
|
|
3
|
+
"version": "5.0.0-canary.13874+8e13ad8024",
|
|
4
4
|
"description": "The CedarJS Command Line",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,16 +33,16 @@
|
|
|
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.
|
|
37
|
-
"@cedarjs/cli-helpers": "5.0.0-canary.
|
|
38
|
-
"@cedarjs/fastify-web": "5.0.0-canary.
|
|
39
|
-
"@cedarjs/internal": "5.0.0-canary.
|
|
40
|
-
"@cedarjs/prerender": "5.0.0-canary.
|
|
41
|
-
"@cedarjs/project-config": "5.0.0-canary.
|
|
42
|
-
"@cedarjs/structure": "5.0.0-canary.
|
|
43
|
-
"@cedarjs/telemetry": "5.0.0-canary.
|
|
44
|
-
"@cedarjs/utils": "5.0.0-canary.
|
|
45
|
-
"@cedarjs/web-server": "5.0.0-canary.
|
|
36
|
+
"@cedarjs/api-server": "5.0.0-canary.13874",
|
|
37
|
+
"@cedarjs/cli-helpers": "5.0.0-canary.13874",
|
|
38
|
+
"@cedarjs/fastify-web": "5.0.0-canary.13874",
|
|
39
|
+
"@cedarjs/internal": "5.0.0-canary.13874",
|
|
40
|
+
"@cedarjs/prerender": "5.0.0-canary.13874",
|
|
41
|
+
"@cedarjs/project-config": "5.0.0-canary.13874",
|
|
42
|
+
"@cedarjs/structure": "5.0.0-canary.13874",
|
|
43
|
+
"@cedarjs/telemetry": "5.0.0-canary.13874",
|
|
44
|
+
"@cedarjs/utils": "5.0.0-canary.13874",
|
|
45
|
+
"@cedarjs/web-server": "5.0.0-canary.13874",
|
|
46
46
|
"@listr2/prompt-adapter-enquirer": "4.2.1",
|
|
47
47
|
"@opentelemetry/api": "1.9.0",
|
|
48
48
|
"@opentelemetry/core": "1.30.1",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@opentelemetry/resources": "1.30.1",
|
|
51
51
|
"@opentelemetry/sdk-trace-node": "1.30.1",
|
|
52
52
|
"@opentelemetry/semantic-conventions": "1.38.0",
|
|
53
|
-
"@prisma/internals": "7.
|
|
53
|
+
"@prisma/internals": "7.8.0",
|
|
54
54
|
"ansis": "4.2.0",
|
|
55
55
|
"archiver": "7.0.1",
|
|
56
56
|
"boxen": "5.1.2",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"pascalcase": "1.0.0",
|
|
76
76
|
"pluralize": "8.0.0",
|
|
77
77
|
"portfinder": "1.0.38",
|
|
78
|
-
"prettier": "3.8.
|
|
79
|
-
"prisma": "7.
|
|
78
|
+
"prettier": "3.8.3",
|
|
79
|
+
"prisma": "7.8.0",
|
|
80
80
|
"prompts": "2.4.2",
|
|
81
81
|
"recast": "0.23.11",
|
|
82
82
|
"rimraf": "6.1.3",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"@babel/cli": "7.28.6",
|
|
96
96
|
"@babel/core": "^7.26.10",
|
|
97
97
|
"@types/archiver": "^7.0.0",
|
|
98
|
-
"memfs": "4.57.
|
|
98
|
+
"memfs": "4.57.2",
|
|
99
99
|
"node-ssh": "13.2.1",
|
|
100
100
|
"ts-dedent": "2.2.0",
|
|
101
101
|
"tsx": "4.21.0",
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"publishConfig": {
|
|
109
109
|
"access": "public"
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "8e13ad80246a92eda7120fd66b98d3052daae274"
|
|
112
112
|
}
|