@meetploy/cli 1.14.0 → 1.15.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.
package/dist/index.js
CHANGED
|
@@ -54,30 +54,22 @@ async function detectMonorepo(projectDir) {
|
|
|
54
54
|
const hasLerna = await fileExists(join(projectDir, "lerna.json"));
|
|
55
55
|
if (hasLerna) {
|
|
56
56
|
indicators.push("lerna.json");
|
|
57
|
-
|
|
58
|
-
type = "lerna";
|
|
59
|
-
}
|
|
57
|
+
type ??= "lerna";
|
|
60
58
|
}
|
|
61
59
|
const hasNx = await fileExists(join(projectDir, "nx.json"));
|
|
62
60
|
if (hasNx) {
|
|
63
61
|
indicators.push("nx.json");
|
|
64
|
-
|
|
65
|
-
type = "nx";
|
|
66
|
-
}
|
|
62
|
+
type ??= "nx";
|
|
67
63
|
}
|
|
68
64
|
const hasTurbo = await fileExists(join(projectDir, "turbo.json"));
|
|
69
65
|
if (hasTurbo) {
|
|
70
66
|
indicators.push("turbo.json");
|
|
71
|
-
|
|
72
|
-
type = "turbo";
|
|
73
|
-
}
|
|
67
|
+
type ??= "turbo";
|
|
74
68
|
}
|
|
75
69
|
const hasRush = await fileExists(join(projectDir, "rush.json"));
|
|
76
70
|
if (hasRush) {
|
|
77
71
|
indicators.push("rush.json");
|
|
78
|
-
|
|
79
|
-
type = "rush";
|
|
80
|
-
}
|
|
72
|
+
type ??= "rush";
|
|
81
73
|
}
|
|
82
74
|
return {
|
|
83
75
|
isMonorepo: indicators.length > 0,
|
|
@@ -191,6 +183,19 @@ function validatePloyConfig(config, configFile = "ploy.yaml", options = {}) {
|
|
|
191
183
|
validatedConfig.out = validateRelativePath(config.out, "out", configFile);
|
|
192
184
|
validatedConfig.base = validateRelativePath(config.base, "base", configFile);
|
|
193
185
|
validatedConfig.main = validateRelativePath(config.main, "main", configFile);
|
|
186
|
+
if (config.env !== void 0) {
|
|
187
|
+
if (typeof config.env !== "object" || config.env === null) {
|
|
188
|
+
throw new Error(`'env' in ${configFile} must be a map of KEY: value`);
|
|
189
|
+
}
|
|
190
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
191
|
+
if (!BINDING_NAME_REGEX.test(key)) {
|
|
192
|
+
throw new Error(`Invalid env key '${key}' in ${configFile}. Env keys must be uppercase with underscores (e.g., FOO, MY_VAR)`);
|
|
193
|
+
}
|
|
194
|
+
if (typeof value !== "string") {
|
|
195
|
+
throw new Error(`Env key '${key}' in ${configFile} must have a string value`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
194
199
|
validateBindings(config.db, "db", configFile);
|
|
195
200
|
validateBindings(config.queue, "queue", configFile);
|
|
196
201
|
validateBindings(config.cache, "cache", configFile);
|
|
@@ -271,7 +276,50 @@ function readAndValidatePloyConfigSync(projectDir, configPath, validationOptions
|
|
|
271
276
|
return validatePloyConfig(config, configFile, validationOptions);
|
|
272
277
|
}
|
|
273
278
|
function hasBindings(config) {
|
|
274
|
-
return !!(config.db
|
|
279
|
+
return !!(config.env ?? config.db ?? config.queue ?? config.cache ?? config.state ?? config.workflow ?? config.ai ?? config.auth);
|
|
280
|
+
}
|
|
281
|
+
function parseDotEnv(content) {
|
|
282
|
+
const result = {};
|
|
283
|
+
for (const line of content.split("\n")) {
|
|
284
|
+
const trimmed = line.trim();
|
|
285
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const eqIndex = trimmed.indexOf("=");
|
|
289
|
+
if (eqIndex === -1) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
293
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
294
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
295
|
+
value = value.slice(1, -1);
|
|
296
|
+
}
|
|
297
|
+
result[key] = value;
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
function loadDotEnvSync(projectDir) {
|
|
302
|
+
const envPath = join(projectDir, ".env");
|
|
303
|
+
if (!existsSync(envPath)) {
|
|
304
|
+
return {};
|
|
305
|
+
}
|
|
306
|
+
const content = readFileSync(envPath, "utf-8");
|
|
307
|
+
return parseDotEnv(content);
|
|
308
|
+
}
|
|
309
|
+
function resolveEnvVars(configEnv, dotEnv, processEnv) {
|
|
310
|
+
const result = {};
|
|
311
|
+
for (const [key, value] of Object.entries(configEnv)) {
|
|
312
|
+
if (value.startsWith("$")) {
|
|
313
|
+
const refName = value.slice(1);
|
|
314
|
+
const resolved = dotEnv[refName] ?? processEnv[refName];
|
|
315
|
+
if (resolved !== void 0) {
|
|
316
|
+
result[key] = resolved;
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
result[key] = value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return result;
|
|
275
323
|
}
|
|
276
324
|
function getWorkerEntryPoint(projectDir, config) {
|
|
277
325
|
if (config.main) {
|
|
@@ -314,10 +362,13 @@ __export(cli_exports, {
|
|
|
314
362
|
getWorkerEntryPoint: () => getWorkerEntryPoint,
|
|
315
363
|
hasBindings: () => hasBindings,
|
|
316
364
|
isPnpmWorkspace: () => isPnpmWorkspace,
|
|
365
|
+
loadDotEnvSync: () => loadDotEnvSync,
|
|
366
|
+
parseDotEnv: () => parseDotEnv,
|
|
317
367
|
readAndValidatePloyConfig: () => readAndValidatePloyConfig,
|
|
318
368
|
readAndValidatePloyConfigSync: () => readAndValidatePloyConfigSync,
|
|
319
369
|
readPloyConfig: () => readPloyConfig,
|
|
320
370
|
readPloyConfigSync: () => readPloyConfigSync,
|
|
371
|
+
resolveEnvVars: () => resolveEnvVars,
|
|
321
372
|
validatePloyConfig: () => validatePloyConfig
|
|
322
373
|
});
|
|
323
374
|
var init_cli = __esm({
|
|
@@ -1043,7 +1094,7 @@ export async function executeWorkflow<TInput, TOutput, TEnv>(
|
|
|
1043
1094
|
`;
|
|
1044
1095
|
}
|
|
1045
1096
|
});
|
|
1046
|
-
function generateWrapperCode(config, mockServiceUrl) {
|
|
1097
|
+
function generateWrapperCode(config, mockServiceUrl, envVars) {
|
|
1047
1098
|
const imports = [];
|
|
1048
1099
|
const bindings = [];
|
|
1049
1100
|
if (config.db) {
|
|
@@ -1133,6 +1184,11 @@ function generateWrapperCode(config, mockServiceUrl) {
|
|
|
1133
1184
|
}
|
|
1134
1185
|
}
|
|
1135
1186
|
}` : "";
|
|
1187
|
+
const envVarsEntries = envVars && Object.keys(envVars).length > 0 ? Object.entries(envVars).map(([key, value]) => ` ${JSON.stringify(key)}: ${JSON.stringify(value)}`).join(",\n") : "";
|
|
1188
|
+
const envVarsCode = envVarsEntries ? `
|
|
1189
|
+
injectedEnv.vars = {
|
|
1190
|
+
${envVarsEntries}
|
|
1191
|
+
};` : "";
|
|
1136
1192
|
return `${imports.join("\n")}
|
|
1137
1193
|
|
|
1138
1194
|
const ployBindings = {
|
|
@@ -1141,7 +1197,7 @@ ${bindings.join("\n")}
|
|
|
1141
1197
|
|
|
1142
1198
|
export default {
|
|
1143
1199
|
async fetch(request, env, ctx) {
|
|
1144
|
-
const injectedEnv = { ...env, ...ployBindings }
|
|
1200
|
+
const injectedEnv = { ...env, ...ployBindings };${envVarsCode}
|
|
1145
1201
|
${workflowHandlerCode}
|
|
1146
1202
|
${queueHandlerCode}
|
|
1147
1203
|
|
|
@@ -1154,7 +1210,7 @@ export default {
|
|
|
1154
1210
|
|
|
1155
1211
|
async scheduled(event, env, ctx) {
|
|
1156
1212
|
if (userWorker.scheduled) {
|
|
1157
|
-
const injectedEnv = { ...env, ...ployBindings }
|
|
1213
|
+
const injectedEnv = { ...env, ...ployBindings };${envVarsCode}
|
|
1158
1214
|
return userWorker.scheduled(event, injectedEnv, ctx);
|
|
1159
1215
|
}
|
|
1160
1216
|
}
|
|
@@ -1209,8 +1265,8 @@ function createRuntimePlugin(_config) {
|
|
|
1209
1265
|
};
|
|
1210
1266
|
}
|
|
1211
1267
|
async function bundleWorker(options) {
|
|
1212
|
-
const { projectDir, tempDir, entryPoint, config, mockServiceUrl } = options;
|
|
1213
|
-
const wrapperCode = generateWrapperCode(config, mockServiceUrl);
|
|
1268
|
+
const { projectDir, tempDir, entryPoint, config, mockServiceUrl, envVars } = options;
|
|
1269
|
+
const wrapperCode = generateWrapperCode(config, mockServiceUrl, envVars);
|
|
1214
1270
|
const wrapperPath = join(tempDir, "wrapper.ts");
|
|
1215
1271
|
writeFileSync(wrapperPath, wrapperCode);
|
|
1216
1272
|
const bundlePath = join(tempDir, "worker.bundle.js");
|
|
@@ -1352,16 +1408,15 @@ function createFileWatcher(srcDir, onRebuild) {
|
|
|
1352
1408
|
if (debounceTimer) {
|
|
1353
1409
|
clearTimeout(debounceTimer);
|
|
1354
1410
|
}
|
|
1355
|
-
debounceTimer = setTimeout(
|
|
1411
|
+
debounceTimer = setTimeout(() => {
|
|
1356
1412
|
if (isRebuilding) {
|
|
1357
1413
|
return;
|
|
1358
1414
|
}
|
|
1359
1415
|
isRebuilding = true;
|
|
1360
|
-
|
|
1361
|
-
await onRebuild();
|
|
1362
|
-
} finally {
|
|
1416
|
+
onRebuild().finally(() => {
|
|
1363
1417
|
isRebuilding = false;
|
|
1364
|
-
}
|
|
1418
|
+
}).catch(() => {
|
|
1419
|
+
});
|
|
1365
1420
|
}, 100);
|
|
1366
1421
|
}
|
|
1367
1422
|
return {
|
|
@@ -1402,7 +1457,7 @@ function createFileWatcher(srcDir, onRebuild) {
|
|
|
1402
1457
|
debounceTimer = null;
|
|
1403
1458
|
}
|
|
1404
1459
|
if (watcher) {
|
|
1405
|
-
watcher.close();
|
|
1460
|
+
void watcher.close();
|
|
1406
1461
|
watcher = null;
|
|
1407
1462
|
}
|
|
1408
1463
|
}
|
|
@@ -1421,14 +1476,28 @@ var init_watcher = __esm({
|
|
|
1421
1476
|
}
|
|
1422
1477
|
});
|
|
1423
1478
|
|
|
1479
|
+
// ../emulator/dist/config/env.js
|
|
1480
|
+
function resolveEnvVars2(projectDir, config) {
|
|
1481
|
+
if (!config.env) {
|
|
1482
|
+
return {};
|
|
1483
|
+
}
|
|
1484
|
+
const dotEnv = loadDotEnvSync(projectDir);
|
|
1485
|
+
return resolveEnvVars(config.env, dotEnv, process.env);
|
|
1486
|
+
}
|
|
1487
|
+
var init_env = __esm({
|
|
1488
|
+
"../emulator/dist/config/env.js"() {
|
|
1489
|
+
init_cli();
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1424
1493
|
// ../emulator/dist/config/ploy-config.js
|
|
1425
1494
|
function readPloyConfig2(projectDir, configPath) {
|
|
1426
1495
|
const config = readPloyConfigSync(projectDir, configPath);
|
|
1427
1496
|
if (!config.kind) {
|
|
1428
|
-
throw new Error(`Missing required field 'kind' in ${configPath
|
|
1497
|
+
throw new Error(`Missing required field 'kind' in ${configPath ?? "ploy.yaml"}`);
|
|
1429
1498
|
}
|
|
1430
1499
|
if (config.kind !== "dynamic" && config.kind !== "worker") {
|
|
1431
|
-
throw new Error(`Invalid kind '${config.kind}' in ${configPath
|
|
1500
|
+
throw new Error(`Invalid kind '${config.kind}' in ${configPath ?? "ploy.yaml"}. Must be 'dynamic' or 'worker'`);
|
|
1432
1501
|
}
|
|
1433
1502
|
return config;
|
|
1434
1503
|
}
|
|
@@ -1444,7 +1513,7 @@ function generateWorkerdConfig(options) {
|
|
|
1444
1513
|
const { port, mockServicePort } = options;
|
|
1445
1514
|
const services = [
|
|
1446
1515
|
'(name = "main", worker = .worker)',
|
|
1447
|
-
`(name = "mock", external = (address = "localhost:${mockServicePort}", http = ()))`,
|
|
1516
|
+
`(name = "mock", external = (address = "localhost:${String(mockServicePort)}", http = ()))`,
|
|
1448
1517
|
'(name = "internet", network = (allow = ["public", "private", "local"], tlsOptions = (trustBrowserCas = true)))'
|
|
1449
1518
|
];
|
|
1450
1519
|
const bindings = [
|
|
@@ -1458,7 +1527,7 @@ const config :Workerd.Config = (
|
|
|
1458
1527
|
${services.join(",\n ")}
|
|
1459
1528
|
],
|
|
1460
1529
|
sockets = [
|
|
1461
|
-
(name = "http", address = "*:${port}", http = (), service = "main")
|
|
1530
|
+
(name = "http", address = "*:${String(port)}", http = (), service = "main")
|
|
1462
1531
|
]
|
|
1463
1532
|
);
|
|
1464
1533
|
|
|
@@ -1664,7 +1733,7 @@ function createAuthHandlers(db) {
|
|
|
1664
1733
|
return c.json({ error: message }, 500);
|
|
1665
1734
|
}
|
|
1666
1735
|
};
|
|
1667
|
-
const meHandler =
|
|
1736
|
+
const meHandler = (c) => {
|
|
1668
1737
|
try {
|
|
1669
1738
|
const cookieToken = getCookie(c, "ploy_session");
|
|
1670
1739
|
const authHeader = c.req.header("Authorization");
|
|
@@ -1708,7 +1777,7 @@ function createAuthHandlers(db) {
|
|
|
1708
1777
|
return c.json({ error: message }, 500);
|
|
1709
1778
|
}
|
|
1710
1779
|
};
|
|
1711
|
-
const signoutHandler =
|
|
1780
|
+
const signoutHandler = (c) => {
|
|
1712
1781
|
try {
|
|
1713
1782
|
const sessionToken = getCookie(c, "ploy_session");
|
|
1714
1783
|
if (sessionToken) {
|
|
@@ -1823,8 +1892,8 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1823
1892
|
if (tableName !== "auth_users" && tableName !== "auth_sessions") {
|
|
1824
1893
|
return c.json({ error: "Table not found" }, 404);
|
|
1825
1894
|
}
|
|
1826
|
-
const limit = parseInt(c.req.query("limit")
|
|
1827
|
-
const offset = parseInt(c.req.query("offset")
|
|
1895
|
+
const limit = parseInt(c.req.query("limit") ?? "50", 10);
|
|
1896
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
1828
1897
|
try {
|
|
1829
1898
|
const db = dbManager2.emulatorDb;
|
|
1830
1899
|
const columnsResult = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
@@ -2036,8 +2105,8 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2036
2105
|
return c.json({ error: `Database binding '${binding}' not found` }, 404);
|
|
2037
2106
|
}
|
|
2038
2107
|
const tableName = c.req.param("tableName");
|
|
2039
|
-
const limit = parseInt(c.req.query("limit")
|
|
2040
|
-
const offset = parseInt(c.req.query("offset")
|
|
2108
|
+
const limit = parseInt(c.req.query("limit") ?? "50", 10);
|
|
2109
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
2041
2110
|
try {
|
|
2042
2111
|
const db = dbManager2.getD1Database(resourceName);
|
|
2043
2112
|
const columnsResult = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
@@ -2118,7 +2187,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2118
2187
|
app.get("/api/queue/:binding/messages", (c) => {
|
|
2119
2188
|
const binding = c.req.param("binding");
|
|
2120
2189
|
const queueName = config.queue?.[binding];
|
|
2121
|
-
const limit = parseInt(c.req.query("limit")
|
|
2190
|
+
const limit = parseInt(c.req.query("limit") ?? "10", 10);
|
|
2122
2191
|
if (!queueName) {
|
|
2123
2192
|
return c.json({ error: "Queue not found" }, 404);
|
|
2124
2193
|
}
|
|
@@ -2145,8 +2214,8 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2145
2214
|
app.get("/api/cache/:binding/entries", (c) => {
|
|
2146
2215
|
const binding = c.req.param("binding");
|
|
2147
2216
|
const cacheName = config.cache?.[binding];
|
|
2148
|
-
const limit = parseInt(c.req.query("limit")
|
|
2149
|
-
const offset = parseInt(c.req.query("offset")
|
|
2217
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
2218
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
2150
2219
|
const now = Math.floor(Date.now() / 1e3);
|
|
2151
2220
|
if (!cacheName) {
|
|
2152
2221
|
return c.json({ error: "Cache binding not found" }, 404);
|
|
@@ -2178,9 +2247,9 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2178
2247
|
app.get("/api/state/:binding/entries", (c) => {
|
|
2179
2248
|
const binding = c.req.param("binding");
|
|
2180
2249
|
const stateName = config.state?.[binding];
|
|
2181
|
-
const limit = parseInt(c.req.query("limit")
|
|
2182
|
-
const offset = parseInt(c.req.query("offset")
|
|
2183
|
-
const search = c.req.query("search")
|
|
2250
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
2251
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
2252
|
+
const search = c.req.query("search") ?? "";
|
|
2184
2253
|
if (!stateName) {
|
|
2185
2254
|
return c.json({ error: "State binding not found" }, 404);
|
|
2186
2255
|
}
|
|
@@ -2210,7 +2279,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2210
2279
|
app.get("/api/workflow/:binding/executions", (c) => {
|
|
2211
2280
|
const binding = c.req.param("binding");
|
|
2212
2281
|
const workflowConfig = config.workflow?.[binding];
|
|
2213
|
-
const limit = parseInt(c.req.query("limit")
|
|
2282
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
2214
2283
|
if (!workflowConfig) {
|
|
2215
2284
|
return c.json({ error: "Workflow not found" }, 404);
|
|
2216
2285
|
}
|
|
@@ -2473,7 +2542,7 @@ function createQueueHandlers(db) {
|
|
|
2473
2542
|
const transaction = db.transaction(() => {
|
|
2474
2543
|
for (const msg of messages) {
|
|
2475
2544
|
const id = randomUUID();
|
|
2476
|
-
const visibleAt = now + (msg.delaySeconds
|
|
2545
|
+
const visibleAt = now + (msg.delaySeconds ?? 0);
|
|
2477
2546
|
insert.run(id, queueName, JSON.stringify(msg.payload), visibleAt);
|
|
2478
2547
|
messageIds.push(id);
|
|
2479
2548
|
}
|
|
@@ -3102,6 +3171,7 @@ var init_emulator = __esm({
|
|
|
3102
3171
|
"../emulator/dist/emulator.js"() {
|
|
3103
3172
|
init_bundler();
|
|
3104
3173
|
init_watcher();
|
|
3174
|
+
init_env();
|
|
3105
3175
|
init_ploy_config2();
|
|
3106
3176
|
init_workerd_config();
|
|
3107
3177
|
init_mock_server();
|
|
@@ -3119,6 +3189,7 @@ var init_emulator = __esm({
|
|
|
3119
3189
|
workerdProcess = null;
|
|
3120
3190
|
fileWatcher = null;
|
|
3121
3191
|
queueProcessor = null;
|
|
3192
|
+
resolvedEnvVars = {};
|
|
3122
3193
|
constructor(options = {}) {
|
|
3123
3194
|
const port = options.port ?? 8787;
|
|
3124
3195
|
this.options = {
|
|
@@ -3136,20 +3207,24 @@ var init_emulator = __esm({
|
|
|
3136
3207
|
try {
|
|
3137
3208
|
this.config = readPloyConfig2(this.projectDir, this.options.configPath);
|
|
3138
3209
|
debug(`Loaded config: ${JSON.stringify(this.config)}`, this.options.verbose);
|
|
3210
|
+
this.resolvedEnvVars = resolveEnvVars2(this.projectDir, this.config);
|
|
3211
|
+
if (Object.keys(this.resolvedEnvVars).length > 0) {
|
|
3212
|
+
debug(`Resolved env vars: ${Object.keys(this.resolvedEnvVars).join(", ")}`, this.options.verbose);
|
|
3213
|
+
}
|
|
3139
3214
|
this.tempDir = ensureTempDir(this.projectDir);
|
|
3140
3215
|
ensureDataDir(this.projectDir);
|
|
3141
3216
|
debug(`Temp dir: ${this.tempDir}`, this.options.verbose);
|
|
3142
3217
|
this.dbManager = initializeDatabases(this.projectDir);
|
|
3143
3218
|
debug("Initialized databases", this.options.verbose);
|
|
3144
|
-
const workerUrl = `http://${this.options.host}:${this.options.port}`;
|
|
3219
|
+
const workerUrl = `http://${this.options.host}:${String(this.options.port)}`;
|
|
3145
3220
|
const dashboardPort = this.options.dashboardPort;
|
|
3146
3221
|
this.mockServer = await startMockServer(this.dbManager, this.config, {
|
|
3147
3222
|
workerUrl,
|
|
3148
3223
|
port: dashboardPort,
|
|
3149
3224
|
dashboardEnabled: true
|
|
3150
3225
|
});
|
|
3151
|
-
debug(`Mock server started on port ${this.mockServer.port}`, this.options.verbose);
|
|
3152
|
-
const mockServiceUrl = `http://localhost:${this.mockServer.port}`;
|
|
3226
|
+
debug(`Mock server started on port ${String(this.mockServer.port)}`, this.options.verbose);
|
|
3227
|
+
const mockServiceUrl = `http://localhost:${String(this.mockServer.port)}`;
|
|
3153
3228
|
const entryPoint = getWorkerEntryPoint2(this.projectDir, this.config);
|
|
3154
3229
|
debug(`Entry point: ${entryPoint}`, this.options.verbose);
|
|
3155
3230
|
await this.bundle(entryPoint, mockServiceUrl);
|
|
@@ -3176,13 +3251,16 @@ var init_emulator = __esm({
|
|
|
3176
3251
|
debug(`Watching ${srcDir} for changes`, this.options.verbose);
|
|
3177
3252
|
}
|
|
3178
3253
|
if (this.config.queue) {
|
|
3179
|
-
const workerUrl2 = `http://${this.options.host}:${this.options.port}`;
|
|
3254
|
+
const workerUrl2 = `http://${this.options.host}:${String(this.options.port)}`;
|
|
3180
3255
|
this.queueProcessor = createQueueProcessor(this.dbManager.emulatorDb, this.config.queue, workerUrl2);
|
|
3181
3256
|
this.queueProcessor.start();
|
|
3182
3257
|
debug("Queue processor started", this.options.verbose);
|
|
3183
3258
|
}
|
|
3184
|
-
success(`Emulator running at http://${this.options.host}:${this.options.port}`);
|
|
3185
|
-
log(` Dashboard: http://${this.options.host}:${this.mockServer.port}`);
|
|
3259
|
+
success(`Emulator running at http://${this.options.host}:${String(this.options.port)}`);
|
|
3260
|
+
log(` Dashboard: http://${this.options.host}:${String(this.mockServer.port)}`);
|
|
3261
|
+
if (Object.keys(this.resolvedEnvVars).length > 0) {
|
|
3262
|
+
log(` Env vars: ${Object.keys(this.resolvedEnvVars).join(", ")}`);
|
|
3263
|
+
}
|
|
3186
3264
|
if (this.config.db) {
|
|
3187
3265
|
log(` DB bindings: ${Object.keys(this.config.db).join(", ")}`);
|
|
3188
3266
|
}
|
|
@@ -3211,7 +3289,8 @@ var init_emulator = __esm({
|
|
|
3211
3289
|
tempDir: this.tempDir,
|
|
3212
3290
|
entryPoint,
|
|
3213
3291
|
config: this.config,
|
|
3214
|
-
mockServiceUrl
|
|
3292
|
+
mockServiceUrl,
|
|
3293
|
+
envVars: this.resolvedEnvVars
|
|
3215
3294
|
});
|
|
3216
3295
|
}
|
|
3217
3296
|
async startWorkerd(configPath) {
|
|
@@ -3230,16 +3309,14 @@ var init_emulator = __esm({
|
|
|
3230
3309
|
shell: true,
|
|
3231
3310
|
env: {
|
|
3232
3311
|
...process.env,
|
|
3233
|
-
PATH: `${workerdBinDir}:${process.env.PATH
|
|
3312
|
+
PATH: `${workerdBinDir}:${process.env.PATH ?? ""}`
|
|
3234
3313
|
}
|
|
3235
3314
|
});
|
|
3236
3315
|
let started = false;
|
|
3237
3316
|
let stderrOutput = "";
|
|
3238
3317
|
this.workerdProcess.stdout?.on("data", (data) => {
|
|
3239
3318
|
const output = data.toString();
|
|
3240
|
-
|
|
3241
|
-
process.stdout.write(output);
|
|
3242
|
-
}
|
|
3319
|
+
log(`[workerd stdout] ${output.trim()}`);
|
|
3243
3320
|
if (!started && (output.includes("Listening") || output.includes("running"))) {
|
|
3244
3321
|
started = true;
|
|
3245
3322
|
resolve(void 0);
|
|
@@ -3267,13 +3344,13 @@ var init_emulator = __esm({
|
|
|
3267
3344
|
});
|
|
3268
3345
|
this.workerdProcess.on("exit", (code) => {
|
|
3269
3346
|
if (code !== 0 && code !== null) {
|
|
3270
|
-
error(`workerd exited with code ${code}`);
|
|
3347
|
+
error(`workerd exited with code ${String(code)}`);
|
|
3271
3348
|
if (stderrOutput) {
|
|
3272
3349
|
error(`workerd stderr: ${stderrOutput.trim()}`);
|
|
3273
3350
|
}
|
|
3274
3351
|
}
|
|
3275
3352
|
if (!started) {
|
|
3276
|
-
reject(new Error(`workerd exited with code ${code}`));
|
|
3353
|
+
reject(new Error(`workerd exited with code ${String(code)}`));
|
|
3277
3354
|
}
|
|
3278
3355
|
});
|
|
3279
3356
|
setTimeout(() => {
|
|
@@ -3310,10 +3387,11 @@ var init_emulator = __esm({
|
|
|
3310
3387
|
return this.projectDir;
|
|
3311
3388
|
}
|
|
3312
3389
|
setupSignalHandlers() {
|
|
3313
|
-
const handler =
|
|
3390
|
+
const handler = () => {
|
|
3314
3391
|
log("\nShutting down...");
|
|
3315
|
-
|
|
3316
|
-
|
|
3392
|
+
void this.stop().then(() => {
|
|
3393
|
+
process.exit(0);
|
|
3394
|
+
});
|
|
3317
3395
|
};
|
|
3318
3396
|
process.on("SIGINT", handler);
|
|
3319
3397
|
process.on("SIGTERM", handler);
|
|
@@ -3428,7 +3506,7 @@ function createDevD1(databaseId, apiUrl) {
|
|
|
3428
3506
|
async batch(statements) {
|
|
3429
3507
|
const stmts = statements.map((s) => {
|
|
3430
3508
|
const data = s.__db_data;
|
|
3431
|
-
return data
|
|
3509
|
+
return data ?? s;
|
|
3432
3510
|
});
|
|
3433
3511
|
const response = await fetch(`${apiUrl}/db`, {
|
|
3434
3512
|
method: "POST",
|
|
@@ -3488,7 +3566,7 @@ async function initPloyForDev(config) {
|
|
|
3488
3566
|
globalThis.__PLOY_DEV_INITIALIZED__ = true;
|
|
3489
3567
|
const cliMockServerUrl = process.env.PLOY_MOCK_SERVER_URL;
|
|
3490
3568
|
if (cliMockServerUrl) {
|
|
3491
|
-
const configPath2 = config?.configPath
|
|
3569
|
+
const configPath2 = config?.configPath ?? "./ploy.yaml";
|
|
3492
3570
|
const projectDir2 = process.cwd();
|
|
3493
3571
|
let ployConfig2;
|
|
3494
3572
|
try {
|
|
@@ -3536,7 +3614,7 @@ async function initPloyForDev(config) {
|
|
|
3536
3614
|
console.log(`[Ploy] Using CLI mock server at ${cliMockServerUrl} (${features2.join(", ")})`);
|
|
3537
3615
|
return;
|
|
3538
3616
|
}
|
|
3539
|
-
const configPath = config?.configPath
|
|
3617
|
+
const configPath = config?.configPath ?? "./ploy.yaml";
|
|
3540
3618
|
const projectDir = process.cwd();
|
|
3541
3619
|
let ployConfig;
|
|
3542
3620
|
try {
|
|
@@ -3559,7 +3637,7 @@ async function initPloyForDev(config) {
|
|
|
3559
3637
|
ensureDataDir(projectDir);
|
|
3560
3638
|
dbManager = initializeDatabases(projectDir);
|
|
3561
3639
|
mockServer = await startMockServer(dbManager, ployConfig, {});
|
|
3562
|
-
const apiUrl = `http://localhost:${mockServer.port}`;
|
|
3640
|
+
const apiUrl = `http://localhost:${String(mockServer.port)}`;
|
|
3563
3641
|
const env = {};
|
|
3564
3642
|
if (hasDbBindings && ployConfig.db) {
|
|
3565
3643
|
for (const [bindingName, databaseId] of Object.entries(ployConfig.db)) {
|
|
@@ -3607,13 +3685,15 @@ async function initPloyForDev(config) {
|
|
|
3607
3685
|
dbManager.close();
|
|
3608
3686
|
}
|
|
3609
3687
|
});
|
|
3610
|
-
process.on("SIGINT",
|
|
3611
|
-
|
|
3612
|
-
|
|
3688
|
+
process.on("SIGINT", () => {
|
|
3689
|
+
void cleanup().then(() => {
|
|
3690
|
+
process.exit(0);
|
|
3691
|
+
});
|
|
3613
3692
|
});
|
|
3614
|
-
process.on("SIGTERM",
|
|
3615
|
-
|
|
3616
|
-
|
|
3693
|
+
process.on("SIGTERM", () => {
|
|
3694
|
+
void cleanup().then(() => {
|
|
3695
|
+
process.exit(0);
|
|
3696
|
+
});
|
|
3617
3697
|
});
|
|
3618
3698
|
}
|
|
3619
3699
|
var PLOY_CONTEXT_SYMBOL, mockServer, dbManager;
|
|
@@ -3650,7 +3730,7 @@ async function startDevDashboard(options = {}) {
|
|
|
3650
3730
|
port,
|
|
3651
3731
|
dashboardEnabled: true
|
|
3652
3732
|
});
|
|
3653
|
-
debug(`Mock server started on port ${mockServer2.port}`, verbose);
|
|
3733
|
+
debug(`Mock server started on port ${String(mockServer2.port)}`, verbose);
|
|
3654
3734
|
if (config.db) {
|
|
3655
3735
|
log(` DB bindings: ${Object.keys(config.db).join(", ")}`);
|
|
3656
3736
|
}
|
|
@@ -3945,8 +4025,8 @@ async function listTables(options) {
|
|
|
3945
4025
|
);
|
|
3946
4026
|
if (error2 || !data) {
|
|
3947
4027
|
throw new ApiClientError(
|
|
3948
|
-
error2?.message
|
|
3949
|
-
response
|
|
4028
|
+
error2?.message ?? "Failed to fetch tables",
|
|
4029
|
+
response.status
|
|
3950
4030
|
);
|
|
3951
4031
|
}
|
|
3952
4032
|
const tablesResult = data;
|
|
@@ -3967,13 +4047,13 @@ async function listTables(options) {
|
|
|
3967
4047
|
});
|
|
3968
4048
|
if (schemaError || !schemaData) {
|
|
3969
4049
|
throw new ApiClientError(
|
|
3970
|
-
schemaError?.message
|
|
3971
|
-
schemaResponse
|
|
4050
|
+
schemaError?.message ?? "Failed to fetch schema",
|
|
4051
|
+
schemaResponse.status
|
|
3972
4052
|
);
|
|
3973
4053
|
}
|
|
3974
4054
|
const schemaResult = schemaData;
|
|
3975
4055
|
for (const table of schemaResult.tables) {
|
|
3976
|
-
console.log(`Table: ${table.name}`);
|
|
4056
|
+
console.log(`Table: ${String(table.name)}`);
|
|
3977
4057
|
console.log("-".repeat(40));
|
|
3978
4058
|
console.log(
|
|
3979
4059
|
" " + "COLUMN".padEnd(20) + "TYPE".padEnd(15) + "NULLABLE".padEnd(10) + "PK"
|
|
@@ -3983,7 +4063,7 @@ async function listTables(options) {
|
|
|
3983
4063
|
const nullable = col.notNull ? "NO" : "YES";
|
|
3984
4064
|
const pk = col.primaryKey ? "YES" : "";
|
|
3985
4065
|
console.log(
|
|
3986
|
-
" " + col.name.padEnd(20) + col.type.padEnd(15) + nullable.padEnd(10) + pk
|
|
4066
|
+
" " + String(col.name).padEnd(20) + String(col.type).padEnd(15) + nullable.padEnd(10) + pk
|
|
3987
4067
|
);
|
|
3988
4068
|
}
|
|
3989
4069
|
console.log("");
|
|
@@ -3992,7 +4072,7 @@ async function listTables(options) {
|
|
|
3992
4072
|
console.log("NAME".padEnd(30) + "ROWS".padEnd(12) + "SIZE");
|
|
3993
4073
|
console.log("-".repeat(55));
|
|
3994
4074
|
for (const table of tablesResult.tables) {
|
|
3995
|
-
console.log(table.name.padEnd(30) + "-".padEnd(12) + "-");
|
|
4075
|
+
console.log(String(table.name).padEnd(30) + "-".padEnd(12) + "-");
|
|
3996
4076
|
}
|
|
3997
4077
|
}
|
|
3998
4078
|
console.log("");
|
|
@@ -4044,23 +4124,23 @@ async function viewAnalytics(options) {
|
|
|
4044
4124
|
);
|
|
4045
4125
|
if (error2 || !data) {
|
|
4046
4126
|
throw new ApiClientError(
|
|
4047
|
-
error2?.message
|
|
4048
|
-
response
|
|
4127
|
+
error2?.message ?? "Failed to fetch analytics",
|
|
4128
|
+
response.status
|
|
4049
4129
|
);
|
|
4050
4130
|
}
|
|
4051
4131
|
const insightsResult = data;
|
|
4052
4132
|
console.log("Database Analytics");
|
|
4053
4133
|
console.log("==================");
|
|
4054
4134
|
console.log("");
|
|
4055
|
-
console.log(` Period: ${insightsResult.timeRange}`);
|
|
4135
|
+
console.log(` Period: ${String(insightsResult.timeRange)}`);
|
|
4056
4136
|
console.log(
|
|
4057
|
-
` Total Queries: ${insightsResult.summary.totalQueries.toLocaleString()}`
|
|
4137
|
+
` Total Queries: ${String(insightsResult.summary.totalQueries.toLocaleString())}`
|
|
4058
4138
|
);
|
|
4059
4139
|
console.log(
|
|
4060
|
-
` Total Rows Read: ${insightsResult.summary.totalRowsRead.toLocaleString()}`
|
|
4140
|
+
` Total Rows Read: ${String(insightsResult.summary.totalRowsRead.toLocaleString())}`
|
|
4061
4141
|
);
|
|
4062
4142
|
console.log(
|
|
4063
|
-
` Total Rows Written: ${insightsResult.summary.totalRowsWritten.toLocaleString()}`
|
|
4143
|
+
` Total Rows Written: ${String(insightsResult.summary.totalRowsWritten.toLocaleString())}`
|
|
4064
4144
|
);
|
|
4065
4145
|
const avgDuration = insightsResult.summary.totalQueries > 0 ? insightsResult.summary.totalDurationMs / insightsResult.summary.totalQueries : 0;
|
|
4066
4146
|
console.log(` Avg Query Duration: ${avgDuration.toFixed(2)}ms`);
|
|
@@ -4071,15 +4151,21 @@ async function viewAnalytics(options) {
|
|
|
4071
4151
|
console.log("");
|
|
4072
4152
|
for (let i = 0; i < insightsResult.topQueries.length; i++) {
|
|
4073
4153
|
const insight = insightsResult.topQueries[i];
|
|
4074
|
-
console.log(`${i + 1}. ${truncateQuery(insight.queryText, 60)}`);
|
|
4075
|
-
console.log(` Calls: ${insight.totalCalls.toLocaleString()}`);
|
|
4076
|
-
console.log(` Avg Duration: ${insight.avgDurationMs.toFixed(2)}ms`);
|
|
4077
4154
|
console.log(
|
|
4078
|
-
|
|
4155
|
+
`${String(i + 1)}. ${truncateQuery(String(insight.queryText), 60)}`
|
|
4156
|
+
);
|
|
4157
|
+
console.log(` Calls: ${String(insight.totalCalls.toLocaleString())}`);
|
|
4158
|
+
console.log(
|
|
4159
|
+
` Avg Duration: ${String(insight.avgDurationMs.toFixed(2))}ms`
|
|
4160
|
+
);
|
|
4161
|
+
console.log(
|
|
4162
|
+
` Total Duration: ${String(insight.totalDurationMs.toFixed(2))}ms`
|
|
4079
4163
|
);
|
|
4080
|
-
console.log(` Rows Read: ${insight.totalRowsRead.toLocaleString()}`);
|
|
4081
4164
|
console.log(
|
|
4082
|
-
` Rows
|
|
4165
|
+
` Rows Read: ${String(insight.totalRowsRead.toLocaleString())}`
|
|
4166
|
+
);
|
|
4167
|
+
console.log(
|
|
4168
|
+
` Rows Written: ${String(insight.totalRowsWritten.toLocaleString())}`
|
|
4083
4169
|
);
|
|
4084
4170
|
console.log("");
|
|
4085
4171
|
}
|
|
@@ -4118,8 +4204,8 @@ async function listDatabases(options) {
|
|
|
4118
4204
|
);
|
|
4119
4205
|
if (error2 || !data) {
|
|
4120
4206
|
throw new ApiClientError(
|
|
4121
|
-
error2?.message
|
|
4122
|
-
response
|
|
4207
|
+
error2?.message ?? "Failed to list databases",
|
|
4208
|
+
response.status
|
|
4123
4209
|
);
|
|
4124
4210
|
}
|
|
4125
4211
|
const databases = data.databases;
|
|
@@ -4136,10 +4222,10 @@ async function listDatabases(options) {
|
|
|
4136
4222
|
);
|
|
4137
4223
|
console.log("-".repeat(80));
|
|
4138
4224
|
for (const db of databases) {
|
|
4139
|
-
const id = db.id.substring(0, Math.min(24, db.id.length));
|
|
4140
|
-
const name = db.name.substring(0, Math.min(23, db.name.length)).padEnd(25);
|
|
4141
|
-
const region = (db.region || "-").padEnd(15);
|
|
4142
|
-
const created = db.createdAt ? new Date(db.createdAt).toLocaleDateString() : "-";
|
|
4225
|
+
const id = String(db.id).substring(0, Math.min(24, String(db.id).length));
|
|
4226
|
+
const name = String(db.name).substring(0, Math.min(23, String(db.name).length)).padEnd(25);
|
|
4227
|
+
const region = (String(db.region) || "-").padEnd(15);
|
|
4228
|
+
const created = db.createdAt ? new Date(String(db.createdAt)).toLocaleDateString() : "-";
|
|
4143
4229
|
console.log(`${id} ${name}${region}${created}`);
|
|
4144
4230
|
}
|
|
4145
4231
|
console.log("");
|
|
@@ -4286,8 +4372,8 @@ async function retryDeployment(deploymentId) {
|
|
|
4286
4372
|
);
|
|
4287
4373
|
if (error2 || !data) {
|
|
4288
4374
|
throw new ApiClientError(
|
|
4289
|
-
error2?.message
|
|
4290
|
-
response
|
|
4375
|
+
error2?.message ?? "Failed to retry deployment",
|
|
4376
|
+
response.status
|
|
4291
4377
|
);
|
|
4292
4378
|
}
|
|
4293
4379
|
const retryResult = data;
|
|
@@ -4325,8 +4411,8 @@ async function viewDeploymentLogs(deploymentId, _options) {
|
|
|
4325
4411
|
);
|
|
4326
4412
|
if (error2 || !data) {
|
|
4327
4413
|
throw new ApiClientError(
|
|
4328
|
-
error2?.message
|
|
4329
|
-
response
|
|
4414
|
+
error2?.message ?? "Failed to fetch logs",
|
|
4415
|
+
response.status
|
|
4330
4416
|
);
|
|
4331
4417
|
}
|
|
4332
4418
|
const logsResult = data;
|
|
@@ -4363,8 +4449,8 @@ async function viewDeployment(deploymentId, _options) {
|
|
|
4363
4449
|
);
|
|
4364
4450
|
if (error2 || !data) {
|
|
4365
4451
|
throw new ApiClientError(
|
|
4366
|
-
error2?.message
|
|
4367
|
-
response
|
|
4452
|
+
error2?.message ?? "Failed to fetch deployment",
|
|
4453
|
+
response.status
|
|
4368
4454
|
);
|
|
4369
4455
|
}
|
|
4370
4456
|
const deployment = data.deployment;
|
|
@@ -4426,14 +4512,14 @@ async function listDeployments(options) {
|
|
|
4426
4512
|
query: {
|
|
4427
4513
|
projectId: options.projectId,
|
|
4428
4514
|
branch: options.branch,
|
|
4429
|
-
limit: options.limit?.toString()
|
|
4515
|
+
limit: options.limit?.toString() ?? "20"
|
|
4430
4516
|
}
|
|
4431
4517
|
}
|
|
4432
4518
|
});
|
|
4433
4519
|
if (error2 || !data) {
|
|
4434
4520
|
throw new ApiClientError(
|
|
4435
|
-
error2?.message
|
|
4436
|
-
response
|
|
4521
|
+
error2?.message ?? "Failed to list deployments",
|
|
4522
|
+
response.status
|
|
4437
4523
|
);
|
|
4438
4524
|
}
|
|
4439
4525
|
const deployments = data.deployments;
|
|
@@ -4452,7 +4538,7 @@ async function listDeployments(options) {
|
|
|
4452
4538
|
for (const d of deployments) {
|
|
4453
4539
|
const id = d.id.substring(0, Math.min(10, d.id.length));
|
|
4454
4540
|
const status = d.status.padEnd(10);
|
|
4455
|
-
const branchSource = d.branch
|
|
4541
|
+
const branchSource = d.branch ?? "-";
|
|
4456
4542
|
const branch = branchSource.substring(0, Math.min(18, branchSource.length)).padEnd(20);
|
|
4457
4543
|
const commit = d.commitSha ? d.commitSha.substring(0, Math.min(8, d.commitSha.length)) : "-";
|
|
4458
4544
|
const created = new Date(d.createdAt).toLocaleDateString();
|
|
@@ -4705,7 +4791,9 @@ async function exchangeCodeForToken(apiUrl, code, redirectUri) {
|
|
|
4705
4791
|
});
|
|
4706
4792
|
if (!response.ok) {
|
|
4707
4793
|
const errorBody = await response.text();
|
|
4708
|
-
throw new Error(
|
|
4794
|
+
throw new Error(
|
|
4795
|
+
`Token exchange failed: ${String(response.status)} - ${errorBody}`
|
|
4796
|
+
);
|
|
4709
4797
|
}
|
|
4710
4798
|
return await response.json();
|
|
4711
4799
|
}
|
|
@@ -4729,43 +4817,46 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4729
4817
|
},
|
|
4730
4818
|
5 * 60 * 1e3
|
|
4731
4819
|
);
|
|
4732
|
-
server = createServer(
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4820
|
+
server = createServer((req, res) => {
|
|
4821
|
+
void (async () => {
|
|
4822
|
+
const reqUrl = new URL(
|
|
4823
|
+
req.url ?? "/",
|
|
4824
|
+
`http://${CALLBACK_HOST}:${String(CALLBACK_PORT)}`
|
|
4825
|
+
);
|
|
4826
|
+
if (reqUrl.pathname !== "/callback") {
|
|
4827
|
+
res.writeHead(404);
|
|
4828
|
+
res.end("Not found");
|
|
4829
|
+
return;
|
|
4830
|
+
}
|
|
4831
|
+
const code = reqUrl.searchParams.get("code");
|
|
4832
|
+
const state = reqUrl.searchParams.get("state");
|
|
4833
|
+
const error2 = reqUrl.searchParams.get("error");
|
|
4834
|
+
const errorDescription = reqUrl.searchParams.get("error_description");
|
|
4835
|
+
if (error2) {
|
|
4836
|
+
clearTimeout(timeout);
|
|
4837
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4838
|
+
res.end(`
|
|
4750
4839
|
<!DOCTYPE html>
|
|
4751
4840
|
<html>
|
|
4752
4841
|
<head><title>Login Failed</title></head>
|
|
4753
4842
|
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
4754
4843
|
<h1>Login Failed</h1>
|
|
4755
4844
|
<p>Error: ${error2}</p>
|
|
4756
|
-
<p>${errorDescription
|
|
4845
|
+
<p>${errorDescription ?? ""}</p>
|
|
4757
4846
|
<p>You can close this window.</p>
|
|
4758
4847
|
</body>
|
|
4759
4848
|
</html>
|
|
4760
4849
|
`);
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4850
|
+
server?.close();
|
|
4851
|
+
reject(
|
|
4852
|
+
new Error(`OAuth error: ${error2} - ${errorDescription ?? ""}`)
|
|
4853
|
+
);
|
|
4854
|
+
return;
|
|
4855
|
+
}
|
|
4856
|
+
if (!code) {
|
|
4857
|
+
clearTimeout(timeout);
|
|
4858
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4859
|
+
res.end(`
|
|
4769
4860
|
<!DOCTYPE html>
|
|
4770
4861
|
<html>
|
|
4771
4862
|
<head><title>Login Failed</title></head>
|
|
@@ -4776,14 +4867,14 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4776
4867
|
</body>
|
|
4777
4868
|
</html>
|
|
4778
4869
|
`);
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4870
|
+
server?.close();
|
|
4871
|
+
reject(new Error("No authorization code received"));
|
|
4872
|
+
return;
|
|
4873
|
+
}
|
|
4874
|
+
if (state !== expectedState) {
|
|
4875
|
+
clearTimeout(timeout);
|
|
4876
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4877
|
+
res.end(`
|
|
4787
4878
|
<!DOCTYPE html>
|
|
4788
4879
|
<html>
|
|
4789
4880
|
<head><title>Login Failed</title></head>
|
|
@@ -4794,20 +4885,20 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4794
4885
|
</body>
|
|
4795
4886
|
</html>
|
|
4796
4887
|
`);
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4888
|
+
server?.close();
|
|
4889
|
+
reject(new Error("State mismatch - possible CSRF attack"));
|
|
4890
|
+
return;
|
|
4891
|
+
}
|
|
4892
|
+
try {
|
|
4893
|
+
const redirectUri = `http://${CALLBACK_HOST}:${String(CALLBACK_PORT)}/callback`;
|
|
4894
|
+
const tokenResponse = await exchangeCodeForToken(
|
|
4895
|
+
apiUrl,
|
|
4896
|
+
code,
|
|
4897
|
+
redirectUri
|
|
4898
|
+
);
|
|
4899
|
+
clearTimeout(timeout);
|
|
4900
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4901
|
+
res.end(`
|
|
4811
4902
|
<!DOCTYPE html>
|
|
4812
4903
|
<html>
|
|
4813
4904
|
<head><title>Login Successful</title></head>
|
|
@@ -4818,12 +4909,12 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4818
4909
|
</body>
|
|
4819
4910
|
</html>
|
|
4820
4911
|
`);
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4912
|
+
server?.close();
|
|
4913
|
+
resolve(tokenResponse);
|
|
4914
|
+
} catch (err) {
|
|
4915
|
+
clearTimeout(timeout);
|
|
4916
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
4917
|
+
res.end(`
|
|
4827
4918
|
<!DOCTYPE html>
|
|
4828
4919
|
<html>
|
|
4829
4920
|
<head><title>Login Failed</title></head>
|
|
@@ -4835,15 +4926,16 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4835
4926
|
</body>
|
|
4836
4927
|
</html>
|
|
4837
4928
|
`);
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4929
|
+
server?.close();
|
|
4930
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4931
|
+
}
|
|
4932
|
+
})();
|
|
4841
4933
|
});
|
|
4842
4934
|
server.on("error", (err) => {
|
|
4843
4935
|
clearTimeout(timeout);
|
|
4844
4936
|
if (err.code === "EADDRINUSE") {
|
|
4845
4937
|
const portError = new Error(
|
|
4846
|
-
`Port ${CALLBACK_PORT} is already in use. Please close any other process using this port and try again.`
|
|
4938
|
+
`Port ${String(CALLBACK_PORT)} is already in use. Please close any other process using this port and try again.`
|
|
4847
4939
|
);
|
|
4848
4940
|
reject(portError);
|
|
4849
4941
|
rejectListening(portError);
|
|
@@ -4862,9 +4954,9 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4862
4954
|
return { tokenPromise, waitForListening };
|
|
4863
4955
|
}
|
|
4864
4956
|
async function loginCommand(options = {}) {
|
|
4865
|
-
const apiUrl = options.apiUrl
|
|
4957
|
+
const apiUrl = options.apiUrl ?? await getApiUrl();
|
|
4866
4958
|
const state = generateState();
|
|
4867
|
-
const redirectUri = `http://${CALLBACK_HOST}:${CALLBACK_PORT}/callback`;
|
|
4959
|
+
const redirectUri = `http://${CALLBACK_HOST}:${String(CALLBACK_PORT)}/callback`;
|
|
4868
4960
|
console.log("Starting login flow...");
|
|
4869
4961
|
console.log("");
|
|
4870
4962
|
const authUrl = new URL(`${apiUrl}/oauth/authorize`);
|
|
@@ -5160,6 +5252,12 @@ async function updateTsConfigInclude(cwd, outputPath) {
|
|
|
5160
5252
|
function generateEnvType(config) {
|
|
5161
5253
|
const imports = [];
|
|
5162
5254
|
const properties = [];
|
|
5255
|
+
if (config.env && Object.keys(config.env).length > 0) {
|
|
5256
|
+
const varProps = Object.keys(config.env).map((key) => ` ${key}: string;`).join("\n");
|
|
5257
|
+
properties.push(` vars: {
|
|
5258
|
+
${varProps}
|
|
5259
|
+
};`);
|
|
5260
|
+
}
|
|
5163
5261
|
if (config.ai) {
|
|
5164
5262
|
properties.push(" AI_URL: string;");
|
|
5165
5263
|
properties.push(" AI_TOKEN: string;");
|
|
@@ -5235,7 +5333,7 @@ async function typesCommand(options = {}) {
|
|
|
5235
5333
|
console.error("Error: ploy.yaml not found in current directory");
|
|
5236
5334
|
process.exit(1);
|
|
5237
5335
|
}
|
|
5238
|
-
const hasBindings2 = config.ai || config.db || config.queue || config.cache || config.state || config.workflow;
|
|
5336
|
+
const hasBindings2 = config.env || config.ai || config.db || config.queue || config.cache || config.state || config.workflow;
|
|
5239
5337
|
if (!hasBindings2) {
|
|
5240
5338
|
console.log("No bindings found in ploy.yaml. Generating empty Env.");
|
|
5241
5339
|
}
|