@meetploy/cli 1.13.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard-dist/assets/main-UY1Z1kG0.js +334 -0
- package/dist/dashboard-dist/assets/main-qA3kxECS.css +1 -0
- package/dist/dashboard-dist/index.html +2 -2
- package/dist/dev.js +103 -25
- package/dist/index.js +334 -171
- package/package.json +1 -1
- package/dist/dashboard-dist/assets/main-Bj2g1WCI.js +0 -324
- package/dist/dashboard-dist/assets/main-q4PKg1PN.css +0 -1
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,
|
|
@@ -137,15 +129,12 @@ function getCompatibilityFlags(config) {
|
|
|
137
129
|
return [...new Set(allFlags)];
|
|
138
130
|
}
|
|
139
131
|
function getCompatibilityDate(config) {
|
|
140
|
-
return config.compatibility_date
|
|
132
|
+
return config.compatibility_date ?? DEFAULT_COMPATIBILITY_DATE;
|
|
141
133
|
}
|
|
142
134
|
function validateBindings(bindings, bindingType, configFile) {
|
|
143
135
|
if (bindings === void 0) {
|
|
144
136
|
return;
|
|
145
137
|
}
|
|
146
|
-
if (typeof bindings !== "object" || bindings === null) {
|
|
147
|
-
throw new Error(`'${bindingType}' in ${configFile} must be an object`);
|
|
148
|
-
}
|
|
149
138
|
for (const [bindingName, resourceName] of Object.entries(bindings)) {
|
|
150
139
|
if (!BINDING_NAME_REGEX.test(bindingName)) {
|
|
151
140
|
throw new Error(`Invalid ${bindingType} binding name '${bindingName}' in ${configFile}. Binding names must be uppercase with underscores (e.g., DB, USERS_DB)`);
|
|
@@ -197,6 +186,7 @@ function validatePloyConfig(config, configFile = "ploy.yaml", options = {}) {
|
|
|
197
186
|
validateBindings(config.db, "db", configFile);
|
|
198
187
|
validateBindings(config.queue, "queue", configFile);
|
|
199
188
|
validateBindings(config.cache, "cache", configFile);
|
|
189
|
+
validateBindings(config.state, "state", configFile);
|
|
200
190
|
validateBindings(config.workflow, "workflow", configFile);
|
|
201
191
|
if (config.ai !== void 0 && typeof config.ai !== "boolean") {
|
|
202
192
|
throw new Error(`'ai' in ${configFile} must be a boolean`);
|
|
@@ -223,9 +213,6 @@ function validatePloyConfig(config, configFile = "ploy.yaml", options = {}) {
|
|
|
223
213
|
}
|
|
224
214
|
}
|
|
225
215
|
if (config.auth !== void 0) {
|
|
226
|
-
if (typeof config.auth !== "object" || config.auth === null) {
|
|
227
|
-
throw new Error(`'auth' in ${configFile} must be an object`);
|
|
228
|
-
}
|
|
229
216
|
if (!config.auth.binding) {
|
|
230
217
|
throw new Error(`'auth.binding' is required in ${configFile} when auth is configured`);
|
|
231
218
|
}
|
|
@@ -239,7 +226,7 @@ function validatePloyConfig(config, configFile = "ploy.yaml", options = {}) {
|
|
|
239
226
|
return validatedConfig;
|
|
240
227
|
}
|
|
241
228
|
async function readPloyConfig(projectDir, configPath) {
|
|
242
|
-
const configFile = configPath
|
|
229
|
+
const configFile = configPath ?? "ploy.yaml";
|
|
243
230
|
const fullPath = join(projectDir, configFile);
|
|
244
231
|
try {
|
|
245
232
|
const content = await readFileAsync(fullPath, "utf-8");
|
|
@@ -254,7 +241,7 @@ async function readPloyConfig(projectDir, configPath) {
|
|
|
254
241
|
}
|
|
255
242
|
}
|
|
256
243
|
function readPloyConfigSync(projectDir, configPath) {
|
|
257
|
-
const configFile = configPath
|
|
244
|
+
const configFile = configPath ?? "ploy.yaml";
|
|
258
245
|
const fullPath = join(projectDir, configFile);
|
|
259
246
|
if (!existsSync(fullPath)) {
|
|
260
247
|
throw new Error(`Config file not found: ${fullPath}`);
|
|
@@ -263,7 +250,7 @@ function readPloyConfigSync(projectDir, configPath) {
|
|
|
263
250
|
return parse(content);
|
|
264
251
|
}
|
|
265
252
|
async function readAndValidatePloyConfig(projectDir, configPath, validationOptions) {
|
|
266
|
-
const configFile = configPath
|
|
253
|
+
const configFile = configPath ?? "ploy.yaml";
|
|
267
254
|
const config = await readPloyConfig(projectDir, configPath);
|
|
268
255
|
if (!config) {
|
|
269
256
|
return null;
|
|
@@ -271,12 +258,12 @@ async function readAndValidatePloyConfig(projectDir, configPath, validationOptio
|
|
|
271
258
|
return validatePloyConfig(config, configFile, validationOptions);
|
|
272
259
|
}
|
|
273
260
|
function readAndValidatePloyConfigSync(projectDir, configPath, validationOptions) {
|
|
274
|
-
const configFile = configPath
|
|
261
|
+
const configFile = configPath ?? "ploy.yaml";
|
|
275
262
|
const config = readPloyConfigSync(projectDir, configPath);
|
|
276
263
|
return validatePloyConfig(config, configFile, validationOptions);
|
|
277
264
|
}
|
|
278
265
|
function hasBindings(config) {
|
|
279
|
-
return !!(config.db
|
|
266
|
+
return !!(config.db ?? config.queue ?? config.cache ?? config.state ?? config.workflow ?? config.ai ?? config.auth);
|
|
280
267
|
}
|
|
281
268
|
function getWorkerEntryPoint(projectDir, config) {
|
|
282
269
|
if (config.main) {
|
|
@@ -777,6 +764,66 @@ export function initializeQueue<T = unknown>(queueName: string, serviceUrl: stri
|
|
|
777
764
|
}
|
|
778
765
|
});
|
|
779
766
|
|
|
767
|
+
// ../emulator/dist/runtime/state-runtime.js
|
|
768
|
+
var STATE_RUNTIME_CODE;
|
|
769
|
+
var init_state_runtime = __esm({
|
|
770
|
+
"../emulator/dist/runtime/state-runtime.js"() {
|
|
771
|
+
STATE_RUNTIME_CODE = `
|
|
772
|
+
interface StateBinding {
|
|
773
|
+
get: (key: string) => Promise<string | null>;
|
|
774
|
+
set: (key: string, value: string) => Promise<void>;
|
|
775
|
+
delete: (key: string) => Promise<void>;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
export function initializeState(stateName: string, serviceUrl: string): StateBinding {
|
|
779
|
+
return {
|
|
780
|
+
async get(key: string): Promise<string | null> {
|
|
781
|
+
const response = await fetch(serviceUrl + "/state/get", {
|
|
782
|
+
method: "POST",
|
|
783
|
+
headers: { "Content-Type": "application/json" },
|
|
784
|
+
body: JSON.stringify({ stateName, key }),
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
if (!response.ok) {
|
|
788
|
+
const errorText = await response.text();
|
|
789
|
+
throw new Error("State get failed: " + errorText);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const result = await response.json();
|
|
793
|
+
return result.value ?? null;
|
|
794
|
+
},
|
|
795
|
+
|
|
796
|
+
async set(key: string, value: string): Promise<void> {
|
|
797
|
+
const response = await fetch(serviceUrl + "/state/set", {
|
|
798
|
+
method: "POST",
|
|
799
|
+
headers: { "Content-Type": "application/json" },
|
|
800
|
+
body: JSON.stringify({ stateName, key, value }),
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
if (!response.ok) {
|
|
804
|
+
const errorText = await response.text();
|
|
805
|
+
throw new Error("State set failed: " + errorText);
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
|
|
809
|
+
async delete(key: string): Promise<void> {
|
|
810
|
+
const response = await fetch(serviceUrl + "/state/delete", {
|
|
811
|
+
method: "POST",
|
|
812
|
+
headers: { "Content-Type": "application/json" },
|
|
813
|
+
body: JSON.stringify({ stateName, key }),
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
if (!response.ok) {
|
|
817
|
+
const errorText = await response.text();
|
|
818
|
+
throw new Error("State delete failed: " + errorText);
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
`;
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
780
827
|
// ../emulator/dist/runtime/workflow-runtime.js
|
|
781
828
|
var WORKFLOW_RUNTIME_CODE;
|
|
782
829
|
var init_workflow_runtime = __esm({
|
|
@@ -1009,6 +1056,12 @@ function generateWrapperCode(config, mockServiceUrl) {
|
|
|
1009
1056
|
bindings.push(` ${bindingName}: initializeCache("${cacheName}", "${mockServiceUrl}"),`);
|
|
1010
1057
|
}
|
|
1011
1058
|
}
|
|
1059
|
+
if (config.state) {
|
|
1060
|
+
imports.push('import { initializeState } from "__ploy_state_runtime__";');
|
|
1061
|
+
for (const [bindingName, stateName] of Object.entries(config.state)) {
|
|
1062
|
+
bindings.push(` ${bindingName}: initializeState("${stateName}", "${mockServiceUrl}"),`);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1012
1065
|
if (config.workflow) {
|
|
1013
1066
|
imports.push('import { initializeWorkflow, createStepContext, executeWorkflow } from "__ploy_workflow_runtime__";');
|
|
1014
1067
|
for (const [bindingName, workflowName] of Object.entries(config.workflow)) {
|
|
@@ -1116,6 +1169,10 @@ function createRuntimePlugin(_config) {
|
|
|
1116
1169
|
path: "__ploy_cache_runtime__",
|
|
1117
1170
|
namespace: "ploy-runtime"
|
|
1118
1171
|
}));
|
|
1172
|
+
build2.onResolve({ filter: /^__ploy_state_runtime__$/ }, () => ({
|
|
1173
|
+
path: "__ploy_state_runtime__",
|
|
1174
|
+
namespace: "ploy-runtime"
|
|
1175
|
+
}));
|
|
1119
1176
|
build2.onResolve({ filter: /^__ploy_workflow_runtime__$/ }, () => ({
|
|
1120
1177
|
path: "__ploy_workflow_runtime__",
|
|
1121
1178
|
namespace: "ploy-runtime"
|
|
@@ -1132,6 +1189,10 @@ function createRuntimePlugin(_config) {
|
|
|
1132
1189
|
contents: CACHE_RUNTIME_CODE,
|
|
1133
1190
|
loader: "ts"
|
|
1134
1191
|
}));
|
|
1192
|
+
build2.onLoad({ filter: /^__ploy_state_runtime__$/, namespace: "ploy-runtime" }, () => ({
|
|
1193
|
+
contents: STATE_RUNTIME_CODE,
|
|
1194
|
+
loader: "ts"
|
|
1195
|
+
}));
|
|
1135
1196
|
build2.onLoad({ filter: /^__ploy_workflow_runtime__$/, namespace: "ploy-runtime" }, () => ({
|
|
1136
1197
|
contents: WORKFLOW_RUNTIME_CODE,
|
|
1137
1198
|
loader: "ts"
|
|
@@ -1175,6 +1236,7 @@ var init_bundler = __esm({
|
|
|
1175
1236
|
init_cache_runtime();
|
|
1176
1237
|
init_db_runtime();
|
|
1177
1238
|
init_queue_runtime();
|
|
1239
|
+
init_state_runtime();
|
|
1178
1240
|
init_workflow_runtime();
|
|
1179
1241
|
NODE_BUILTINS = [
|
|
1180
1242
|
"assert",
|
|
@@ -1282,16 +1344,15 @@ function createFileWatcher(srcDir, onRebuild) {
|
|
|
1282
1344
|
if (debounceTimer) {
|
|
1283
1345
|
clearTimeout(debounceTimer);
|
|
1284
1346
|
}
|
|
1285
|
-
debounceTimer = setTimeout(
|
|
1347
|
+
debounceTimer = setTimeout(() => {
|
|
1286
1348
|
if (isRebuilding) {
|
|
1287
1349
|
return;
|
|
1288
1350
|
}
|
|
1289
1351
|
isRebuilding = true;
|
|
1290
|
-
|
|
1291
|
-
await onRebuild();
|
|
1292
|
-
} finally {
|
|
1352
|
+
onRebuild().finally(() => {
|
|
1293
1353
|
isRebuilding = false;
|
|
1294
|
-
}
|
|
1354
|
+
}).catch(() => {
|
|
1355
|
+
});
|
|
1295
1356
|
}, 100);
|
|
1296
1357
|
}
|
|
1297
1358
|
return {
|
|
@@ -1332,7 +1393,7 @@ function createFileWatcher(srcDir, onRebuild) {
|
|
|
1332
1393
|
debounceTimer = null;
|
|
1333
1394
|
}
|
|
1334
1395
|
if (watcher) {
|
|
1335
|
-
watcher.close();
|
|
1396
|
+
void watcher.close();
|
|
1336
1397
|
watcher = null;
|
|
1337
1398
|
}
|
|
1338
1399
|
}
|
|
@@ -1355,10 +1416,10 @@ var init_watcher = __esm({
|
|
|
1355
1416
|
function readPloyConfig2(projectDir, configPath) {
|
|
1356
1417
|
const config = readPloyConfigSync(projectDir, configPath);
|
|
1357
1418
|
if (!config.kind) {
|
|
1358
|
-
throw new Error(`Missing required field 'kind' in ${configPath
|
|
1419
|
+
throw new Error(`Missing required field 'kind' in ${configPath ?? "ploy.yaml"}`);
|
|
1359
1420
|
}
|
|
1360
1421
|
if (config.kind !== "dynamic" && config.kind !== "worker") {
|
|
1361
|
-
throw new Error(`Invalid kind '${config.kind}' in ${configPath
|
|
1422
|
+
throw new Error(`Invalid kind '${config.kind}' in ${configPath ?? "ploy.yaml"}. Must be 'dynamic' or 'worker'`);
|
|
1362
1423
|
}
|
|
1363
1424
|
return config;
|
|
1364
1425
|
}
|
|
@@ -1374,7 +1435,7 @@ function generateWorkerdConfig(options) {
|
|
|
1374
1435
|
const { port, mockServicePort } = options;
|
|
1375
1436
|
const services = [
|
|
1376
1437
|
'(name = "main", worker = .worker)',
|
|
1377
|
-
`(name = "mock", external = (address = "localhost:${mockServicePort}", http = ()))`,
|
|
1438
|
+
`(name = "mock", external = (address = "localhost:${String(mockServicePort)}", http = ()))`,
|
|
1378
1439
|
'(name = "internet", network = (allow = ["public", "private", "local"], tlsOptions = (trustBrowserCas = true)))'
|
|
1379
1440
|
];
|
|
1380
1441
|
const bindings = [
|
|
@@ -1388,7 +1449,7 @@ const config :Workerd.Config = (
|
|
|
1388
1449
|
${services.join(",\n ")}
|
|
1389
1450
|
],
|
|
1390
1451
|
sockets = [
|
|
1391
|
-
(name = "http", address = "*:${port}", http = (), service = "main")
|
|
1452
|
+
(name = "http", address = "*:${String(port)}", http = (), service = "main")
|
|
1392
1453
|
]
|
|
1393
1454
|
);
|
|
1394
1455
|
|
|
@@ -1594,7 +1655,7 @@ function createAuthHandlers(db) {
|
|
|
1594
1655
|
return c.json({ error: message }, 500);
|
|
1595
1656
|
}
|
|
1596
1657
|
};
|
|
1597
|
-
const meHandler =
|
|
1658
|
+
const meHandler = (c) => {
|
|
1598
1659
|
try {
|
|
1599
1660
|
const cookieToken = getCookie(c, "ploy_session");
|
|
1600
1661
|
const authHeader = c.req.header("Authorization");
|
|
@@ -1638,7 +1699,7 @@ function createAuthHandlers(db) {
|
|
|
1638
1699
|
return c.json({ error: message }, 500);
|
|
1639
1700
|
}
|
|
1640
1701
|
};
|
|
1641
|
-
const signoutHandler =
|
|
1702
|
+
const signoutHandler = (c) => {
|
|
1642
1703
|
try {
|
|
1643
1704
|
const sessionToken = getCookie(c, "ploy_session");
|
|
1644
1705
|
if (sessionToken) {
|
|
@@ -1731,6 +1792,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1731
1792
|
db: config.db,
|
|
1732
1793
|
queue: config.queue,
|
|
1733
1794
|
cache: config.cache,
|
|
1795
|
+
state: config.state,
|
|
1734
1796
|
workflow: config.workflow,
|
|
1735
1797
|
auth: config.auth
|
|
1736
1798
|
});
|
|
@@ -1752,8 +1814,8 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1752
1814
|
if (tableName !== "auth_users" && tableName !== "auth_sessions") {
|
|
1753
1815
|
return c.json({ error: "Table not found" }, 404);
|
|
1754
1816
|
}
|
|
1755
|
-
const limit = parseInt(c.req.query("limit")
|
|
1756
|
-
const offset = parseInt(c.req.query("offset")
|
|
1817
|
+
const limit = parseInt(c.req.query("limit") ?? "50", 10);
|
|
1818
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
1757
1819
|
try {
|
|
1758
1820
|
const db = dbManager2.emulatorDb;
|
|
1759
1821
|
const columnsResult = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
@@ -1965,8 +2027,8 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1965
2027
|
return c.json({ error: `Database binding '${binding}' not found` }, 404);
|
|
1966
2028
|
}
|
|
1967
2029
|
const tableName = c.req.param("tableName");
|
|
1968
|
-
const limit = parseInt(c.req.query("limit")
|
|
1969
|
-
const offset = parseInt(c.req.query("offset")
|
|
2030
|
+
const limit = parseInt(c.req.query("limit") ?? "50", 10);
|
|
2031
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
1970
2032
|
try {
|
|
1971
2033
|
const db = dbManager2.getD1Database(resourceName);
|
|
1972
2034
|
const columnsResult = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
@@ -2047,7 +2109,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2047
2109
|
app.get("/api/queue/:binding/messages", (c) => {
|
|
2048
2110
|
const binding = c.req.param("binding");
|
|
2049
2111
|
const queueName = config.queue?.[binding];
|
|
2050
|
-
const limit = parseInt(c.req.query("limit")
|
|
2112
|
+
const limit = parseInt(c.req.query("limit") ?? "10", 10);
|
|
2051
2113
|
if (!queueName) {
|
|
2052
2114
|
return c.json({ error: "Queue not found" }, 404);
|
|
2053
2115
|
}
|
|
@@ -2074,8 +2136,8 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2074
2136
|
app.get("/api/cache/:binding/entries", (c) => {
|
|
2075
2137
|
const binding = c.req.param("binding");
|
|
2076
2138
|
const cacheName = config.cache?.[binding];
|
|
2077
|
-
const limit = parseInt(c.req.query("limit")
|
|
2078
|
-
const offset = parseInt(c.req.query("offset")
|
|
2139
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
2140
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
2079
2141
|
const now = Math.floor(Date.now() / 1e3);
|
|
2080
2142
|
if (!cacheName) {
|
|
2081
2143
|
return c.json({ error: "Cache binding not found" }, 404);
|
|
@@ -2104,10 +2166,42 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2104
2166
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
2105
2167
|
}
|
|
2106
2168
|
});
|
|
2169
|
+
app.get("/api/state/:binding/entries", (c) => {
|
|
2170
|
+
const binding = c.req.param("binding");
|
|
2171
|
+
const stateName = config.state?.[binding];
|
|
2172
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
2173
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
2174
|
+
const search = c.req.query("search") ?? "";
|
|
2175
|
+
if (!stateName) {
|
|
2176
|
+
return c.json({ error: "State binding not found" }, 404);
|
|
2177
|
+
}
|
|
2178
|
+
try {
|
|
2179
|
+
const db = dbManager2.emulatorDb;
|
|
2180
|
+
const fetchLimit = limit + 1;
|
|
2181
|
+
const entries = search ? db.prepare(`SELECT key, value
|
|
2182
|
+
FROM state_entries
|
|
2183
|
+
WHERE state_name = ? AND key LIKE ?
|
|
2184
|
+
ORDER BY key ASC
|
|
2185
|
+
LIMIT ? OFFSET ?`).all(stateName, `%${search}%`, fetchLimit, offset) : db.prepare(`SELECT key, value
|
|
2186
|
+
FROM state_entries
|
|
2187
|
+
WHERE state_name = ?
|
|
2188
|
+
ORDER BY key ASC
|
|
2189
|
+
LIMIT ? OFFSET ?`).all(stateName, fetchLimit, offset);
|
|
2190
|
+
const hasMore = entries.length > limit;
|
|
2191
|
+
return c.json({
|
|
2192
|
+
entries: entries.slice(0, limit),
|
|
2193
|
+
hasMore,
|
|
2194
|
+
limit,
|
|
2195
|
+
offset
|
|
2196
|
+
});
|
|
2197
|
+
} catch (err) {
|
|
2198
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
2199
|
+
}
|
|
2200
|
+
});
|
|
2107
2201
|
app.get("/api/workflow/:binding/executions", (c) => {
|
|
2108
2202
|
const binding = c.req.param("binding");
|
|
2109
2203
|
const workflowConfig = config.workflow?.[binding];
|
|
2110
|
-
const limit = parseInt(c.req.query("limit")
|
|
2204
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
2111
2205
|
if (!workflowConfig) {
|
|
2112
2206
|
return c.json({ error: "Workflow not found" }, 404);
|
|
2113
2207
|
}
|
|
@@ -2370,7 +2464,7 @@ function createQueueHandlers(db) {
|
|
|
2370
2464
|
const transaction = db.transaction(() => {
|
|
2371
2465
|
for (const msg of messages) {
|
|
2372
2466
|
const id = randomUUID();
|
|
2373
|
-
const visibleAt = now + (msg.delaySeconds
|
|
2467
|
+
const visibleAt = now + (msg.delaySeconds ?? 0);
|
|
2374
2468
|
insert.run(id, queueName, JSON.stringify(msg.payload), visibleAt);
|
|
2375
2469
|
messageIds.push(id);
|
|
2376
2470
|
}
|
|
@@ -2531,6 +2625,37 @@ var init_queue_service = __esm({
|
|
|
2531
2625
|
"../emulator/dist/services/queue-service.js"() {
|
|
2532
2626
|
}
|
|
2533
2627
|
});
|
|
2628
|
+
|
|
2629
|
+
// ../emulator/dist/services/state-service.js
|
|
2630
|
+
function createStateHandlers(db) {
|
|
2631
|
+
const getHandler = async (c) => {
|
|
2632
|
+
const body = await c.req.json();
|
|
2633
|
+
const { stateName, key } = body;
|
|
2634
|
+
const row = db.prepare(`SELECT value FROM state_entries WHERE state_name = ? AND key = ?`).get(stateName, key);
|
|
2635
|
+
return c.json({ value: row?.value ?? null });
|
|
2636
|
+
};
|
|
2637
|
+
const setHandler = async (c) => {
|
|
2638
|
+
const body = await c.req.json();
|
|
2639
|
+
const { stateName, key, value } = body;
|
|
2640
|
+
db.prepare(`INSERT OR REPLACE INTO state_entries (state_name, key, value) VALUES (?, ?, ?)`).run(stateName, key, value);
|
|
2641
|
+
return c.json({ success: true });
|
|
2642
|
+
};
|
|
2643
|
+
const deleteHandler = async (c) => {
|
|
2644
|
+
const body = await c.req.json();
|
|
2645
|
+
const { stateName, key } = body;
|
|
2646
|
+
db.prepare(`DELETE FROM state_entries WHERE state_name = ? AND key = ?`).run(stateName, key);
|
|
2647
|
+
return c.json({ success: true });
|
|
2648
|
+
};
|
|
2649
|
+
return {
|
|
2650
|
+
getHandler,
|
|
2651
|
+
setHandler,
|
|
2652
|
+
deleteHandler
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
var init_state_service = __esm({
|
|
2656
|
+
"../emulator/dist/services/state-service.js"() {
|
|
2657
|
+
}
|
|
2658
|
+
});
|
|
2534
2659
|
function createWorkflowHandlers(db, workerUrl) {
|
|
2535
2660
|
const triggerHandler = async (c) => {
|
|
2536
2661
|
try {
|
|
@@ -2739,6 +2864,12 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
2739
2864
|
app.post("/cache/set", cacheHandlers.setHandler);
|
|
2740
2865
|
app.post("/cache/delete", cacheHandlers.deleteHandler);
|
|
2741
2866
|
}
|
|
2867
|
+
if (config.state) {
|
|
2868
|
+
const stateHandlers = createStateHandlers(dbManager2.emulatorDb);
|
|
2869
|
+
app.post("/state/get", stateHandlers.getHandler);
|
|
2870
|
+
app.post("/state/set", stateHandlers.setHandler);
|
|
2871
|
+
app.post("/state/delete", stateHandlers.deleteHandler);
|
|
2872
|
+
}
|
|
2742
2873
|
if (config.auth) {
|
|
2743
2874
|
const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
|
|
2744
2875
|
app.post("/auth/signup", authHandlers.signupHandler);
|
|
@@ -2759,7 +2890,9 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
2759
2890
|
resolve({
|
|
2760
2891
|
port: info.port,
|
|
2761
2892
|
close: () => new Promise((res) => {
|
|
2762
|
-
server.close(() =>
|
|
2893
|
+
server.close(() => {
|
|
2894
|
+
res();
|
|
2895
|
+
});
|
|
2763
2896
|
})
|
|
2764
2897
|
});
|
|
2765
2898
|
});
|
|
@@ -2773,6 +2906,7 @@ var init_mock_server = __esm({
|
|
|
2773
2906
|
init_dashboard_routes();
|
|
2774
2907
|
init_db_service();
|
|
2775
2908
|
init_queue_service();
|
|
2909
|
+
init_state_service();
|
|
2776
2910
|
init_workflow_service();
|
|
2777
2911
|
DEFAULT_MOCK_SERVER_PORT = 4003;
|
|
2778
2912
|
}
|
|
@@ -2938,6 +3072,14 @@ CREATE TABLE IF NOT EXISTS cache_entries (
|
|
|
2938
3072
|
expires_at INTEGER NOT NULL,
|
|
2939
3073
|
PRIMARY KEY (cache_name, key)
|
|
2940
3074
|
);
|
|
3075
|
+
|
|
3076
|
+
-- State entries table (durable key-value store, no expiry)
|
|
3077
|
+
CREATE TABLE IF NOT EXISTS state_entries (
|
|
3078
|
+
state_name TEXT NOT NULL,
|
|
3079
|
+
key TEXT NOT NULL,
|
|
3080
|
+
value TEXT NOT NULL,
|
|
3081
|
+
PRIMARY KEY (state_name, key)
|
|
3082
|
+
);
|
|
2941
3083
|
`;
|
|
2942
3084
|
}
|
|
2943
3085
|
});
|
|
@@ -2990,15 +3132,15 @@ var init_emulator = __esm({
|
|
|
2990
3132
|
debug(`Temp dir: ${this.tempDir}`, this.options.verbose);
|
|
2991
3133
|
this.dbManager = initializeDatabases(this.projectDir);
|
|
2992
3134
|
debug("Initialized databases", this.options.verbose);
|
|
2993
|
-
const workerUrl = `http://${this.options.host}:${this.options.port}`;
|
|
3135
|
+
const workerUrl = `http://${this.options.host}:${String(this.options.port)}`;
|
|
2994
3136
|
const dashboardPort = this.options.dashboardPort;
|
|
2995
3137
|
this.mockServer = await startMockServer(this.dbManager, this.config, {
|
|
2996
3138
|
workerUrl,
|
|
2997
3139
|
port: dashboardPort,
|
|
2998
3140
|
dashboardEnabled: true
|
|
2999
3141
|
});
|
|
3000
|
-
debug(`Mock server started on port ${this.mockServer.port}`, this.options.verbose);
|
|
3001
|
-
const mockServiceUrl = `http://localhost:${this.mockServer.port}`;
|
|
3142
|
+
debug(`Mock server started on port ${String(this.mockServer.port)}`, this.options.verbose);
|
|
3143
|
+
const mockServiceUrl = `http://localhost:${String(this.mockServer.port)}`;
|
|
3002
3144
|
const entryPoint = getWorkerEntryPoint2(this.projectDir, this.config);
|
|
3003
3145
|
debug(`Entry point: ${entryPoint}`, this.options.verbose);
|
|
3004
3146
|
await this.bundle(entryPoint, mockServiceUrl);
|
|
@@ -3025,13 +3167,13 @@ var init_emulator = __esm({
|
|
|
3025
3167
|
debug(`Watching ${srcDir} for changes`, this.options.verbose);
|
|
3026
3168
|
}
|
|
3027
3169
|
if (this.config.queue) {
|
|
3028
|
-
const workerUrl2 = `http://${this.options.host}:${this.options.port}`;
|
|
3170
|
+
const workerUrl2 = `http://${this.options.host}:${String(this.options.port)}`;
|
|
3029
3171
|
this.queueProcessor = createQueueProcessor(this.dbManager.emulatorDb, this.config.queue, workerUrl2);
|
|
3030
3172
|
this.queueProcessor.start();
|
|
3031
3173
|
debug("Queue processor started", this.options.verbose);
|
|
3032
3174
|
}
|
|
3033
|
-
success(`Emulator running at http://${this.options.host}:${this.options.port}`);
|
|
3034
|
-
log(` Dashboard: http://${this.options.host}:${this.mockServer.port}`);
|
|
3175
|
+
success(`Emulator running at http://${this.options.host}:${String(this.options.port)}`);
|
|
3176
|
+
log(` Dashboard: http://${this.options.host}:${String(this.mockServer.port)}`);
|
|
3035
3177
|
if (this.config.db) {
|
|
3036
3178
|
log(` DB bindings: ${Object.keys(this.config.db).join(", ")}`);
|
|
3037
3179
|
}
|
|
@@ -3071,7 +3213,7 @@ var init_emulator = __esm({
|
|
|
3071
3213
|
const workerdBin = this.findWorkerdBinary();
|
|
3072
3214
|
log(`[ploy] Using workerd binary: ${workerdBin}`);
|
|
3073
3215
|
debug(`Starting workerd: ${workerdBin} ${args2.join(" ")}`, this.options.verbose);
|
|
3074
|
-
|
|
3216
|
+
await new Promise((resolve, reject) => {
|
|
3075
3217
|
const workerdBinDir = dirname(workerdBin);
|
|
3076
3218
|
this.workerdProcess = spawn(workerdBin, args2, {
|
|
3077
3219
|
cwd: this.tempDir,
|
|
@@ -3079,7 +3221,7 @@ var init_emulator = __esm({
|
|
|
3079
3221
|
shell: true,
|
|
3080
3222
|
env: {
|
|
3081
3223
|
...process.env,
|
|
3082
|
-
PATH: `${workerdBinDir}:${process.env.PATH
|
|
3224
|
+
PATH: `${workerdBinDir}:${process.env.PATH ?? ""}`
|
|
3083
3225
|
}
|
|
3084
3226
|
});
|
|
3085
3227
|
let started = false;
|
|
@@ -3091,7 +3233,7 @@ var init_emulator = __esm({
|
|
|
3091
3233
|
}
|
|
3092
3234
|
if (!started && (output.includes("Listening") || output.includes("running"))) {
|
|
3093
3235
|
started = true;
|
|
3094
|
-
resolve();
|
|
3236
|
+
resolve(void 0);
|
|
3095
3237
|
}
|
|
3096
3238
|
});
|
|
3097
3239
|
this.workerdProcess.stderr?.on("data", (data) => {
|
|
@@ -3105,7 +3247,7 @@ var init_emulator = __esm({
|
|
|
3105
3247
|
}
|
|
3106
3248
|
if (!started && output.includes("Listening")) {
|
|
3107
3249
|
started = true;
|
|
3108
|
-
resolve();
|
|
3250
|
+
resolve(void 0);
|
|
3109
3251
|
}
|
|
3110
3252
|
});
|
|
3111
3253
|
this.workerdProcess.on("error", (err) => {
|
|
@@ -3116,19 +3258,19 @@ var init_emulator = __esm({
|
|
|
3116
3258
|
});
|
|
3117
3259
|
this.workerdProcess.on("exit", (code) => {
|
|
3118
3260
|
if (code !== 0 && code !== null) {
|
|
3119
|
-
error(`workerd exited with code ${code}`);
|
|
3261
|
+
error(`workerd exited with code ${String(code)}`);
|
|
3120
3262
|
if (stderrOutput) {
|
|
3121
3263
|
error(`workerd stderr: ${stderrOutput.trim()}`);
|
|
3122
3264
|
}
|
|
3123
3265
|
}
|
|
3124
3266
|
if (!started) {
|
|
3125
|
-
reject(new Error(`workerd exited with code ${code}`));
|
|
3267
|
+
reject(new Error(`workerd exited with code ${String(code)}`));
|
|
3126
3268
|
}
|
|
3127
3269
|
});
|
|
3128
3270
|
setTimeout(() => {
|
|
3129
3271
|
if (!started) {
|
|
3130
3272
|
started = true;
|
|
3131
|
-
resolve();
|
|
3273
|
+
resolve(void 0);
|
|
3132
3274
|
}
|
|
3133
3275
|
}, 2e3);
|
|
3134
3276
|
});
|
|
@@ -3159,10 +3301,11 @@ var init_emulator = __esm({
|
|
|
3159
3301
|
return this.projectDir;
|
|
3160
3302
|
}
|
|
3161
3303
|
setupSignalHandlers() {
|
|
3162
|
-
const handler =
|
|
3304
|
+
const handler = () => {
|
|
3163
3305
|
log("\nShutting down...");
|
|
3164
|
-
|
|
3165
|
-
|
|
3306
|
+
void this.stop().then(() => {
|
|
3307
|
+
process.exit(0);
|
|
3308
|
+
});
|
|
3166
3309
|
};
|
|
3167
3310
|
process.on("SIGINT", handler);
|
|
3168
3311
|
process.on("SIGTERM", handler);
|
|
@@ -3277,7 +3420,7 @@ function createDevD1(databaseId, apiUrl) {
|
|
|
3277
3420
|
async batch(statements) {
|
|
3278
3421
|
const stmts = statements.map((s) => {
|
|
3279
3422
|
const data = s.__db_data;
|
|
3280
|
-
return data
|
|
3423
|
+
return data ?? s;
|
|
3281
3424
|
});
|
|
3282
3425
|
const response = await fetch(`${apiUrl}/db`, {
|
|
3283
3426
|
method: "POST",
|
|
@@ -3337,7 +3480,7 @@ async function initPloyForDev(config) {
|
|
|
3337
3480
|
globalThis.__PLOY_DEV_INITIALIZED__ = true;
|
|
3338
3481
|
const cliMockServerUrl = process.env.PLOY_MOCK_SERVER_URL;
|
|
3339
3482
|
if (cliMockServerUrl) {
|
|
3340
|
-
const configPath2 = config?.configPath
|
|
3483
|
+
const configPath2 = config?.configPath ?? "./ploy.yaml";
|
|
3341
3484
|
const projectDir2 = process.cwd();
|
|
3342
3485
|
let ployConfig2;
|
|
3343
3486
|
try {
|
|
@@ -3385,7 +3528,7 @@ async function initPloyForDev(config) {
|
|
|
3385
3528
|
console.log(`[Ploy] Using CLI mock server at ${cliMockServerUrl} (${features2.join(", ")})`);
|
|
3386
3529
|
return;
|
|
3387
3530
|
}
|
|
3388
|
-
const configPath = config?.configPath
|
|
3531
|
+
const configPath = config?.configPath ?? "./ploy.yaml";
|
|
3389
3532
|
const projectDir = process.cwd();
|
|
3390
3533
|
let ployConfig;
|
|
3391
3534
|
try {
|
|
@@ -3408,7 +3551,7 @@ async function initPloyForDev(config) {
|
|
|
3408
3551
|
ensureDataDir(projectDir);
|
|
3409
3552
|
dbManager = initializeDatabases(projectDir);
|
|
3410
3553
|
mockServer = await startMockServer(dbManager, ployConfig, {});
|
|
3411
|
-
const apiUrl = `http://localhost:${mockServer.port}`;
|
|
3554
|
+
const apiUrl = `http://localhost:${String(mockServer.port)}`;
|
|
3412
3555
|
const env = {};
|
|
3413
3556
|
if (hasDbBindings && ployConfig.db) {
|
|
3414
3557
|
for (const [bindingName, databaseId] of Object.entries(ployConfig.db)) {
|
|
@@ -3456,13 +3599,15 @@ async function initPloyForDev(config) {
|
|
|
3456
3599
|
dbManager.close();
|
|
3457
3600
|
}
|
|
3458
3601
|
});
|
|
3459
|
-
process.on("SIGINT",
|
|
3460
|
-
|
|
3461
|
-
|
|
3602
|
+
process.on("SIGINT", () => {
|
|
3603
|
+
void cleanup().then(() => {
|
|
3604
|
+
process.exit(0);
|
|
3605
|
+
});
|
|
3462
3606
|
});
|
|
3463
|
-
process.on("SIGTERM",
|
|
3464
|
-
|
|
3465
|
-
|
|
3607
|
+
process.on("SIGTERM", () => {
|
|
3608
|
+
void cleanup().then(() => {
|
|
3609
|
+
process.exit(0);
|
|
3610
|
+
});
|
|
3466
3611
|
});
|
|
3467
3612
|
}
|
|
3468
3613
|
var PLOY_CONTEXT_SYMBOL, mockServer, dbManager;
|
|
@@ -3499,7 +3644,7 @@ async function startDevDashboard(options = {}) {
|
|
|
3499
3644
|
port,
|
|
3500
3645
|
dashboardEnabled: true
|
|
3501
3646
|
});
|
|
3502
|
-
debug(`Mock server started on port ${mockServer2.port}`, verbose);
|
|
3647
|
+
debug(`Mock server started on port ${String(mockServer2.port)}`, verbose);
|
|
3503
3648
|
if (config.db) {
|
|
3504
3649
|
log(` DB bindings: ${Object.keys(config.db).join(", ")}`);
|
|
3505
3650
|
}
|
|
@@ -3794,8 +3939,8 @@ async function listTables(options) {
|
|
|
3794
3939
|
);
|
|
3795
3940
|
if (error2 || !data) {
|
|
3796
3941
|
throw new ApiClientError(
|
|
3797
|
-
error2?.message
|
|
3798
|
-
response
|
|
3942
|
+
error2?.message ?? "Failed to fetch tables",
|
|
3943
|
+
response.status
|
|
3799
3944
|
);
|
|
3800
3945
|
}
|
|
3801
3946
|
const tablesResult = data;
|
|
@@ -3816,13 +3961,13 @@ async function listTables(options) {
|
|
|
3816
3961
|
});
|
|
3817
3962
|
if (schemaError || !schemaData) {
|
|
3818
3963
|
throw new ApiClientError(
|
|
3819
|
-
schemaError?.message
|
|
3820
|
-
schemaResponse
|
|
3964
|
+
schemaError?.message ?? "Failed to fetch schema",
|
|
3965
|
+
schemaResponse.status
|
|
3821
3966
|
);
|
|
3822
3967
|
}
|
|
3823
3968
|
const schemaResult = schemaData;
|
|
3824
3969
|
for (const table of schemaResult.tables) {
|
|
3825
|
-
console.log(`Table: ${table.name}`);
|
|
3970
|
+
console.log(`Table: ${String(table.name)}`);
|
|
3826
3971
|
console.log("-".repeat(40));
|
|
3827
3972
|
console.log(
|
|
3828
3973
|
" " + "COLUMN".padEnd(20) + "TYPE".padEnd(15) + "NULLABLE".padEnd(10) + "PK"
|
|
@@ -3832,7 +3977,7 @@ async function listTables(options) {
|
|
|
3832
3977
|
const nullable = col.notNull ? "NO" : "YES";
|
|
3833
3978
|
const pk = col.primaryKey ? "YES" : "";
|
|
3834
3979
|
console.log(
|
|
3835
|
-
" " + col.name.padEnd(20) + col.type.padEnd(15) + nullable.padEnd(10) + pk
|
|
3980
|
+
" " + String(col.name).padEnd(20) + String(col.type).padEnd(15) + nullable.padEnd(10) + pk
|
|
3836
3981
|
);
|
|
3837
3982
|
}
|
|
3838
3983
|
console.log("");
|
|
@@ -3841,7 +3986,7 @@ async function listTables(options) {
|
|
|
3841
3986
|
console.log("NAME".padEnd(30) + "ROWS".padEnd(12) + "SIZE");
|
|
3842
3987
|
console.log("-".repeat(55));
|
|
3843
3988
|
for (const table of tablesResult.tables) {
|
|
3844
|
-
console.log(table.name.padEnd(30) + "-".padEnd(12) + "-");
|
|
3989
|
+
console.log(String(table.name).padEnd(30) + "-".padEnd(12) + "-");
|
|
3845
3990
|
}
|
|
3846
3991
|
}
|
|
3847
3992
|
console.log("");
|
|
@@ -3893,23 +4038,23 @@ async function viewAnalytics(options) {
|
|
|
3893
4038
|
);
|
|
3894
4039
|
if (error2 || !data) {
|
|
3895
4040
|
throw new ApiClientError(
|
|
3896
|
-
error2?.message
|
|
3897
|
-
response
|
|
4041
|
+
error2?.message ?? "Failed to fetch analytics",
|
|
4042
|
+
response.status
|
|
3898
4043
|
);
|
|
3899
4044
|
}
|
|
3900
4045
|
const insightsResult = data;
|
|
3901
4046
|
console.log("Database Analytics");
|
|
3902
4047
|
console.log("==================");
|
|
3903
4048
|
console.log("");
|
|
3904
|
-
console.log(` Period: ${insightsResult.timeRange}`);
|
|
4049
|
+
console.log(` Period: ${String(insightsResult.timeRange)}`);
|
|
3905
4050
|
console.log(
|
|
3906
|
-
` Total Queries: ${insightsResult.summary.totalQueries.toLocaleString()}`
|
|
4051
|
+
` Total Queries: ${String(insightsResult.summary.totalQueries.toLocaleString())}`
|
|
3907
4052
|
);
|
|
3908
4053
|
console.log(
|
|
3909
|
-
` Total Rows Read: ${insightsResult.summary.totalRowsRead.toLocaleString()}`
|
|
4054
|
+
` Total Rows Read: ${String(insightsResult.summary.totalRowsRead.toLocaleString())}`
|
|
3910
4055
|
);
|
|
3911
4056
|
console.log(
|
|
3912
|
-
` Total Rows Written: ${insightsResult.summary.totalRowsWritten.toLocaleString()}`
|
|
4057
|
+
` Total Rows Written: ${String(insightsResult.summary.totalRowsWritten.toLocaleString())}`
|
|
3913
4058
|
);
|
|
3914
4059
|
const avgDuration = insightsResult.summary.totalQueries > 0 ? insightsResult.summary.totalDurationMs / insightsResult.summary.totalQueries : 0;
|
|
3915
4060
|
console.log(` Avg Query Duration: ${avgDuration.toFixed(2)}ms`);
|
|
@@ -3920,15 +4065,21 @@ async function viewAnalytics(options) {
|
|
|
3920
4065
|
console.log("");
|
|
3921
4066
|
for (let i = 0; i < insightsResult.topQueries.length; i++) {
|
|
3922
4067
|
const insight = insightsResult.topQueries[i];
|
|
3923
|
-
console.log(`${i + 1}. ${truncateQuery(insight.queryText, 60)}`);
|
|
3924
|
-
console.log(` Calls: ${insight.totalCalls.toLocaleString()}`);
|
|
3925
|
-
console.log(` Avg Duration: ${insight.avgDurationMs.toFixed(2)}ms`);
|
|
3926
4068
|
console.log(
|
|
3927
|
-
|
|
4069
|
+
`${String(i + 1)}. ${truncateQuery(String(insight.queryText), 60)}`
|
|
3928
4070
|
);
|
|
3929
|
-
console.log(`
|
|
4071
|
+
console.log(` Calls: ${String(insight.totalCalls.toLocaleString())}`);
|
|
3930
4072
|
console.log(
|
|
3931
|
-
`
|
|
4073
|
+
` Avg Duration: ${String(insight.avgDurationMs.toFixed(2))}ms`
|
|
4074
|
+
);
|
|
4075
|
+
console.log(
|
|
4076
|
+
` Total Duration: ${String(insight.totalDurationMs.toFixed(2))}ms`
|
|
4077
|
+
);
|
|
4078
|
+
console.log(
|
|
4079
|
+
` Rows Read: ${String(insight.totalRowsRead.toLocaleString())}`
|
|
4080
|
+
);
|
|
4081
|
+
console.log(
|
|
4082
|
+
` Rows Written: ${String(insight.totalRowsWritten.toLocaleString())}`
|
|
3932
4083
|
);
|
|
3933
4084
|
console.log("");
|
|
3934
4085
|
}
|
|
@@ -3967,8 +4118,8 @@ async function listDatabases(options) {
|
|
|
3967
4118
|
);
|
|
3968
4119
|
if (error2 || !data) {
|
|
3969
4120
|
throw new ApiClientError(
|
|
3970
|
-
error2?.message
|
|
3971
|
-
response
|
|
4121
|
+
error2?.message ?? "Failed to list databases",
|
|
4122
|
+
response.status
|
|
3972
4123
|
);
|
|
3973
4124
|
}
|
|
3974
4125
|
const databases = data.databases;
|
|
@@ -3985,10 +4136,10 @@ async function listDatabases(options) {
|
|
|
3985
4136
|
);
|
|
3986
4137
|
console.log("-".repeat(80));
|
|
3987
4138
|
for (const db of databases) {
|
|
3988
|
-
const id = db.id.substring(0, Math.min(24, db.id.length));
|
|
3989
|
-
const name = db.name.substring(0, Math.min(23, db.name.length)).padEnd(25);
|
|
3990
|
-
const region = (db.region || "-").padEnd(15);
|
|
3991
|
-
const created = db.createdAt ? new Date(db.createdAt).toLocaleDateString() : "-";
|
|
4139
|
+
const id = String(db.id).substring(0, Math.min(24, String(db.id).length));
|
|
4140
|
+
const name = String(db.name).substring(0, Math.min(23, String(db.name).length)).padEnd(25);
|
|
4141
|
+
const region = (String(db.region) || "-").padEnd(15);
|
|
4142
|
+
const created = db.createdAt ? new Date(String(db.createdAt)).toLocaleDateString() : "-";
|
|
3992
4143
|
console.log(`${id} ${name}${region}${created}`);
|
|
3993
4144
|
}
|
|
3994
4145
|
console.log("");
|
|
@@ -4135,8 +4286,8 @@ async function retryDeployment(deploymentId) {
|
|
|
4135
4286
|
);
|
|
4136
4287
|
if (error2 || !data) {
|
|
4137
4288
|
throw new ApiClientError(
|
|
4138
|
-
error2?.message
|
|
4139
|
-
response
|
|
4289
|
+
error2?.message ?? "Failed to retry deployment",
|
|
4290
|
+
response.status
|
|
4140
4291
|
);
|
|
4141
4292
|
}
|
|
4142
4293
|
const retryResult = data;
|
|
@@ -4174,8 +4325,8 @@ async function viewDeploymentLogs(deploymentId, _options) {
|
|
|
4174
4325
|
);
|
|
4175
4326
|
if (error2 || !data) {
|
|
4176
4327
|
throw new ApiClientError(
|
|
4177
|
-
error2?.message
|
|
4178
|
-
response
|
|
4328
|
+
error2?.message ?? "Failed to fetch logs",
|
|
4329
|
+
response.status
|
|
4179
4330
|
);
|
|
4180
4331
|
}
|
|
4181
4332
|
const logsResult = data;
|
|
@@ -4212,8 +4363,8 @@ async function viewDeployment(deploymentId, _options) {
|
|
|
4212
4363
|
);
|
|
4213
4364
|
if (error2 || !data) {
|
|
4214
4365
|
throw new ApiClientError(
|
|
4215
|
-
error2?.message
|
|
4216
|
-
response
|
|
4366
|
+
error2?.message ?? "Failed to fetch deployment",
|
|
4367
|
+
response.status
|
|
4217
4368
|
);
|
|
4218
4369
|
}
|
|
4219
4370
|
const deployment = data.deployment;
|
|
@@ -4275,14 +4426,14 @@ async function listDeployments(options) {
|
|
|
4275
4426
|
query: {
|
|
4276
4427
|
projectId: options.projectId,
|
|
4277
4428
|
branch: options.branch,
|
|
4278
|
-
limit: options.limit?.toString()
|
|
4429
|
+
limit: options.limit?.toString() ?? "20"
|
|
4279
4430
|
}
|
|
4280
4431
|
}
|
|
4281
4432
|
});
|
|
4282
4433
|
if (error2 || !data) {
|
|
4283
4434
|
throw new ApiClientError(
|
|
4284
|
-
error2?.message
|
|
4285
|
-
response
|
|
4435
|
+
error2?.message ?? "Failed to list deployments",
|
|
4436
|
+
response.status
|
|
4286
4437
|
);
|
|
4287
4438
|
}
|
|
4288
4439
|
const deployments = data.deployments;
|
|
@@ -4301,7 +4452,7 @@ async function listDeployments(options) {
|
|
|
4301
4452
|
for (const d of deployments) {
|
|
4302
4453
|
const id = d.id.substring(0, Math.min(10, d.id.length));
|
|
4303
4454
|
const status = d.status.padEnd(10);
|
|
4304
|
-
const branchSource = d.branch
|
|
4455
|
+
const branchSource = d.branch ?? "-";
|
|
4305
4456
|
const branch = branchSource.substring(0, Math.min(18, branchSource.length)).padEnd(20);
|
|
4306
4457
|
const commit = d.commitSha ? d.commitSha.substring(0, Math.min(8, d.commitSha.length)) : "-";
|
|
4307
4458
|
const created = new Date(d.createdAt).toLocaleDateString();
|
|
@@ -4554,7 +4705,9 @@ async function exchangeCodeForToken(apiUrl, code, redirectUri) {
|
|
|
4554
4705
|
});
|
|
4555
4706
|
if (!response.ok) {
|
|
4556
4707
|
const errorBody = await response.text();
|
|
4557
|
-
throw new Error(
|
|
4708
|
+
throw new Error(
|
|
4709
|
+
`Token exchange failed: ${String(response.status)} - ${errorBody}`
|
|
4710
|
+
);
|
|
4558
4711
|
}
|
|
4559
4712
|
return await response.json();
|
|
4560
4713
|
}
|
|
@@ -4578,43 +4731,46 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4578
4731
|
},
|
|
4579
4732
|
5 * 60 * 1e3
|
|
4580
4733
|
);
|
|
4581
|
-
server = createServer(
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4734
|
+
server = createServer((req, res) => {
|
|
4735
|
+
void (async () => {
|
|
4736
|
+
const reqUrl = new URL(
|
|
4737
|
+
req.url ?? "/",
|
|
4738
|
+
`http://${CALLBACK_HOST}:${String(CALLBACK_PORT)}`
|
|
4739
|
+
);
|
|
4740
|
+
if (reqUrl.pathname !== "/callback") {
|
|
4741
|
+
res.writeHead(404);
|
|
4742
|
+
res.end("Not found");
|
|
4743
|
+
return;
|
|
4744
|
+
}
|
|
4745
|
+
const code = reqUrl.searchParams.get("code");
|
|
4746
|
+
const state = reqUrl.searchParams.get("state");
|
|
4747
|
+
const error2 = reqUrl.searchParams.get("error");
|
|
4748
|
+
const errorDescription = reqUrl.searchParams.get("error_description");
|
|
4749
|
+
if (error2) {
|
|
4750
|
+
clearTimeout(timeout);
|
|
4751
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4752
|
+
res.end(`
|
|
4599
4753
|
<!DOCTYPE html>
|
|
4600
4754
|
<html>
|
|
4601
4755
|
<head><title>Login Failed</title></head>
|
|
4602
4756
|
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
4603
4757
|
<h1>Login Failed</h1>
|
|
4604
4758
|
<p>Error: ${error2}</p>
|
|
4605
|
-
<p>${errorDescription
|
|
4759
|
+
<p>${errorDescription ?? ""}</p>
|
|
4606
4760
|
<p>You can close this window.</p>
|
|
4607
4761
|
</body>
|
|
4608
4762
|
</html>
|
|
4609
4763
|
`);
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4764
|
+
server?.close();
|
|
4765
|
+
reject(
|
|
4766
|
+
new Error(`OAuth error: ${error2} - ${errorDescription ?? ""}`)
|
|
4767
|
+
);
|
|
4768
|
+
return;
|
|
4769
|
+
}
|
|
4770
|
+
if (!code) {
|
|
4771
|
+
clearTimeout(timeout);
|
|
4772
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4773
|
+
res.end(`
|
|
4618
4774
|
<!DOCTYPE html>
|
|
4619
4775
|
<html>
|
|
4620
4776
|
<head><title>Login Failed</title></head>
|
|
@@ -4625,14 +4781,14 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4625
4781
|
</body>
|
|
4626
4782
|
</html>
|
|
4627
4783
|
`);
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4784
|
+
server?.close();
|
|
4785
|
+
reject(new Error("No authorization code received"));
|
|
4786
|
+
return;
|
|
4787
|
+
}
|
|
4788
|
+
if (state !== expectedState) {
|
|
4789
|
+
clearTimeout(timeout);
|
|
4790
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4791
|
+
res.end(`
|
|
4636
4792
|
<!DOCTYPE html>
|
|
4637
4793
|
<html>
|
|
4638
4794
|
<head><title>Login Failed</title></head>
|
|
@@ -4643,20 +4799,20 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4643
4799
|
</body>
|
|
4644
4800
|
</html>
|
|
4645
4801
|
`);
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4802
|
+
server?.close();
|
|
4803
|
+
reject(new Error("State mismatch - possible CSRF attack"));
|
|
4804
|
+
return;
|
|
4805
|
+
}
|
|
4806
|
+
try {
|
|
4807
|
+
const redirectUri = `http://${CALLBACK_HOST}:${String(CALLBACK_PORT)}/callback`;
|
|
4808
|
+
const tokenResponse = await exchangeCodeForToken(
|
|
4809
|
+
apiUrl,
|
|
4810
|
+
code,
|
|
4811
|
+
redirectUri
|
|
4812
|
+
);
|
|
4813
|
+
clearTimeout(timeout);
|
|
4814
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4815
|
+
res.end(`
|
|
4660
4816
|
<!DOCTYPE html>
|
|
4661
4817
|
<html>
|
|
4662
4818
|
<head><title>Login Successful</title></head>
|
|
@@ -4667,12 +4823,12 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4667
4823
|
</body>
|
|
4668
4824
|
</html>
|
|
4669
4825
|
`);
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4826
|
+
server?.close();
|
|
4827
|
+
resolve(tokenResponse);
|
|
4828
|
+
} catch (err) {
|
|
4829
|
+
clearTimeout(timeout);
|
|
4830
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
4831
|
+
res.end(`
|
|
4676
4832
|
<!DOCTYPE html>
|
|
4677
4833
|
<html>
|
|
4678
4834
|
<head><title>Login Failed</title></head>
|
|
@@ -4684,15 +4840,16 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4684
4840
|
</body>
|
|
4685
4841
|
</html>
|
|
4686
4842
|
`);
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4843
|
+
server?.close();
|
|
4844
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4845
|
+
}
|
|
4846
|
+
})();
|
|
4690
4847
|
});
|
|
4691
4848
|
server.on("error", (err) => {
|
|
4692
4849
|
clearTimeout(timeout);
|
|
4693
4850
|
if (err.code === "EADDRINUSE") {
|
|
4694
4851
|
const portError = new Error(
|
|
4695
|
-
`Port ${CALLBACK_PORT} is already in use. Please close any other process using this port and try again.`
|
|
4852
|
+
`Port ${String(CALLBACK_PORT)} is already in use. Please close any other process using this port and try again.`
|
|
4696
4853
|
);
|
|
4697
4854
|
reject(portError);
|
|
4698
4855
|
rejectListening(portError);
|
|
@@ -4711,9 +4868,9 @@ function createCallbackServer(expectedState, apiUrl) {
|
|
|
4711
4868
|
return { tokenPromise, waitForListening };
|
|
4712
4869
|
}
|
|
4713
4870
|
async function loginCommand(options = {}) {
|
|
4714
|
-
const apiUrl = options.apiUrl
|
|
4871
|
+
const apiUrl = options.apiUrl ?? await getApiUrl();
|
|
4715
4872
|
const state = generateState();
|
|
4716
|
-
const redirectUri = `http://${CALLBACK_HOST}:${CALLBACK_PORT}/callback`;
|
|
4873
|
+
const redirectUri = `http://${CALLBACK_HOST}:${String(CALLBACK_PORT)}/callback`;
|
|
4717
4874
|
console.log("Starting login flow...");
|
|
4718
4875
|
console.log("");
|
|
4719
4876
|
const authUrl = new URL(`${apiUrl}/oauth/authorize`);
|
|
@@ -5031,6 +5188,12 @@ function generateEnvType(config) {
|
|
|
5031
5188
|
properties.push(` ${bindingName}: CacheBinding;`);
|
|
5032
5189
|
}
|
|
5033
5190
|
}
|
|
5191
|
+
if (config.state) {
|
|
5192
|
+
imports.push("StateBinding");
|
|
5193
|
+
for (const bindingName of Object.keys(config.state)) {
|
|
5194
|
+
properties.push(` ${bindingName}: StateBinding;`);
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5034
5197
|
if (config.workflow) {
|
|
5035
5198
|
imports.push("WorkflowBinding");
|
|
5036
5199
|
for (const bindingName of Object.keys(config.workflow)) {
|
|
@@ -5078,7 +5241,7 @@ async function typesCommand(options = {}) {
|
|
|
5078
5241
|
console.error("Error: ploy.yaml not found in current directory");
|
|
5079
5242
|
process.exit(1);
|
|
5080
5243
|
}
|
|
5081
|
-
const hasBindings2 = config.ai || config.db || config.queue || config.cache || config.workflow;
|
|
5244
|
+
const hasBindings2 = config.ai || config.db || config.queue || config.cache || config.state || config.workflow;
|
|
5082
5245
|
if (!hasBindings2) {
|
|
5083
5246
|
console.log("No bindings found in ploy.yaml. Generating empty Env.");
|
|
5084
5247
|
}
|