@lumerahq/cli 0.22.1 → 0.22.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.
@@ -95,7 +95,7 @@ async function deploy(options) {
95
95
  console.log(pc.dim("Ensuring app record..."));
96
96
  await ensureAppRecord(apiUrl, token, appName, appTitle);
97
97
  console.log(pc.cyan(`Deploying ${appTitle}...`));
98
- const version = options.version || resolveVersion(dirname(distDir));
98
+ const version = options.version || process.env.LUMERA_APP_VERSION?.trim() || resolveVersion(dirname(distDir));
99
99
  console.log(pc.dim(`Version: ${version}`));
100
100
  const tarball = await createTarball(distDir);
101
101
  console.log(pc.dim(`Package: ${(tarball.length / 1024).toFixed(1)} KB`));
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  dev
3
- } from "./chunk-A55Q4P2T.js";
3
+ } from "./chunk-WV5HOQNX.js";
4
4
  import {
5
5
  syncDeps
6
6
  } from "./chunk-3ESHIPZH.js";
package/dist/index.js CHANGED
@@ -226,29 +226,29 @@ async function main() {
226
226
  switch (command) {
227
227
  // Resource commands
228
228
  case "plan":
229
- await import("./resources-W7TYJOQN.js").then((m) => m.plan(args.slice(1)));
229
+ await import("./resources-DP24VKSG.js").then((m) => m.plan(args.slice(1)));
230
230
  break;
231
231
  case "apply":
232
- await import("./resources-W7TYJOQN.js").then((m) => m.apply(args.slice(1)));
232
+ await import("./resources-DP24VKSG.js").then((m) => m.apply(args.slice(1)));
233
233
  break;
234
234
  case "pull":
235
- await import("./resources-W7TYJOQN.js").then((m) => m.pull(args.slice(1)));
235
+ await import("./resources-DP24VKSG.js").then((m) => m.pull(args.slice(1)));
236
236
  break;
237
237
  case "destroy":
238
- await import("./resources-W7TYJOQN.js").then((m) => m.destroy(args.slice(1)));
238
+ await import("./resources-DP24VKSG.js").then((m) => m.destroy(args.slice(1)));
239
239
  break;
240
240
  case "list":
241
- await import("./resources-W7TYJOQN.js").then((m) => m.list(args.slice(1)));
241
+ await import("./resources-DP24VKSG.js").then((m) => m.list(args.slice(1)));
242
242
  break;
243
243
  case "show":
244
- await import("./resources-W7TYJOQN.js").then((m) => m.show(args.slice(1)));
244
+ await import("./resources-DP24VKSG.js").then((m) => m.show(args.slice(1)));
245
245
  break;
246
246
  case "diff":
247
- await import("./resources-W7TYJOQN.js").then((m) => m.diff(args.slice(1)));
247
+ await import("./resources-DP24VKSG.js").then((m) => m.diff(args.slice(1)));
248
248
  break;
249
249
  // Development
250
250
  case "dev":
251
- await import("./dev-GQKLR7AK.js").then((m) => m.dev(args.slice(1)));
251
+ await import("./dev-UTPD4MN5.js").then((m) => m.dev(args.slice(1)));
252
252
  break;
253
253
  case "run":
254
254
  await import("./run-DJXAXDD2.js").then((m) => m.run(args.slice(1)));
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  deploy
3
- } from "./chunk-A55Q4P2T.js";
3
+ } from "./chunk-WV5HOQNX.js";
4
4
  import {
5
5
  syncDeps
6
6
  } from "./chunk-3ESHIPZH.js";
@@ -125,6 +125,15 @@ var collectionSchemaRule = {
125
125
  const name = parsed.name;
126
126
  const fields = parsed.fields;
127
127
  const indexes = parsed.indexes;
128
+ const search = parsed.search;
129
+ const localFieldTypes = /* @__PURE__ */ new Map();
130
+ if (Array.isArray(fields)) {
131
+ for (const field of fields) {
132
+ if (isRecord2(field) && typeof field.name === "string") {
133
+ localFieldTypes.set(field.name, field.type);
134
+ }
135
+ }
136
+ }
128
137
  if (typeof id !== "string" || id.trim() === "") {
129
138
  issues.push(error(target, 'Missing required string field "id".'));
130
139
  }
@@ -173,6 +182,56 @@ var collectionSchemaRule = {
173
182
  }
174
183
  }
175
184
  }
185
+ if (search !== void 0) {
186
+ if (!isRecord2(search)) {
187
+ issues.push(error(target, 'Optional field "search" must be an object when present.'));
188
+ } else {
189
+ const unknownSearchKeys = Object.keys(search).filter((key) => key !== "fulltext");
190
+ for (const key of unknownSearchKeys) {
191
+ issues.push(error(target, `Unknown search option "${key}".`));
192
+ }
193
+ const fulltext = search.fulltext;
194
+ if (!isRecord2(fulltext)) {
195
+ issues.push(error(target, '"search.fulltext" must be an object.'));
196
+ } else {
197
+ const allowed = /* @__PURE__ */ new Set(["fields", "language", "fuzzy_fields"]);
198
+ for (const key of Object.keys(fulltext).filter((key2) => !allowed.has(key2))) {
199
+ issues.push(error(target, `Unknown search.fulltext option "${key}".`));
200
+ }
201
+ if (!isRecord2(fulltext.fields) || Object.keys(fulltext.fields).length === 0) {
202
+ issues.push(error(target, '"search.fulltext.fields" must be a non-empty field-to-weight object.'));
203
+ } else {
204
+ if (Object.keys(fulltext.fields).length > 8) {
205
+ issues.push(error(target, '"search.fulltext.fields" cannot contain more than 8 fields.'));
206
+ }
207
+ for (const [field, weight] of Object.entries(fulltext.fields)) {
208
+ if (!["A", "B", "C", "D"].includes(String(weight))) {
209
+ issues.push(error(target, `Search field "${field}" must use weight A, B, C, or D.`));
210
+ }
211
+ const type = localFieldTypes.get(field);
212
+ if (type !== "text" && type !== "editor") {
213
+ issues.push(error(target, `Search field "${field}" must name a text or editor field.`));
214
+ }
215
+ }
216
+ }
217
+ if (fulltext.language !== void 0 && fulltext.language !== "english" && fulltext.language !== "simple") {
218
+ issues.push(error(target, '"search.fulltext.language" must be "english" or "simple".'));
219
+ }
220
+ if (fulltext.fuzzy_fields !== void 0) {
221
+ if (!Array.isArray(fulltext.fuzzy_fields) || !fulltext.fuzzy_fields.every((field) => typeof field === "string")) {
222
+ issues.push(error(target, '"search.fulltext.fuzzy_fields" must be an array of field names.'));
223
+ } else {
224
+ for (const field of fulltext.fuzzy_fields) {
225
+ const type = localFieldTypes.get(field);
226
+ if (type !== "text" && type !== "editor") {
227
+ issues.push(error(target, `Fuzzy search field "${field}" must name a text or editor field.`));
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
176
235
  return issues;
177
236
  }
178
237
  };
@@ -1455,7 +1514,7 @@ function convertCollectionToApiFormat(local) {
1455
1514
  const indexName = `idx_${local.id}_${idx.fields.join("_")}`;
1456
1515
  return `CREATE ${unique}INDEX ${indexName} ON ${local.name} (${fields})`;
1457
1516
  });
1458
- return { id: local.id, name: local.name, schema, indexes };
1517
+ return { id: local.id, name: local.name, schema, indexes, search: local.search ?? null };
1459
1518
  }
1460
1519
  function mapFieldType(type) {
1461
1520
  const typeMap = {
@@ -1545,6 +1604,19 @@ function indexesDiffer(local, remoteIndexes) {
1545
1604
  }
1546
1605
  return false;
1547
1606
  }
1607
+ function normalizeCollectionSearch(search) {
1608
+ if (!search) return "null";
1609
+ const fields = Object.fromEntries(
1610
+ Object.entries(search.fulltext.fields).sort(([left], [right]) => left.localeCompare(right))
1611
+ );
1612
+ return JSON.stringify({
1613
+ fulltext: {
1614
+ fields,
1615
+ language: search.fulltext.language ?? "english",
1616
+ fuzzy_fields: [...search.fulltext.fuzzy_fields ?? []].sort()
1617
+ }
1618
+ });
1619
+ }
1548
1620
  async function planCollections(api, localCollections) {
1549
1621
  const changes = [];
1550
1622
  const remoteCollections = await api.listCollections();
@@ -1582,12 +1654,14 @@ async function planCollections(api, localCollections) {
1582
1654
  }
1583
1655
  }
1584
1656
  const indexesChanged = indexesDiffer(local, remote.indexes || []);
1585
- if (added.length > 0 || removed.length > 0 || modified.length > 0 || indexesChanged) {
1657
+ const searchChanged = normalizeCollectionSearch(local.search) !== normalizeCollectionSearch(remote.search);
1658
+ if (added.length > 0 || removed.length > 0 || modified.length > 0 || indexesChanged || searchChanged) {
1586
1659
  const details = [];
1587
1660
  if (added.length > 0) details.push(`+${added.length} field${added.length > 1 ? "s" : ""}`);
1588
1661
  if (removed.length > 0) details.push(`-${removed.length} field${removed.length > 1 ? "s" : ""}`);
1589
1662
  if (modified.length > 0) details.push(`~${modified.length} field${modified.length > 1 ? "s" : ""} (${modified.join(", ")})`);
1590
1663
  if (indexesChanged) details.push("indexes changed");
1664
+ if (searchChanged) details.push("search changed");
1591
1665
  const fieldDetails = [];
1592
1666
  for (const name of added) {
1593
1667
  const f = localFieldMap.get(name);
@@ -2099,7 +2173,8 @@ async function pullCollections(api, platformDir, filterName, appName) {
2099
2173
  return { fields: match[2].split(",").map((f) => f.trim()), unique: !!match[1] };
2100
2174
  }
2101
2175
  return null;
2102
- }).filter((idx) => idx !== null)
2176
+ }).filter((idx) => idx !== null),
2177
+ search: collection.search
2103
2178
  };
2104
2179
  const fileName = toSafeFilename(localName);
2105
2180
  const filePath = join2(collectionsDir, `${fileName}.json`);
@@ -3375,6 +3450,14 @@ async function apply(args) {
3375
3450
  totalErrors += await applyMailboxes(api, localMailboxes);
3376
3451
  console.log();
3377
3452
  }
3453
+ if (totalErrors > 0) {
3454
+ console.log(pc2.red(` \u2717 ${totalErrors} error${totalErrors > 1 ? "s" : ""} during apply.`));
3455
+ if (willDeployApp) {
3456
+ console.log(pc2.dim(" App deploy skipped because resource apply failed."));
3457
+ }
3458
+ console.log();
3459
+ process.exit(1);
3460
+ }
3378
3461
  if (willDeployApp) {
3379
3462
  console.log(pc2.bold(" App:"));
3380
3463
  await applyApp(args);
@@ -3382,11 +3465,6 @@ async function apply(args) {
3382
3465
  }
3383
3466
  totalCreated = creates.length;
3384
3467
  totalUpdated = updates.length;
3385
- if (totalErrors > 0) {
3386
- console.log(pc2.red(` \u2717 ${totalErrors} error${totalErrors > 1 ? "s" : ""} during apply.`));
3387
- console.log();
3388
- process.exit(1);
3389
- }
3390
3468
  const summary = [];
3391
3469
  if (totalCreated > 0) summary.push(pc2.green(`${totalCreated} created`));
3392
3470
  if (totalUpdated > 0) summary.push(pc2.yellow(`${totalUpdated} updated`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.22.1",
3
+ "version": "0.22.3",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {
@@ -24,7 +24,7 @@
24
24
  "check:ci": "biome check . && tsr generate && tsc --noEmit"
25
25
  },
26
26
  "dependencies": {
27
- "@lumerahq/ui": "^0.11.1",
27
+ "@lumerahq/ui": "^0.11.2",
28
28
  "@tanstack/react-query": "^5.90.11",
29
29
  "@tanstack/react-router": "1.155.0",
30
30
  "clsx": "^2.1.1",