@lumerahq/cli 0.35.1 → 0.35.3

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.
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  buildReleaseRunnerArgs,
7
7
  runReleaseRunner
8
- } from "./chunk-2EHJH2QU.js";
8
+ } from "./chunk-A36PFXKK.js";
9
9
  import {
10
10
  userContextHeaders
11
11
  } from "./chunk-2A2DSLIE.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  findFunctionsProjectRoot
3
- } from "./chunk-XZ5VCTGY.js";
3
+ } from "./chunk-PYUWPRBT.js";
4
4
  import {
5
5
  RELEASE_SDK_REQUIREMENT
6
- } from "./chunk-5FGDEZSJ.js";
6
+ } from "./chunk-WZXBYKAW.js";
7
7
 
8
8
  // src/commands/project.ts
9
9
  import { spawn } from "child_process";
@@ -146,6 +146,7 @@ async function syncResourceShareDeps(projectRoot, api, projectExternalId, opts)
146
146
  mkdirSync(viewsDir, { recursive: true });
147
147
  }
148
148
  for (const item of functionImports) {
149
+ if (item.managed_by_git) continue;
149
150
  const fileName = functionImportFileName(item);
150
151
  const filePath = join(functionsDir, fileName);
151
152
  const exists = existsSync(filePath);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FUNCTIONS_SDK_REQUIREMENT
3
- } from "./chunk-5FGDEZSJ.js";
3
+ } from "./chunk-WZXBYKAW.js";
4
4
 
5
5
  // src/commands/functions.ts
6
6
  import { spawn } from "child_process";
@@ -1,6 +1,6 @@
1
1
  // src/lib/functions-sdk.ts
2
- var FUNCTIONS_SDK_VERSION_RANGE = ">=0.45.0,<0.46.0";
3
- var FUNCTIONS_SDK_COMPATIBILITY_RANGE = ">=0.34.0,<0.46.0";
2
+ var FUNCTIONS_SDK_VERSION_RANGE = ">=0.47.0,<0.48.0";
3
+ var FUNCTIONS_SDK_COMPATIBILITY_RANGE = ">=0.34.0,<0.48.0";
4
4
  var FUNCTIONS_SDK_REQUIREMENT = `lumera[functions]${FUNCTIONS_SDK_COMPATIBILITY_RANGE}`;
5
5
  var DEFAULT_FUNCTIONS_SDK_REQUIREMENT = `lumera[functions]${FUNCTIONS_SDK_VERSION_RANGE}`;
6
6
  var RELEASE_SDK_REQUIREMENT = `lumera[release]${FUNCTIONS_SDK_VERSION_RANGE}`;
@@ -2,7 +2,7 @@ import {
2
2
  deps,
3
3
  syncDeps,
4
4
  syncResourceShareDeps
5
- } from "./chunk-A27HZEU6.js";
5
+ } from "./chunk-COPJBRRG.js";
6
6
  import "./chunk-2CR762KB.js";
7
7
  import "./chunk-2A2DSLIE.js";
8
8
  import "./chunk-JLVVHTBY.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  dev
3
- } from "./chunk-5TQ3BQ2X.js";
3
+ } from "./chunk-3SON777C.js";
4
4
  import "./chunk-UGAIWMQV.js";
5
- import "./chunk-2EHJH2QU.js";
6
- import "./chunk-XZ5VCTGY.js";
7
- import "./chunk-5FGDEZSJ.js";
5
+ import "./chunk-A36PFXKK.js";
6
+ import "./chunk-PYUWPRBT.js";
7
+ import "./chunk-WZXBYKAW.js";
8
8
  import {
9
9
  syncDeps
10
- } from "./chunk-A27HZEU6.js";
10
+ } from "./chunk-COPJBRRG.js";
11
11
  import {
12
12
  loadEnv
13
13
  } from "./chunk-2CR762KB.js";
@@ -4,8 +4,8 @@ import {
4
4
  findFunctionsProjectRoot,
5
5
  functions,
6
6
  runFunctionRunner
7
- } from "./chunk-XZ5VCTGY.js";
8
- import "./chunk-5FGDEZSJ.js";
7
+ } from "./chunk-PYUWPRBT.js";
8
+ import "./chunk-WZXBYKAW.js";
9
9
  import "./chunk-PNKVD2UK.js";
10
10
  export {
11
11
  buildFunctionRunnerArgs,
package/dist/index.js CHANGED
@@ -243,40 +243,40 @@ async function main() {
243
243
  switch (command) {
244
244
  // Resource commands
245
245
  case "plan":
246
- await import("./resources-X7K436TK.js").then((m) => m.plan(args.slice(1)));
246
+ await import("./resources-OZ2JIMR6.js").then((m) => m.plan(args.slice(1)));
247
247
  break;
248
248
  case "apply":
249
- await import("./resources-X7K436TK.js").then((m) => m.apply(args.slice(1)));
249
+ await import("./resources-OZ2JIMR6.js").then((m) => m.apply(args.slice(1)));
250
250
  break;
251
251
  case "pull":
252
- await import("./resources-X7K436TK.js").then((m) => m.pull(args.slice(1)));
252
+ await import("./resources-OZ2JIMR6.js").then((m) => m.pull(args.slice(1)));
253
253
  break;
254
254
  case "destroy":
255
- await import("./resources-X7K436TK.js").then((m) => m.destroy(args.slice(1)));
255
+ await import("./resources-OZ2JIMR6.js").then((m) => m.destroy(args.slice(1)));
256
256
  break;
257
257
  case "list":
258
- await import("./resources-X7K436TK.js").then((m) => m.list(args.slice(1)));
258
+ await import("./resources-OZ2JIMR6.js").then((m) => m.list(args.slice(1)));
259
259
  break;
260
260
  case "show":
261
- await import("./resources-X7K436TK.js").then((m) => m.show(args.slice(1)));
261
+ await import("./resources-OZ2JIMR6.js").then((m) => m.show(args.slice(1)));
262
262
  break;
263
263
  case "diff":
264
- await import("./resources-X7K436TK.js").then((m) => m.diff(args.slice(1)));
264
+ await import("./resources-OZ2JIMR6.js").then((m) => m.diff(args.slice(1)));
265
265
  break;
266
266
  // Development
267
267
  case "dev":
268
- await import("./dev-E2O6R7WW.js").then((m) => m.dev(args.slice(1)));
268
+ await import("./dev-NX7HLTG7.js").then((m) => m.dev(args.slice(1)));
269
269
  break;
270
270
  case "run":
271
271
  await import("./run-BJ7PXSEH.js").then((m) => m.run(args.slice(1)));
272
272
  break;
273
273
  case "functions":
274
- await import("./functions-5OOHGQT7.js").then(
274
+ await import("./functions-OPZATS2S.js").then(
275
275
  (m) => m.functions(subcommand, args.slice(2))
276
276
  );
277
277
  break;
278
278
  case "project":
279
- await import("./project-ISKXDHGJ.js").then(
279
+ await import("./project-YVH7DOMW.js").then(
280
280
  (m) => m.project(subcommand, args.slice(2))
281
281
  );
282
282
  break;
@@ -294,7 +294,7 @@ async function main() {
294
294
  await import("./status-NPNIY3QP.js").then((m) => m.status(args.slice(1)));
295
295
  break;
296
296
  case "migrate":
297
- await import("./migrate-Q2QY3YSL.js").then((m) => m.migrate(args.slice(1)));
297
+ await import("./migrate-3JERY7QU.js").then((m) => m.migrate(args.slice(1)));
298
298
  break;
299
299
  // Skills
300
300
  case "skills":
@@ -302,7 +302,7 @@ async function main() {
302
302
  break;
303
303
  // Dependencies
304
304
  case "deps":
305
- await import("./deps-DLJUDFNK.js").then((m) => m.deps(args.slice(1)));
305
+ await import("./deps-4BPFSUNT.js").then((m) => m.deps(args.slice(1)));
306
306
  break;
307
307
  // Feature flags (read the in-sandbox snapshot)
308
308
  case "flags":
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-UGAIWMQV.js";
5
5
  import {
6
6
  DEFAULT_FUNCTIONS_SDK_REQUIREMENT
7
- } from "./chunk-5FGDEZSJ.js";
7
+ } from "./chunk-WZXBYKAW.js";
8
8
  import {
9
9
  detectProjectVersion,
10
10
  findProjectRoot
@@ -2,9 +2,9 @@ import {
2
2
  buildReleaseRunnerArgs,
3
3
  project,
4
4
  runReleaseRunner
5
- } from "./chunk-2EHJH2QU.js";
6
- import "./chunk-XZ5VCTGY.js";
7
- import "./chunk-5FGDEZSJ.js";
5
+ } from "./chunk-A36PFXKK.js";
6
+ import "./chunk-PYUWPRBT.js";
7
+ import "./chunk-WZXBYKAW.js";
8
8
  import "./chunk-PNKVD2UK.js";
9
9
  export {
10
10
  buildReleaseRunnerArgs,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  deploy,
3
3
  deployAppAPI
4
- } from "./chunk-5TQ3BQ2X.js";
4
+ } from "./chunk-3SON777C.js";
5
5
  import "./chunk-UGAIWMQV.js";
6
- import "./chunk-2EHJH2QU.js";
7
- import "./chunk-XZ5VCTGY.js";
8
- import "./chunk-5FGDEZSJ.js";
6
+ import "./chunk-A36PFXKK.js";
7
+ import "./chunk-PYUWPRBT.js";
8
+ import "./chunk-WZXBYKAW.js";
9
9
  import {
10
10
  syncDeps
11
- } from "./chunk-A27HZEU6.js";
11
+ } from "./chunk-COPJBRRG.js";
12
12
  import {
13
13
  loadEnv
14
14
  } from "./chunk-2CR762KB.js";
@@ -382,7 +382,7 @@ var automationMainGuardRule = {
382
382
  if (entrypoint === null) return [];
383
383
  const guard = findTopLevelMainGuard(target.source);
384
384
  if (guard === null) return [];
385
- const warning2 = {
385
+ const warning3 = {
386
386
  ruleId: "automation-main-guard",
387
387
  target,
388
388
  severity: "warning",
@@ -390,7 +390,7 @@ var automationMainGuardRule = {
390
390
  snippet: guard.snippet,
391
391
  message: `Function automations must not contain a top-level __main__ guard. Lumera invokes "${entrypoint}" automatically; remove the guard.`
392
392
  };
393
- return [warning2];
393
+ return [warning3];
394
394
  }
395
395
  };
396
396
 
@@ -407,6 +407,14 @@ function error(target, message, snippet) {
407
407
  snippet
408
408
  };
409
409
  }
410
+ function warning(target, message) {
411
+ return {
412
+ ruleId: "collection-schema",
413
+ target,
414
+ severity: "warning",
415
+ message
416
+ };
417
+ }
410
418
  var collectionSchemaRule = {
411
419
  id: "collection-schema",
412
420
  description: "Validates local collection JSON files before planning or applying resources.",
@@ -466,6 +474,19 @@ var collectionSchemaRule = {
466
474
  if (typeof field.type !== "string" || field.type.trim() === "") {
467
475
  issues.push(error(target, `Field ${typeof field.name === "string" ? `"${field.name}"` : `at index ${i}`} is missing required string field "type".`));
468
476
  }
477
+ if (field.type === "lumera_file" || field.type === "file") {
478
+ const label = typeof field.name === "string" ? `"${field.name}"` : `at index ${i}`;
479
+ const ignored = [
480
+ ...isRecord3(field.options) && field.options.maxSelect !== void 0 ? ['"options.maxSelect"'] : [],
481
+ ...field.multiple !== void 0 ? ['"multiple"'] : []
482
+ ];
483
+ if (ignored.length > 0) {
484
+ issues.push(warning(
485
+ target,
486
+ `File field ${label} sets ${ignored.join(" and ")}, which collection files ignore; it deploys with maxSelect ${field.maxSelect ?? 1}. Set top-level "maxSelect" instead. A file field's maxSelect cannot change after it is first deployed.`
487
+ ));
488
+ }
489
+ }
469
490
  }
470
491
  }
471
492
  if (audit !== void 0 && typeof audit !== "boolean") {
@@ -817,7 +838,7 @@ function error2(target, message, line) {
817
838
  ...line !== void 0 ? { line } : {}
818
839
  };
819
840
  }
820
- function warning(target, message, line) {
841
+ function warning2(target, message, line) {
821
842
  return {
822
843
  ruleId: "hook-verify",
823
844
  target,
@@ -897,7 +918,7 @@ var hookVerifyRule = {
897
918
  if (errors.length > 0) return errors;
898
919
  const advisories = [];
899
920
  if (finding.legacyHandlerPattern) {
900
- advisories.push(warning(target, LEGACY_HANDLER_MIGRATION_HINT));
921
+ advisories.push(warning2(target, LEGACY_HANDLER_MIGRATION_HINT));
901
922
  }
902
923
  let script = finding.scriptBody ?? "";
903
924
  if (ctx.appName) {
@@ -1193,16 +1214,19 @@ async function safeLintPass(label, build) {
1193
1214
  return null;
1194
1215
  }
1195
1216
  }
1196
- async function lintCollectionFiles(projectRoot, platformDir, filterName) {
1217
+ async function lintCollectionFiles(projectRoot, platformDir, filterName, printWarnings = true) {
1197
1218
  const result = await safeLintPass(
1198
1219
  "collection",
1199
1220
  async () => runLint({ projectRoot, targets: buildCollectionTargets(platformDir, filterName) })
1200
1221
  );
1201
- if (!result || result.issues.length === 0) return;
1202
- safePrintLint(result.issues, projectRoot);
1222
+ if (!result || result.issues.length === 0) return [];
1223
+ if (printWarnings || result.errors.length > 0) {
1224
+ safePrintLint(result.issues, projectRoot);
1225
+ }
1203
1226
  if (result.errors.length > 0) {
1204
1227
  throw new Error(`Found ${result.errors.length} collection lint error(s)`);
1205
1228
  }
1229
+ return result.issues;
1206
1230
  }
1207
1231
  async function lintHookFiles(api, projectRoot, platformDir, appName, filterName) {
1208
1232
  const result = await safeLintPass("hook", async () => {
@@ -2111,6 +2135,20 @@ function fieldsDiffer(local, remote) {
2111
2135
  }
2112
2136
  return false;
2113
2137
  }
2138
+ function lumeraFileMaxSelectErrors(local, remote) {
2139
+ const remoteFields = new Map(remote.schema.map((f) => [f.name, f]));
2140
+ const errors = [];
2141
+ for (const field of local.fields) {
2142
+ const deployed = remoteFields.get(field.name);
2143
+ if (mapFieldType(field.type) !== "lumera_file" || deployed?.type !== "lumera_file") continue;
2144
+ const deployedMaxSelect = Math.max(Number(normalizeFieldOption("lumera_file", "maxSelect", deployed.options?.maxSelect)), 1);
2145
+ const requestedMaxSelect = Math.max(Number(normalizeFieldOption("lumera_file", "maxSelect", field.maxSelect)), 1);
2146
+ if (deployedMaxSelect === requestedMaxSelect) continue;
2147
+ const requested = field.maxSelect === void 0 ? "omitting maxSelect (defaults to 1)" : `maxSelect ${requestedMaxSelect}`;
2148
+ errors.push(`File field "${field.name}" is deployed with maxSelect ${deployedMaxSelect}; ${requested} would change it, which is not supported. Keep maxSelect ${deployedMaxSelect}.`);
2149
+ }
2150
+ return errors;
2151
+ }
2114
2152
  function localIndexesToSql(local, appName) {
2115
2153
  if (!local.indexes || local.indexes.length === 0) return [];
2116
2154
  return local.indexes.map((idx) => buildIndexSql(local, idx, appName)).sort();
@@ -2224,13 +2262,15 @@ async function planCollections(api, localCollections, appName, projectId) {
2224
2262
  const f = remoteFieldMap.get(name);
2225
2263
  fieldDetails.push({ action: "-", name: f.name, type: f.type, required: f.required });
2226
2264
  }
2265
+ const errors = lumeraFileMaxSelectErrors(local, remote);
2227
2266
  changes.push({
2228
2267
  type: "update",
2229
2268
  resource: "collection",
2230
2269
  id: local.id,
2231
2270
  name: local.name,
2232
2271
  details: details.join(", "),
2233
- fieldDetails
2272
+ fieldDetails,
2273
+ ...errors.length > 0 ? { errors } : {}
2234
2274
  });
2235
2275
  }
2236
2276
  }
@@ -4422,8 +4462,9 @@ async function plan(args) {
4422
4462
  console.log(pc2.cyan(pc2.bold(" Plan")));
4423
4463
  console.log(pc2.dim(" Comparing local files to remote state..."));
4424
4464
  console.log();
4465
+ let collectionLintWarnings = [];
4425
4466
  if (!type || type === "collections") {
4426
- await lintCollectionFiles(projectRoot, platformDir, name || void 0);
4467
+ collectionLintWarnings = await lintCollectionFiles(projectRoot, platformDir, name || void 0, false);
4427
4468
  }
4428
4469
  if (!type || type === "hooks") {
4429
4470
  await lintHookFiles(api, projectRoot, platformDir, appName, name || void 0);
@@ -4498,6 +4539,7 @@ async function plan(args) {
4498
4539
  }
4499
4540
  }
4500
4541
  const lintWarnings = [
4542
+ ...collectionLintWarnings,
4501
4543
  ...agentLintWarnings,
4502
4544
  ...await safeLint(projectRoot, localAutomations)
4503
4545
  ];
@@ -4518,6 +4560,7 @@ async function plan(args) {
4518
4560
  safePrintLint(lintWarnings, projectRoot);
4519
4561
  return;
4520
4562
  }
4563
+ const collectionErrorCount = allChanges.reduce((count, change) => count + (change.errors?.length ?? 0), 0);
4521
4564
  if (jsonOutput) {
4522
4565
  const warnings = [];
4523
4566
  for (const change of allChanges) {
@@ -4528,8 +4571,9 @@ async function plan(args) {
4528
4571
  }
4529
4572
  }
4530
4573
  }
4531
- console.log(JSON.stringify({ changes: allChanges, warnings, validationErrors: planValidationErrors, lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
4532
- if (planValidationErrors > 0) process.exitCode = 1;
4574
+ const validationErrors = planValidationErrors + collectionErrorCount;
4575
+ console.log(JSON.stringify({ changes: allChanges, warnings, validationErrors, lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
4576
+ if (validationErrors > 0) process.exitCode = 1;
4533
4577
  return;
4534
4578
  }
4535
4579
  console.log(pc2.bold(" Changes:"));
@@ -4547,6 +4591,10 @@ async function plan(args) {
4547
4591
  }
4548
4592
  console.log();
4549
4593
  }
4594
+ if (change.errors && change.errors.length > 0) {
4595
+ for (const message of change.errors) console.log(pc2.red(` \u2717 ${message}`));
4596
+ console.log();
4597
+ }
4550
4598
  if (change.textDiffs && change.textDiffs.length > 0) {
4551
4599
  const maxDiffLines = 20;
4552
4600
  for (const td of change.textDiffs) {
@@ -4572,6 +4620,11 @@ async function plan(args) {
4572
4620
  safePrintLint(lintWarnings, projectRoot);
4573
4621
  if (planValidationErrors > 0) {
4574
4622
  console.log(pc2.red(` \u2717 Fix ${planValidationErrors} agent skill validation error${planValidationErrors === 1 ? "" : "s"} before applying.`));
4623
+ }
4624
+ if (collectionErrorCount > 0) {
4625
+ console.log(pc2.red(` \u2717 Fix ${collectionErrorCount} collection error${collectionErrorCount === 1 ? "" : "s"} before applying.`));
4626
+ }
4627
+ if (planValidationErrors > 0 || collectionErrorCount > 0) {
4575
4628
  process.exitCode = 1;
4576
4629
  } else {
4577
4630
  console.log(pc2.dim(` Run 'lumera apply' to apply these changes.`));
@@ -4679,6 +4732,15 @@ async function apply(args) {
4679
4732
  console.log();
4680
4733
  process.exit(1);
4681
4734
  }
4735
+ const collectionErrors = allChanges.flatMap((change) => (change.errors ?? []).map((message) => `${change.resource} ${change.name}: ${message}`));
4736
+ if (collectionErrors.length > 0) {
4737
+ console.log();
4738
+ for (const message of collectionErrors) console.log(pc2.red(` \u2717 ${message}`));
4739
+ console.log();
4740
+ console.log(pc2.red(` \u2717 Apply aborted: fix ${collectionErrors.length} collection error${collectionErrors.length === 1 ? "" : "s"} first. Nothing was applied.`));
4741
+ console.log();
4742
+ process.exit(1);
4743
+ }
4682
4744
  let willDeployApp = false;
4683
4745
  if (!type && !skipApp) {
4684
4746
  try {
@@ -5297,6 +5359,7 @@ export {
5297
5359
  loadLocalCollections,
5298
5360
  loadLocalSkills,
5299
5361
  localProjectSkillSlug,
5362
+ lumeraFileMaxSelectErrors,
5300
5363
  plan,
5301
5364
  planAgents,
5302
5365
  planCollections,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.35.1",
3
+ "version": "0.35.3",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {
@@ -4,7 +4,7 @@ version = "0.1.0"
4
4
  description = "{{projectTitle}} - Lumera custom app"
5
5
  requires-python = ">=3.11"
6
6
  dependencies = [
7
- "lumera[functions]>=0.45.0,<0.46.0",
7
+ "lumera[functions]>=0.47.0,<0.48.0",
8
8
  ]
9
9
 
10
10
  [project.optional-dependencies]