@meetploy/cli 1.14.1 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +103 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -183,6 +183,19 @@ function validatePloyConfig(config, configFile = "ploy.yaml", options = {}) {
183
183
  validatedConfig.out = validateRelativePath(config.out, "out", configFile);
184
184
  validatedConfig.base = validateRelativePath(config.base, "base", configFile);
185
185
  validatedConfig.main = validateRelativePath(config.main, "main", configFile);
186
+ if (config.env !== void 0) {
187
+ if (typeof config.env !== "object" || config.env === null) {
188
+ throw new Error(`'env' in ${configFile} must be a map of KEY: value`);
189
+ }
190
+ for (const [key, value] of Object.entries(config.env)) {
191
+ if (!BINDING_NAME_REGEX.test(key)) {
192
+ throw new Error(`Invalid env key '${key}' in ${configFile}. Env keys must be uppercase with underscores (e.g., FOO, MY_VAR)`);
193
+ }
194
+ if (typeof value !== "string") {
195
+ throw new Error(`Env key '${key}' in ${configFile} must have a string value`);
196
+ }
197
+ }
198
+ }
186
199
  validateBindings(config.db, "db", configFile);
187
200
  validateBindings(config.queue, "queue", configFile);
188
201
  validateBindings(config.cache, "cache", configFile);
@@ -263,7 +276,50 @@ function readAndValidatePloyConfigSync(projectDir, configPath, validationOptions
263
276
  return validatePloyConfig(config, configFile, validationOptions);
264
277
  }
265
278
  function hasBindings(config) {
266
- return !!(config.db ?? config.queue ?? config.cache ?? config.state ?? config.workflow ?? config.ai ?? config.auth);
279
+ return !!(config.env ?? config.db ?? config.queue ?? config.cache ?? config.state ?? config.workflow ?? config.ai ?? config.auth);
280
+ }
281
+ function parseDotEnv(content) {
282
+ const result = {};
283
+ for (const line of content.split("\n")) {
284
+ const trimmed = line.trim();
285
+ if (!trimmed || trimmed.startsWith("#")) {
286
+ continue;
287
+ }
288
+ const eqIndex = trimmed.indexOf("=");
289
+ if (eqIndex === -1) {
290
+ continue;
291
+ }
292
+ const key = trimmed.slice(0, eqIndex).trim();
293
+ let value = trimmed.slice(eqIndex + 1).trim();
294
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
295
+ value = value.slice(1, -1);
296
+ }
297
+ result[key] = value;
298
+ }
299
+ return result;
300
+ }
301
+ function loadDotEnvSync(projectDir) {
302
+ const envPath = join(projectDir, ".env");
303
+ if (!existsSync(envPath)) {
304
+ return {};
305
+ }
306
+ const content = readFileSync(envPath, "utf-8");
307
+ return parseDotEnv(content);
308
+ }
309
+ function resolveEnvVars(configEnv, dotEnv, processEnv) {
310
+ const result = {};
311
+ for (const [key, value] of Object.entries(configEnv)) {
312
+ if (value.startsWith("$")) {
313
+ const refName = value.slice(1);
314
+ const resolved = dotEnv[refName] ?? processEnv[refName];
315
+ if (resolved !== void 0) {
316
+ result[key] = resolved;
317
+ }
318
+ } else {
319
+ result[key] = value;
320
+ }
321
+ }
322
+ return result;
267
323
  }
268
324
  function getWorkerEntryPoint(projectDir, config) {
269
325
  if (config.main) {
@@ -306,10 +362,13 @@ __export(cli_exports, {
306
362
  getWorkerEntryPoint: () => getWorkerEntryPoint,
307
363
  hasBindings: () => hasBindings,
308
364
  isPnpmWorkspace: () => isPnpmWorkspace,
365
+ loadDotEnvSync: () => loadDotEnvSync,
366
+ parseDotEnv: () => parseDotEnv,
309
367
  readAndValidatePloyConfig: () => readAndValidatePloyConfig,
310
368
  readAndValidatePloyConfigSync: () => readAndValidatePloyConfigSync,
311
369
  readPloyConfig: () => readPloyConfig,
312
370
  readPloyConfigSync: () => readPloyConfigSync,
371
+ resolveEnvVars: () => resolveEnvVars,
313
372
  validatePloyConfig: () => validatePloyConfig
314
373
  });
315
374
  var init_cli = __esm({
@@ -1035,7 +1094,7 @@ export async function executeWorkflow<TInput, TOutput, TEnv>(
1035
1094
  `;
1036
1095
  }
1037
1096
  });
1038
- function generateWrapperCode(config, mockServiceUrl) {
1097
+ function generateWrapperCode(config, mockServiceUrl, envVars) {
1039
1098
  const imports = [];
1040
1099
  const bindings = [];
1041
1100
  if (config.db) {
@@ -1125,6 +1184,11 @@ function generateWrapperCode(config, mockServiceUrl) {
1125
1184
  }
1126
1185
  }
1127
1186
  }` : "";
1187
+ const envVarsEntries = envVars && Object.keys(envVars).length > 0 ? Object.entries(envVars).map(([key, value]) => ` ${JSON.stringify(key)}: ${JSON.stringify(value)}`).join(",\n") : "";
1188
+ const envVarsCode = envVarsEntries ? `
1189
+ injectedEnv.vars = {
1190
+ ${envVarsEntries}
1191
+ };` : "";
1128
1192
  return `${imports.join("\n")}
1129
1193
 
1130
1194
  const ployBindings = {
@@ -1133,7 +1197,7 @@ ${bindings.join("\n")}
1133
1197
 
1134
1198
  export default {
1135
1199
  async fetch(request, env, ctx) {
1136
- const injectedEnv = { ...env, ...ployBindings };
1200
+ const injectedEnv = { ...env, ...ployBindings };${envVarsCode}
1137
1201
  ${workflowHandlerCode}
1138
1202
  ${queueHandlerCode}
1139
1203
 
@@ -1146,7 +1210,7 @@ export default {
1146
1210
 
1147
1211
  async scheduled(event, env, ctx) {
1148
1212
  if (userWorker.scheduled) {
1149
- const injectedEnv = { ...env, ...ployBindings };
1213
+ const injectedEnv = { ...env, ...ployBindings };${envVarsCode}
1150
1214
  return userWorker.scheduled(event, injectedEnv, ctx);
1151
1215
  }
1152
1216
  }
@@ -1201,8 +1265,8 @@ function createRuntimePlugin(_config) {
1201
1265
  };
1202
1266
  }
1203
1267
  async function bundleWorker(options) {
1204
- const { projectDir, tempDir, entryPoint, config, mockServiceUrl } = options;
1205
- const wrapperCode = generateWrapperCode(config, mockServiceUrl);
1268
+ const { projectDir, tempDir, entryPoint, config, mockServiceUrl, envVars } = options;
1269
+ const wrapperCode = generateWrapperCode(config, mockServiceUrl, envVars);
1206
1270
  const wrapperPath = join(tempDir, "wrapper.ts");
1207
1271
  writeFileSync(wrapperPath, wrapperCode);
1208
1272
  const bundlePath = join(tempDir, "worker.bundle.js");
@@ -1412,6 +1476,20 @@ var init_watcher = __esm({
1412
1476
  }
1413
1477
  });
1414
1478
 
1479
+ // ../emulator/dist/config/env.js
1480
+ function resolveEnvVars2(projectDir, config) {
1481
+ if (!config.env) {
1482
+ return {};
1483
+ }
1484
+ const dotEnv = loadDotEnvSync(projectDir);
1485
+ return resolveEnvVars(config.env, dotEnv, process.env);
1486
+ }
1487
+ var init_env = __esm({
1488
+ "../emulator/dist/config/env.js"() {
1489
+ init_cli();
1490
+ }
1491
+ });
1492
+
1415
1493
  // ../emulator/dist/config/ploy-config.js
1416
1494
  function readPloyConfig2(projectDir, configPath) {
1417
1495
  const config = readPloyConfigSync(projectDir, configPath);
@@ -3093,6 +3171,7 @@ var init_emulator = __esm({
3093
3171
  "../emulator/dist/emulator.js"() {
3094
3172
  init_bundler();
3095
3173
  init_watcher();
3174
+ init_env();
3096
3175
  init_ploy_config2();
3097
3176
  init_workerd_config();
3098
3177
  init_mock_server();
@@ -3110,6 +3189,7 @@ var init_emulator = __esm({
3110
3189
  workerdProcess = null;
3111
3190
  fileWatcher = null;
3112
3191
  queueProcessor = null;
3192
+ resolvedEnvVars = {};
3113
3193
  constructor(options = {}) {
3114
3194
  const port = options.port ?? 8787;
3115
3195
  this.options = {
@@ -3127,6 +3207,10 @@ var init_emulator = __esm({
3127
3207
  try {
3128
3208
  this.config = readPloyConfig2(this.projectDir, this.options.configPath);
3129
3209
  debug(`Loaded config: ${JSON.stringify(this.config)}`, this.options.verbose);
3210
+ this.resolvedEnvVars = resolveEnvVars2(this.projectDir, this.config);
3211
+ if (Object.keys(this.resolvedEnvVars).length > 0) {
3212
+ debug(`Resolved env vars: ${Object.keys(this.resolvedEnvVars).join(", ")}`, this.options.verbose);
3213
+ }
3130
3214
  this.tempDir = ensureTempDir(this.projectDir);
3131
3215
  ensureDataDir(this.projectDir);
3132
3216
  debug(`Temp dir: ${this.tempDir}`, this.options.verbose);
@@ -3174,6 +3258,9 @@ var init_emulator = __esm({
3174
3258
  }
3175
3259
  success(`Emulator running at http://${this.options.host}:${String(this.options.port)}`);
3176
3260
  log(` Dashboard: http://${this.options.host}:${String(this.mockServer.port)}`);
3261
+ if (Object.keys(this.resolvedEnvVars).length > 0) {
3262
+ log(` Env vars: ${Object.keys(this.resolvedEnvVars).join(", ")}`);
3263
+ }
3177
3264
  if (this.config.db) {
3178
3265
  log(` DB bindings: ${Object.keys(this.config.db).join(", ")}`);
3179
3266
  }
@@ -3202,7 +3289,8 @@ var init_emulator = __esm({
3202
3289
  tempDir: this.tempDir,
3203
3290
  entryPoint,
3204
3291
  config: this.config,
3205
- mockServiceUrl
3292
+ mockServiceUrl,
3293
+ envVars: this.resolvedEnvVars
3206
3294
  });
3207
3295
  }
3208
3296
  async startWorkerd(configPath) {
@@ -3228,9 +3316,7 @@ var init_emulator = __esm({
3228
3316
  let stderrOutput = "";
3229
3317
  this.workerdProcess.stdout?.on("data", (data) => {
3230
3318
  const output = data.toString();
3231
- if (this.options.verbose) {
3232
- process.stdout.write(output);
3233
- }
3319
+ log(`[workerd stdout] ${output.trim()}`);
3234
3320
  if (!started && (output.includes("Listening") || output.includes("running"))) {
3235
3321
  started = true;
3236
3322
  resolve(void 0);
@@ -5166,6 +5252,12 @@ async function updateTsConfigInclude(cwd, outputPath) {
5166
5252
  function generateEnvType(config) {
5167
5253
  const imports = [];
5168
5254
  const properties = [];
5255
+ if (config.env && Object.keys(config.env).length > 0) {
5256
+ const varProps = Object.keys(config.env).map((key) => ` ${key}: string;`).join("\n");
5257
+ properties.push(` vars: {
5258
+ ${varProps}
5259
+ };`);
5260
+ }
5169
5261
  if (config.ai) {
5170
5262
  properties.push(" AI_URL: string;");
5171
5263
  properties.push(" AI_TOKEN: string;");
@@ -5241,7 +5333,7 @@ async function typesCommand(options = {}) {
5241
5333
  console.error("Error: ploy.yaml not found in current directory");
5242
5334
  process.exit(1);
5243
5335
  }
5244
- const hasBindings2 = config.ai || config.db || config.queue || config.cache || config.state || config.workflow;
5336
+ const hasBindings2 = config.env || config.ai || config.db || config.queue || config.cache || config.state || config.workflow;
5245
5337
  if (!hasBindings2) {
5246
5338
  console.log("No bindings found in ploy.yaml. Generating empty Env.");
5247
5339
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meetploy/cli",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",