@lumerahq/cli 0.24.2 → 0.24.4

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
@@ -227,25 +227,25 @@ async function main() {
227
227
  switch (command) {
228
228
  // Resource commands
229
229
  case "plan":
230
- await import("./resources-A5T7SNOH.js").then((m) => m.plan(args.slice(1)));
230
+ await import("./resources-2PAJHJO7.js").then((m) => m.plan(args.slice(1)));
231
231
  break;
232
232
  case "apply":
233
- await import("./resources-A5T7SNOH.js").then((m) => m.apply(args.slice(1)));
233
+ await import("./resources-2PAJHJO7.js").then((m) => m.apply(args.slice(1)));
234
234
  break;
235
235
  case "pull":
236
- await import("./resources-A5T7SNOH.js").then((m) => m.pull(args.slice(1)));
236
+ await import("./resources-2PAJHJO7.js").then((m) => m.pull(args.slice(1)));
237
237
  break;
238
238
  case "destroy":
239
- await import("./resources-A5T7SNOH.js").then((m) => m.destroy(args.slice(1)));
239
+ await import("./resources-2PAJHJO7.js").then((m) => m.destroy(args.slice(1)));
240
240
  break;
241
241
  case "list":
242
- await import("./resources-A5T7SNOH.js").then((m) => m.list(args.slice(1)));
242
+ await import("./resources-2PAJHJO7.js").then((m) => m.list(args.slice(1)));
243
243
  break;
244
244
  case "show":
245
- await import("./resources-A5T7SNOH.js").then((m) => m.show(args.slice(1)));
245
+ await import("./resources-2PAJHJO7.js").then((m) => m.show(args.slice(1)));
246
246
  break;
247
247
  case "diff":
248
- await import("./resources-A5T7SNOH.js").then((m) => m.diff(args.slice(1)));
248
+ await import("./resources-2PAJHJO7.js").then((m) => m.diff(args.slice(1)));
249
249
  break;
250
250
  // Development
251
251
  case "dev":
@@ -10,6 +10,7 @@ import {
10
10
  import {
11
11
  ApiError,
12
12
  createApiClient,
13
+ isApiErrorStatus,
13
14
  validateCollectionNameLength
14
15
  } from "./chunk-FHHWIV4C.js";
15
16
  import {
@@ -182,6 +183,11 @@ var collectionSchemaRule = {
182
183
  }
183
184
  if (!Array.isArray(index.fields) || !index.fields.every((field) => typeof field === "string" && field.trim() !== "")) {
184
185
  issues.push(error(target, `Index at index ${i} must include a non-empty string array "fields".`));
186
+ } else if (index.fields.length === 1 && index.fields[0].trim().toLowerCase() === "updated" && index.unique !== true) {
187
+ issues.push(error(
188
+ target,
189
+ `Index at index ${i} exactly duplicates Lumera's managed non-unique "updated" index. Remove the redundant declaration.`
190
+ ));
185
191
  }
186
192
  }
187
193
  }
@@ -897,6 +903,14 @@ function stripNamespacePrefix(name, appName) {
897
903
  }
898
904
  return name;
899
905
  }
906
+ function buildIndexSql(local, idx, appName) {
907
+ const fields = idx.fields.join(", ");
908
+ const unique = idx.unique ? "UNIQUE " : "";
909
+ const physicalId = appName ? sanitizeSlugForCollectionName(appName) + NAMESPACE_SEPARATOR + local.id : local.id;
910
+ const indexTypeTag = idx.unique ? "u" : "n";
911
+ const indexName = `idx_${physicalId}_${indexTypeTag}_${idx.fields.join("_")}`;
912
+ return `CREATE ${unique}INDEX ${indexName} ON ${local.name} (${fields})`;
913
+ }
900
914
  var AGENT_IDLE_REAP_TIMEOUT_MIN_SECONDS = 10;
901
915
  var AGENT_IDLE_REAP_TIMEOUT_MAX_SECONDS = 900;
902
916
  function buildCollectionIdMap(collections, appName) {
@@ -1515,7 +1529,7 @@ function loadLocalHooks(platformDir, filterName, appName) {
1515
1529
  }
1516
1530
  return hooks;
1517
1531
  }
1518
- function convertCollectionToApiFormat(local) {
1532
+ function convertCollectionToApiFormat(local, appName) {
1519
1533
  const schema = local.fields.map((field) => {
1520
1534
  const apiField = {
1521
1535
  name: field.name,
@@ -1544,12 +1558,7 @@ function convertCollectionToApiFormat(local) {
1544
1558
  }
1545
1559
  return apiField;
1546
1560
  });
1547
- const indexes = local.indexes?.map((idx) => {
1548
- const fields = idx.fields.join(", ");
1549
- const unique = idx.unique ? "UNIQUE " : "";
1550
- const indexName = `idx_${local.id}_${idx.fields.join("_")}`;
1551
- return `CREATE ${unique}INDEX ${indexName} ON ${local.name} (${fields})`;
1552
- });
1561
+ const indexes = local.indexes?.map((idx) => buildIndexSql(local, idx, appName));
1553
1562
  return {
1554
1563
  id: local.id,
1555
1564
  name: local.name,
@@ -1622,24 +1631,15 @@ function fieldsDiffer(local, remote) {
1622
1631
  if (localMax !== remoteMax) return true;
1623
1632
  return false;
1624
1633
  }
1625
- function localIndexesToSql(local) {
1634
+ function localIndexesToSql(local, appName) {
1626
1635
  if (!local.indexes || local.indexes.length === 0) return [];
1627
- return local.indexes.map((idx) => {
1628
- const fields = idx.fields.join(", ");
1629
- const unique = idx.unique ? "UNIQUE " : "";
1630
- const indexName = `idx_${local.id}_${idx.fields.join("_")}`;
1631
- return `CREATE ${unique}INDEX ${indexName} ON ${local.name} (${fields})`;
1632
- }).sort();
1636
+ return local.indexes.map((idx) => buildIndexSql(local, idx, appName)).sort();
1633
1637
  }
1634
1638
  function normalizeRemoteIndexes(remoteIndexes) {
1635
- return remoteIndexes.filter((idx) => {
1636
- const lower = idx.toLowerCase();
1637
- if (lower.includes("external_id") || lower.includes("updated")) return false;
1638
- return true;
1639
- }).sort();
1639
+ return [...remoteIndexes].sort();
1640
1640
  }
1641
- function indexesDiffer(local, remoteIndexes) {
1642
- const localSql = localIndexesToSql(local);
1641
+ function indexesDiffer(local, remoteIndexes, appName) {
1642
+ const localSql = localIndexesToSql(local, appName);
1643
1643
  const remoteSql = normalizeRemoteIndexes(remoteIndexes);
1644
1644
  if (localSql.length !== remoteSql.length) return true;
1645
1645
  for (let i = 0; i < localSql.length; i++) {
@@ -1667,12 +1667,27 @@ function normalizeCollectionSearch(search) {
1667
1667
  } : {}
1668
1668
  });
1669
1669
  }
1670
- async function planCollections(api, localCollections) {
1670
+ async function planCollections(api, localCollections, appName) {
1671
1671
  const changes = [];
1672
1672
  const remoteCollections = await api.listCollections();
1673
1673
  const remoteById = new Map(remoteCollections.map((c) => [c.id, c]));
1674
1674
  for (const local of localCollections) {
1675
- const remote = remoteById.get(local.id);
1675
+ let remote = remoteById.get(local.id);
1676
+ if (!remote) {
1677
+ const refs = [.../* @__PURE__ */ new Set([local.id, local.name])];
1678
+ for (const ref of refs) {
1679
+ try {
1680
+ const candidate = await api.getCollection(ref);
1681
+ const isExactLegacy = !candidate.managed && !candidate.project_id && (candidate.id === local.id || candidate.name === local.name);
1682
+ if (isExactLegacy) {
1683
+ remote = candidate;
1684
+ break;
1685
+ }
1686
+ } catch (error3) {
1687
+ if (!isApiErrorStatus(error3, 404)) throw error3;
1688
+ }
1689
+ }
1690
+ }
1676
1691
  if (!remote) {
1677
1692
  changes.push({
1678
1693
  type: "create",
@@ -1706,11 +1721,12 @@ async function planCollections(api, localCollections) {
1706
1721
  modified.push(name);
1707
1722
  }
1708
1723
  }
1709
- const indexesChanged = indexesDiffer(local, remote.indexes || []);
1724
+ const indexesChanged = indexesDiffer(local, remote.indexes || [], appName);
1710
1725
  const searchChanged = normalizeCollectionSearch(local.search) !== normalizeCollectionSearch(remote.search);
1711
1726
  const auditChanged = local.audit !== void 0 && local.audit !== remote.audit;
1712
1727
  if (added.length > 0 || removed.length > 0 || modified.length > 0 || indexesChanged || searchChanged || auditChanged) {
1713
1728
  const details = [];
1729
+ if (!remote.project_id) details.push("legacy unscoped collection");
1714
1730
  if (added.length > 0) details.push(`+${added.length} field${added.length > 1 ? "s" : ""}`);
1715
1731
  if (removed.length > 0) details.push(`-${removed.length} field${removed.length > 1 ? "s" : ""}`);
1716
1732
  if (modified.length > 0) details.push(`~${modified.length} field${modified.length > 1 ? "s" : ""} (${modified.join(", ")})`);
@@ -1975,7 +1991,7 @@ async function planHooks(api, localHooks, collections, projectId) {
1975
1991
  }
1976
1992
  return changes;
1977
1993
  }
1978
- async function applyCollections(api, localCollections) {
1994
+ async function applyCollections(api, localCollections, appName) {
1979
1995
  let errors = 0;
1980
1996
  const hasRelations = localCollections.some((c) => c.fields.some((f) => f.type === "relation"));
1981
1997
  if (hasRelations) {
@@ -1992,7 +2008,7 @@ async function applyCollections(api, localCollections) {
1992
2008
  }
1993
2009
  for (const local of localCollections) {
1994
2010
  if (pass1Failed.has(local.name)) continue;
1995
- const apiFormat = convertCollectionToApiFormat(local);
2011
+ const apiFormat = convertCollectionToApiFormat(local, appName);
1996
2012
  try {
1997
2013
  await api.ensureCollection(local.name, apiFormat);
1998
2014
  console.log(pc2.green(" \u2713"), `${local.name} (schema)`);
@@ -2003,7 +2019,7 @@ async function applyCollections(api, localCollections) {
2003
2019
  }
2004
2020
  } else {
2005
2021
  for (const local of localCollections) {
2006
- const apiFormat = convertCollectionToApiFormat(local);
2022
+ const apiFormat = convertCollectionToApiFormat(local, appName);
2007
2023
  try {
2008
2024
  await api.ensureCollection(local.name, apiFormat);
2009
2025
  console.log(pc2.green(" \u2713"), `${local.name}`);
@@ -3265,7 +3281,7 @@ async function plan(args) {
3265
3281
  if (!type || type === "collections") {
3266
3282
  const localCollections = loadLocalCollections(platformDir, name || void 0);
3267
3283
  if (localCollections.length > 0) {
3268
- const changes = await planCollections(api, localCollections);
3284
+ const changes = await planCollections(api, localCollections, appName);
3269
3285
  allChanges.push(...changes);
3270
3286
  }
3271
3287
  }
@@ -3412,7 +3428,7 @@ async function apply(args) {
3412
3428
  const localHooks = !type || type === "hooks" ? loadLocalHooks(platformDir, name || void 0, appName) : [];
3413
3429
  const localAgents = !type || type === "agents" ? loadLocalAgents(platformDir, name || void 0, appName) : [];
3414
3430
  const localMailboxes = !type || type === "mailboxes" ? loadLocalMailboxes(platformDir, name || void 0) : [];
3415
- if (localCollections.length > 0) allChanges.push(...await planCollections(api, localCollections));
3431
+ if (localCollections.length > 0) allChanges.push(...await planCollections(api, localCollections, appName));
3416
3432
  if (localAutomations.length > 0) allChanges.push(...await planAutomations(api, localAutomations, projectId));
3417
3433
  if (localHooks.length > 0) allChanges.push(...await planHooks(api, localHooks, collections, projectId));
3418
3434
  if (localAgents.length > 0) allChanges.push(...await planAgents(api, localAgents, projectId));
@@ -3487,7 +3503,7 @@ async function apply(args) {
3487
3503
  let totalSkipped = 0;
3488
3504
  if (localCollections.length > 0) {
3489
3505
  console.log(pc2.bold(" Collections:"));
3490
- totalErrors += await applyCollections(api, localCollections);
3506
+ totalErrors += await applyCollections(api, localCollections, appName);
3491
3507
  console.log();
3492
3508
  }
3493
3509
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.24.2",
3
+ "version": "0.24.4",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {