@lumerahq/cli 0.19.20 → 0.19.22

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.
@@ -7,6 +7,55 @@ import {
7
7
  fetchWithRetry
8
8
  } from "./chunk-FJFIWC7G.js";
9
9
 
10
+ // src/lib/name-limits.ts
11
+ var MAX_PROJECT_NAME_LENGTH = 30;
12
+ var MAX_COLLECTION_NAME_LENGTH = 30;
13
+ var MAX_POSTGRES_IDENTIFIER_BYTES = 63;
14
+ var PROJECT_EXTERNAL_ID_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
15
+ var RESERVED_PROJECT_EXTERNAL_IDS = /* @__PURE__ */ new Set([
16
+ "collection",
17
+ "collections",
18
+ "default",
19
+ "admin",
20
+ "api",
21
+ "system",
22
+ "internal",
23
+ "platform",
24
+ "lumera",
25
+ "lm"
26
+ ]);
27
+ function lengthOf(value) {
28
+ return Array.from(value).length;
29
+ }
30
+ function validateProjectNameLength(name, label = "App name") {
31
+ if (lengthOf(name.trim()) > MAX_PROJECT_NAME_LENGTH) {
32
+ return `${label} must be ${MAX_PROJECT_NAME_LENGTH} characters or less`;
33
+ }
34
+ return null;
35
+ }
36
+ function validateProjectExternalId(externalId) {
37
+ const trimmed = externalId.trim();
38
+ const lengthError = validateProjectNameLength(trimmed, "App ID");
39
+ if (lengthError) return lengthError;
40
+ if (!PROJECT_EXTERNAL_ID_REGEX.test(trimmed)) {
41
+ return "App ID must use lowercase letters, numbers, and hyphens only";
42
+ }
43
+ if (RESERVED_PROJECT_EXTERNAL_IDS.has(trimmed)) {
44
+ return `App ID "${trimmed}" is a reserved name`;
45
+ }
46
+ return null;
47
+ }
48
+ function validateCollectionNameLength(name) {
49
+ const trimmed = name.trim();
50
+ if (lengthOf(trimmed) > MAX_COLLECTION_NAME_LENGTH) {
51
+ return `collection name must be ${MAX_COLLECTION_NAME_LENGTH} characters or less`;
52
+ }
53
+ if (new TextEncoder().encode(trimmed).byteLength > MAX_POSTGRES_IDENTIFIER_BYTES) {
54
+ return `collection name must be ${MAX_POSTGRES_IDENTIFIER_BYTES} bytes or less`;
55
+ }
56
+ return null;
57
+ }
58
+
10
59
  // src/lib/api.ts
11
60
  init_auth();
12
61
  import { readFileSync } from "fs";
@@ -282,6 +331,10 @@ var ApiClient = class {
282
331
  // Projects — register/lookup via PocketBase records API
283
332
  async registerProject(name, externalId) {
284
333
  const extId = externalId || name;
334
+ const nameError = validateProjectNameLength(name, "App name");
335
+ if (nameError) throw new Error(nameError);
336
+ const externalIdError = validateProjectExternalId(extId);
337
+ if (externalIdError) throw new Error(externalIdError);
285
338
  const existing = await this.getProjectByExternalId(extId);
286
339
  if (existing) {
287
340
  return existing;
@@ -364,6 +417,9 @@ function createApiClient(token, baseUrl, projectExternalId) {
364
417
  }
365
418
 
366
419
  export {
420
+ validateProjectNameLength,
421
+ validateProjectExternalId,
422
+ validateCollectionNameLength,
367
423
  userContextHeaders,
368
424
  ApiError,
369
425
  isApiErrorStatus,
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  isApiErrorStatus
7
- } from "./chunk-ML7XB7Z2.js";
7
+ } from "./chunk-JMRD5WIY.js";
8
8
  import {
9
9
  findProjectRoot,
10
10
  getAppName
@@ -1,6 +1,8 @@
1
1
  import {
2
- userContextHeaders
3
- } from "./chunk-ML7XB7Z2.js";
2
+ userContextHeaders,
3
+ validateProjectExternalId,
4
+ validateProjectNameLength
5
+ } from "./chunk-JMRD5WIY.js";
4
6
  import {
5
7
  fetchWithRetry
6
8
  } from "./chunk-FJFIWC7G.js";
@@ -63,6 +65,10 @@ async function createTarball(distDir) {
63
65
  });
64
66
  }
65
67
  async function ensureAppRecord(apiBase, token, appName, appTitle) {
68
+ const appIdError = validateProjectExternalId(appName);
69
+ if (appIdError) throw new Error(appIdError);
70
+ const appNameError = validateProjectNameLength(appTitle, "App name");
71
+ if (appNameError) throw new Error(appNameError);
66
72
  const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName }));
67
73
  const searchRes = await fetchWithRetry(
68
74
  `${apiBase}/pb/collections/lm_custom_apps/records?filter=${filterParam}`,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deps,
3
3
  syncDeps
4
- } from "./chunk-S7FJU4GM.js";
4
+ } from "./chunk-RH4FQF6O.js";
5
5
  import "./chunk-2CR762KB.js";
6
- import "./chunk-ML7XB7Z2.js";
6
+ import "./chunk-JMRD5WIY.js";
7
7
  import "./chunk-ZH3NVYEQ.js";
8
8
  import "./chunk-FJFIWC7G.js";
9
9
  import "./chunk-PNKVD2UK.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  dev
3
- } from "./chunk-PY4J2U2M.js";
3
+ } from "./chunk-XT4EGGK7.js";
4
4
  import {
5
5
  syncDeps
6
- } from "./chunk-S7FJU4GM.js";
6
+ } from "./chunk-RH4FQF6O.js";
7
7
  import {
8
8
  loadEnv
9
9
  } from "./chunk-2CR762KB.js";
10
10
  import {
11
11
  createApiClient
12
- } from "./chunk-ML7XB7Z2.js";
12
+ } from "./chunk-JMRD5WIY.js";
13
13
  import {
14
14
  findProjectRoot,
15
15
  getApiUrl,
package/dist/index.js CHANGED
@@ -219,39 +219,39 @@ async function main() {
219
219
  switch (command) {
220
220
  // Resource commands
221
221
  case "plan":
222
- await import("./resources-FLFD64X2.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.plan(args.slice(1)));
223
223
  break;
224
224
  case "apply":
225
- await import("./resources-FLFD64X2.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.apply(args.slice(1)));
226
226
  break;
227
227
  case "pull":
228
- await import("./resources-FLFD64X2.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.pull(args.slice(1)));
229
229
  break;
230
230
  case "destroy":
231
- await import("./resources-FLFD64X2.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.destroy(args.slice(1)));
232
232
  break;
233
233
  case "list":
234
- await import("./resources-FLFD64X2.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.list(args.slice(1)));
235
235
  break;
236
236
  case "show":
237
- await import("./resources-FLFD64X2.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.show(args.slice(1)));
238
238
  break;
239
239
  case "diff":
240
- await import("./resources-FLFD64X2.js").then((m) => m.diff(args.slice(1)));
240
+ await import("./resources-Z5ZMPWTK.js").then((m) => m.diff(args.slice(1)));
241
241
  break;
242
242
  // Development
243
243
  case "dev":
244
- await import("./dev-O6TXHGAK.js").then((m) => m.dev(args.slice(1)));
244
+ await import("./dev-EQK27BUM.js").then((m) => m.dev(args.slice(1)));
245
245
  break;
246
246
  case "run":
247
- await import("./run-NR3725J2.js").then((m) => m.run(args.slice(1)));
247
+ await import("./run-LHHX7UBN.js").then((m) => m.run(args.slice(1)));
248
248
  break;
249
249
  // Project
250
250
  case "init":
251
- await import("./init-HUV5ZJAE.js").then((m) => m.init(args.slice(1)));
251
+ await import("./init-UF4JYQSQ.js").then((m) => m.init(args.slice(1)));
252
252
  break;
253
253
  case "register":
254
- await import("./register-OZDOFUPF.js").then((m) => m.register(args.slice(1)));
254
+ await import("./register-BQCVUPWS.js").then((m) => m.register(args.slice(1)));
255
255
  break;
256
256
  case "templates":
257
257
  await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
@@ -268,7 +268,7 @@ async function main() {
268
268
  break;
269
269
  // Dependencies
270
270
  case "deps":
271
- await import("./deps-H4QYLGM7.js").then((m) => m.deps(args.slice(1)));
271
+ await import("./deps-VTLXKCQ7.js").then((m) => m.deps(args.slice(1)));
272
272
  break;
273
273
  // Auth
274
274
  case "login":
@@ -6,8 +6,9 @@ import {
6
6
  spinner
7
7
  } from "./chunk-BHYDYR75.js";
8
8
  import {
9
- createApiClient
10
- } from "./chunk-ML7XB7Z2.js";
9
+ createApiClient,
10
+ validateProjectNameLength
11
+ } from "./chunk-JMRD5WIY.js";
11
12
  import {
12
13
  getToken,
13
14
  init_auth,
@@ -280,6 +281,8 @@ async function init(args) {
280
281
  initial: "my-app",
281
282
  validate: (value) => {
282
283
  if (!value) return "Project name is required";
284
+ const lengthError = validateProjectNameLength(value, "Project name");
285
+ if (lengthError) return lengthError;
283
286
  if (!/^[a-z0-9-]+$/.test(value)) {
284
287
  return "Use lowercase letters, numbers, and hyphens only";
285
288
  }
@@ -301,6 +304,11 @@ async function init(args) {
301
304
  console.log(pc.red(" Error: Project name must use lowercase letters, numbers, and hyphens only"));
302
305
  process.exit(1);
303
306
  }
307
+ const projectNameLengthError = validateProjectNameLength(finalProjectName, "Project name");
308
+ if (projectNameLengthError) {
309
+ console.log(pc.red(` Error: ${projectNameLengthError}`));
310
+ process.exit(1);
311
+ }
304
312
  if (!directory) {
305
313
  if (nonInteractive) {
306
314
  directory = finalProjectName;
@@ -5,8 +5,9 @@ import {
5
5
  spinner
6
6
  } from "./chunk-BHYDYR75.js";
7
7
  import {
8
- createApiClient
9
- } from "./chunk-ML7XB7Z2.js";
8
+ createApiClient,
9
+ validateProjectExternalId
10
+ } from "./chunk-JMRD5WIY.js";
10
11
  import {
11
12
  findProjectRoot,
12
13
  getAppName,
@@ -45,6 +46,11 @@ async function register(args) {
45
46
  const projectRoot = findProjectRoot();
46
47
  loadEnv(projectRoot);
47
48
  const appName = getAppName(projectRoot);
49
+ const appNameError = validateProjectExternalId(appName);
50
+ if (appNameError) {
51
+ console.error(pc.red(` ${appNameError}`));
52
+ process.exit(1);
53
+ }
48
54
  const existingId = getProjectId(projectRoot);
49
55
  if (existingId && !force) {
50
56
  console.log();
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  deploy
3
- } from "./chunk-PY4J2U2M.js";
3
+ } from "./chunk-XT4EGGK7.js";
4
4
  import {
5
5
  syncDeps
6
- } from "./chunk-S7FJU4GM.js";
6
+ } from "./chunk-RH4FQF6O.js";
7
7
  import {
8
8
  loadEnv
9
9
  } from "./chunk-2CR762KB.js";
10
10
  import {
11
11
  ApiError,
12
- createApiClient
13
- } from "./chunk-ML7XB7Z2.js";
12
+ createApiClient,
13
+ validateCollectionNameLength
14
+ } from "./chunk-JMRD5WIY.js";
14
15
  import {
15
16
  findProjectRoot,
16
17
  getApiUrl,
@@ -1261,6 +1262,11 @@ function loadLocalCollections(platformDir, filterName) {
1261
1262
  errors.push(`${file}: missing name field`);
1262
1263
  continue;
1263
1264
  }
1265
+ const collectionNameError = validateCollectionNameLength(collection.name);
1266
+ if (collectionNameError) {
1267
+ errors.push(`${file}: ${collectionNameError}`);
1268
+ continue;
1269
+ }
1264
1270
  if (/\s/.test(collection.name)) {
1265
1271
  errors.push(`${file}: collection name "${collection.name}" contains spaces - use underscores instead`);
1266
1272
  continue;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-ML7XB7Z2.js";
6
+ } from "./chunk-JMRD5WIY.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getApiUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.19.20",
3
+ "version": "0.19.22",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {
@@ -12,8 +12,8 @@ importers:
12
12
  .:
13
13
  dependencies:
14
14
  '@lumerahq/ui':
15
- specifier: ^0.9.1
16
- version: 0.9.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
15
+ specifier: ^0.10.0
16
+ version: 0.10.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
17
17
  '@tanstack/react-query':
18
18
  specifier: ^5.90.11
19
19
  version: 5.101.0(react@19.2.7)
@@ -457,8 +457,8 @@ packages:
457
457
  '@jridgewell/trace-mapping@0.3.31':
458
458
  resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
459
459
 
460
- '@lumerahq/ui@0.9.1':
461
- resolution: {integrity: sha512-Mp/Yg5kD71UMniFNchZF7t3E9pz2+G+cUiXOdVgyjMNfvmsVnOH5/jQ5ytNWEol01mVa1QAbo1rCHU78fB/Edw==}
460
+ '@lumerahq/ui@0.10.0':
461
+ resolution: {integrity: sha512-dWz7J9jXJBtHiAy7SFWWWhsoLOQ8TXVu7/FWvkW3wIkeTWRUdDreIaYIrOhpnvkH+PrTafiLGybSGrLQP+knSw==}
462
462
  peerDependencies:
463
463
  react: ^18.0.0 || ^19.0.0
464
464
  react-dom: ^18.0.0 || ^19.0.0
@@ -2311,7 +2311,7 @@ snapshots:
2311
2311
  '@jridgewell/resolve-uri': 3.1.2
2312
2312
  '@jridgewell/sourcemap-codec': 1.5.5
2313
2313
 
2314
- '@lumerahq/ui@0.9.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
2314
+ '@lumerahq/ui@0.10.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
2315
2315
  dependencies:
2316
2316
  '@base-ui/react': 1.5.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
2317
2317
  '@tanstack/react-query': 5.101.0(react@19.2.7)