@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/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
- if (!type) {
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
- if (!type) {
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
- if (!type) {
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
- if (!type) {
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 || DEFAULT_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 || "ploy.yaml";
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 || "ploy.yaml";
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 || "ploy.yaml";
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 || "ploy.yaml";
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 || config.queue || config.cache || config.workflow || config.ai || config.auth);
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(async () => {
1347
+ debounceTimer = setTimeout(() => {
1286
1348
  if (isRebuilding) {
1287
1349
  return;
1288
1350
  }
1289
1351
  isRebuilding = true;
1290
- try {
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 || "ploy.yaml"}`);
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 || "ploy.yaml"}. Must be 'dynamic' or 'worker'`);
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 = async (c) => {
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 = async (c) => {
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") || "50", 10);
1756
- const offset = parseInt(c.req.query("offset") || "0", 10);
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") || "50", 10);
1969
- const offset = parseInt(c.req.query("offset") || "0", 10);
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") || "10", 10);
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") || "20", 10);
2078
- const offset = parseInt(c.req.query("offset") || "0", 10);
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") || "20", 10);
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 || 0);
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(() => res());
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
- return await new Promise((resolve, reject) => {
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 = async () => {
3304
+ const handler = () => {
3163
3305
  log("\nShutting down...");
3164
- await this.stop();
3165
- process.exit(0);
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 || s;
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 || "./ploy.yaml";
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 || "./ploy.yaml";
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", async () => {
3460
- await cleanup();
3461
- process.exit(0);
3602
+ process.on("SIGINT", () => {
3603
+ void cleanup().then(() => {
3604
+ process.exit(0);
3605
+ });
3462
3606
  });
3463
- process.on("SIGTERM", async () => {
3464
- await cleanup();
3465
- process.exit(0);
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 || "Failed to fetch tables",
3798
- response?.status || 500
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 || "Failed to fetch schema",
3820
- schemaResponse?.status || 500
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 || "Failed to fetch analytics",
3897
- response?.status || 500
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
- ` Total Duration: ${insight.totalDurationMs.toFixed(2)}ms`
4069
+ `${String(i + 1)}. ${truncateQuery(String(insight.queryText), 60)}`
3928
4070
  );
3929
- console.log(` Rows Read: ${insight.totalRowsRead.toLocaleString()}`);
4071
+ console.log(` Calls: ${String(insight.totalCalls.toLocaleString())}`);
3930
4072
  console.log(
3931
- ` Rows Written: ${insight.totalRowsWritten.toLocaleString()}`
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 || "Failed to list databases",
3971
- response?.status || 500
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 || "Failed to retry deployment",
4139
- response?.status || 500
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 || "Failed to fetch logs",
4178
- response?.status || 500
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 || "Failed to fetch deployment",
4216
- response?.status || 500
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() || "20"
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 || "Failed to list deployments",
4285
- response?.status || 500
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(`Token exchange failed: ${response.status} - ${errorBody}`);
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(async (req, res) => {
4582
- const reqUrl = new URL(
4583
- req.url || "/",
4584
- `http://${CALLBACK_HOST}:${CALLBACK_PORT}`
4585
- );
4586
- if (reqUrl.pathname !== "/callback") {
4587
- res.writeHead(404);
4588
- res.end("Not found");
4589
- return;
4590
- }
4591
- const code = reqUrl.searchParams.get("code");
4592
- const state = reqUrl.searchParams.get("state");
4593
- const error2 = reqUrl.searchParams.get("error");
4594
- const errorDescription = reqUrl.searchParams.get("error_description");
4595
- if (error2) {
4596
- clearTimeout(timeout);
4597
- res.writeHead(400, { "Content-Type": "text/html" });
4598
- res.end(`
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 || ""}</p>
4759
+ <p>${errorDescription ?? ""}</p>
4606
4760
  <p>You can close this window.</p>
4607
4761
  </body>
4608
4762
  </html>
4609
4763
  `);
4610
- server?.close();
4611
- reject(new Error(`OAuth error: ${error2} - ${errorDescription || ""}`));
4612
- return;
4613
- }
4614
- if (!code) {
4615
- clearTimeout(timeout);
4616
- res.writeHead(400, { "Content-Type": "text/html" });
4617
- res.end(`
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
- server?.close();
4629
- reject(new Error("No authorization code received"));
4630
- return;
4631
- }
4632
- if (state !== expectedState) {
4633
- clearTimeout(timeout);
4634
- res.writeHead(400, { "Content-Type": "text/html" });
4635
- res.end(`
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
- server?.close();
4647
- reject(new Error("State mismatch - possible CSRF attack"));
4648
- return;
4649
- }
4650
- try {
4651
- const redirectUri = `http://${CALLBACK_HOST}:${CALLBACK_PORT}/callback`;
4652
- const tokenResponse = await exchangeCodeForToken(
4653
- apiUrl,
4654
- code,
4655
- redirectUri
4656
- );
4657
- clearTimeout(timeout);
4658
- res.writeHead(200, { "Content-Type": "text/html" });
4659
- res.end(`
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
- server?.close();
4671
- resolve(tokenResponse);
4672
- } catch (err) {
4673
- clearTimeout(timeout);
4674
- res.writeHead(500, { "Content-Type": "text/html" });
4675
- res.end(`
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
- server?.close();
4688
- reject(err);
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 || await getApiUrl();
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
  }