@lumerahq/cli 0.19.8 → 0.19.9-dev.1

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.
@@ -195,6 +195,12 @@ var ApiClient = class {
195
195
  body: JSON.stringify(def)
196
196
  });
197
197
  }
198
+ async compileHook(script) {
199
+ await this.request("/api/pb/hooks/compile", {
200
+ method: "POST",
201
+ body: JSON.stringify({ script })
202
+ });
203
+ }
198
204
  async updateHook(id, def) {
199
205
  return this.request(`/api/pb/hooks/${id}`, {
200
206
  method: "PATCH",
@@ -355,6 +361,7 @@ function createApiClient(token, baseUrl, projectExternalId) {
355
361
  }
356
362
 
357
363
  export {
364
+ ApiError,
358
365
  isApiErrorStatus,
359
366
  createApiClient
360
367
  };
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  isApiErrorStatus
7
- } from "./chunk-SMZESQV2.js";
7
+ } from "./chunk-EDRAYUWN.js";
8
8
  import {
9
9
  findProjectRoot,
10
10
  getAppName
@@ -2,9 +2,9 @@ import {
2
2
  deps,
3
3
  projectResourceDepsEnabled,
4
4
  syncDeps
5
- } from "./chunk-2WDZ3QKS.js";
5
+ } from "./chunk-WWEIOMW6.js";
6
6
  import "./chunk-2CR762KB.js";
7
- import "./chunk-SMZESQV2.js";
7
+ import "./chunk-EDRAYUWN.js";
8
8
  import "./chunk-ZH3NVYEQ.js";
9
9
  import "./chunk-FJFIWC7G.js";
10
10
  import "./chunk-PNKVD2UK.js";
@@ -4,13 +4,13 @@ import {
4
4
  import {
5
5
  projectResourceDepsEnabled,
6
6
  syncDeps
7
- } from "./chunk-2WDZ3QKS.js";
7
+ } from "./chunk-WWEIOMW6.js";
8
8
  import {
9
9
  loadEnv
10
10
  } from "./chunk-2CR762KB.js";
11
11
  import {
12
12
  createApiClient
13
- } from "./chunk-SMZESQV2.js";
13
+ } from "./chunk-EDRAYUWN.js";
14
14
  import {
15
15
  findProjectRoot,
16
16
  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-EPCEYCFY.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-5ELZGJOH.js").then((m) => m.plan(args.slice(1)));
223
223
  break;
224
224
  case "apply":
225
- await import("./resources-EPCEYCFY.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-5ELZGJOH.js").then((m) => m.apply(args.slice(1)));
226
226
  break;
227
227
  case "pull":
228
- await import("./resources-EPCEYCFY.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-5ELZGJOH.js").then((m) => m.pull(args.slice(1)));
229
229
  break;
230
230
  case "destroy":
231
- await import("./resources-EPCEYCFY.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-5ELZGJOH.js").then((m) => m.destroy(args.slice(1)));
232
232
  break;
233
233
  case "list":
234
- await import("./resources-EPCEYCFY.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-5ELZGJOH.js").then((m) => m.list(args.slice(1)));
235
235
  break;
236
236
  case "show":
237
- await import("./resources-EPCEYCFY.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-5ELZGJOH.js").then((m) => m.show(args.slice(1)));
238
238
  break;
239
239
  case "diff":
240
- await import("./resources-EPCEYCFY.js").then((m) => m.diff(args.slice(1)));
240
+ await import("./resources-5ELZGJOH.js").then((m) => m.diff(args.slice(1)));
241
241
  break;
242
242
  // Development
243
243
  case "dev":
244
- await import("./dev-RENAXSGD.js").then((m) => m.dev(args.slice(1)));
244
+ await import("./dev-3SDNIPWA.js").then((m) => m.dev(args.slice(1)));
245
245
  break;
246
246
  case "run":
247
- await import("./run-XWXUBWWH.js").then((m) => m.run(args.slice(1)));
247
+ await import("./run-YUL73K5O.js").then((m) => m.run(args.slice(1)));
248
248
  break;
249
249
  // Project
250
250
  case "init":
251
- await import("./init-J5BNFCSP.js").then((m) => m.init(args.slice(1)));
251
+ await import("./init-FW4RFXLL.js").then((m) => m.init(args.slice(1)));
252
252
  break;
253
253
  case "register":
254
- await import("./register-ZYUFXURK.js").then((m) => m.register(args.slice(1)));
254
+ await import("./register-HR2QQFWX.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-53AOYHH2.js").then((m) => m.deps(args.slice(1)));
271
+ await import("./deps-WSZGH35V.js").then((m) => m.deps(args.slice(1)));
272
272
  break;
273
273
  // Auth
274
274
  case "login":
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-BHYDYR75.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-SMZESQV2.js";
10
+ } from "./chunk-EDRAYUWN.js";
11
11
  import {
12
12
  getToken,
13
13
  init_auth,
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-BHYDYR75.js";
7
7
  import {
8
8
  createApiClient
9
- } from "./chunk-SMZESQV2.js";
9
+ } from "./chunk-EDRAYUWN.js";
10
10
  import {
11
11
  findProjectRoot,
12
12
  getAppName,
@@ -4,13 +4,14 @@ import {
4
4
  import {
5
5
  projectResourceDepsEnabled,
6
6
  syncDeps
7
- } from "./chunk-2WDZ3QKS.js";
7
+ } from "./chunk-WWEIOMW6.js";
8
8
  import {
9
9
  loadEnv
10
10
  } from "./chunk-2CR762KB.js";
11
11
  import {
12
+ ApiError,
12
13
  createApiClient
13
- } from "./chunk-SMZESQV2.js";
14
+ } from "./chunk-EDRAYUWN.js";
14
15
  import {
15
16
  findProjectRoot,
16
17
  getApiUrl,
@@ -119,6 +120,212 @@ var collectionSchemaRule = {
119
120
  }
120
121
  };
121
122
 
123
+ // src/lib/hooks-parse.ts
124
+ var VALID_HOOK_TRIGGERS = [
125
+ "before_create",
126
+ "after_create",
127
+ "before_update",
128
+ "after_update",
129
+ "before_delete",
130
+ "after_delete"
131
+ ];
132
+ var KNOWN_HOOK_CONFIG_KEYS = /* @__PURE__ */ new Set([
133
+ "external_id",
134
+ "collection",
135
+ "trigger",
136
+ "name",
137
+ "enabled",
138
+ "metadata"
139
+ ]);
140
+ var CONFIG_BLOCK_RE = /export\s+const\s+config\s*[=:]\s*(\{[\s\S]*?\});?/;
141
+ function extractConfigBody(content) {
142
+ const m = content.match(CONFIG_BLOCK_RE);
143
+ return m ? m[1] : null;
144
+ }
145
+ function extractTopLevelConfigKeys(configBody) {
146
+ if (!configBody.startsWith("{") || !configBody.endsWith("}")) return [];
147
+ const inner = configBody.slice(1, -1);
148
+ let stripped = "";
149
+ let depth = 0;
150
+ let inString = null;
151
+ let prev = "";
152
+ for (const ch of inner) {
153
+ if (inString) {
154
+ if (ch === inString && prev !== "\\") inString = null;
155
+ prev = ch;
156
+ continue;
157
+ }
158
+ if (ch === '"' || ch === "'" || ch === "`") {
159
+ inString = ch;
160
+ prev = ch;
161
+ continue;
162
+ }
163
+ if (ch === "{" || ch === "[") {
164
+ depth++;
165
+ prev = ch;
166
+ continue;
167
+ }
168
+ if (ch === "}" || ch === "]") {
169
+ depth--;
170
+ prev = ch;
171
+ continue;
172
+ }
173
+ if (depth === 0) stripped += ch;
174
+ prev = ch;
175
+ }
176
+ const keys = [];
177
+ const keyRe = /(?:^|,)\s*(?:['"]([^'"]+)['"]|(\w+))\s*:/g;
178
+ let m;
179
+ while ((m = keyRe.exec(stripped)) !== null) {
180
+ keys.push(m[1] || m[2]);
181
+ }
182
+ return keys;
183
+ }
184
+ function parseHookConfig(content) {
185
+ const configMatch = content.match(CONFIG_BLOCK_RE);
186
+ if (!configMatch) return null;
187
+ const externalId = configMatch[1].match(/external_id\s*:\s*['"]([^'"]+)['"]/)?.[1];
188
+ const collection = configMatch[1].match(/collection\s*:\s*['"]([^'"]+)['"]/)?.[1];
189
+ const trigger = configMatch[1].match(/trigger\s*:\s*['"]([^'"]+)['"]/)?.[1];
190
+ const enabled = configMatch[1].match(/enabled\s*:\s*(true|false)/)?.[1];
191
+ const name = configMatch[1].match(/name\s*:\s*['"]([^'"]+)['"]/)?.[1];
192
+ if (!collection || !trigger) return null;
193
+ const hook = {
194
+ external_id: externalId || "",
195
+ collection,
196
+ trigger,
197
+ enabled: enabled !== "false"
198
+ };
199
+ if (name) hook.name = name;
200
+ const metadataMatch = configMatch[1].match(/metadata\s*:\s*(\{[^}]*\})/);
201
+ if (metadataMatch) {
202
+ try {
203
+ const metaStr = metadataMatch[1].replace(/'/g, '"').replace(/(\w+)\s*:/g, '"$1":').replace(/,\s*}/g, "}");
204
+ hook.metadata = JSON.parse(metaStr);
205
+ } catch {
206
+ }
207
+ }
208
+ return hook;
209
+ }
210
+ function extractHookScript(content) {
211
+ const handlerMatch = content.match(
212
+ /export\s+default\s+(?:async\s+)?function\s*(?:\w+)?\s*\([^)]*\)\s*\{([\s\S]*)\}[\s\n]*$/
213
+ );
214
+ if (handlerMatch) {
215
+ return handlerMatch[1].trim();
216
+ }
217
+ const simpleMatch = content.match(
218
+ /export\s+default\s+async\s+function[^{]*\{([\s\S]*)\}[\s\n]*$/
219
+ );
220
+ if (simpleMatch) {
221
+ return simpleMatch[1].trim();
222
+ }
223
+ return content.replace(/export\s+const\s+config[\s\S]*?;/, "").trim();
224
+ }
225
+ function hasDefaultHandler(content) {
226
+ return /export\s+default\s+(?:async\s+)?function\b/.test(content);
227
+ }
228
+
229
+ // src/lib/lint/rules/hook-verify.ts
230
+ function error2(target, message) {
231
+ return {
232
+ ruleId: "hook-verify",
233
+ target,
234
+ severity: "error",
235
+ message
236
+ };
237
+ }
238
+ var hookVerifyRule = {
239
+ id: "hook-verify",
240
+ description: "Validates hook file structure (config export, trigger, known keys, default handler) and verifies the script compiles via the backend.",
241
+ appliesTo: ["hook"],
242
+ async check(target, ctx) {
243
+ const config = parseHookConfig(target.source);
244
+ if (!config) {
245
+ return [
246
+ error2(
247
+ target,
248
+ "Missing or unparseable `export const config = {...}`. Hook files must export a config object with at least `collection` and `trigger`."
249
+ )
250
+ ];
251
+ }
252
+ const errors = [];
253
+ if (!config.external_id && !ctx.appName) {
254
+ errors.push(
255
+ error2(
256
+ target,
257
+ "Hook config is missing `external_id` and no app name is configured to derive one from the file name."
258
+ )
259
+ );
260
+ }
261
+ if (!hasDefaultHandler(target.source)) {
262
+ errors.push(
263
+ error2(
264
+ target,
265
+ "Missing default exported handler. Hook files must `export default function(ctx) { ... }` (async allowed)."
266
+ )
267
+ );
268
+ }
269
+ if (!VALID_HOOK_TRIGGERS.includes(config.trigger)) {
270
+ errors.push(
271
+ error2(
272
+ target,
273
+ `Invalid trigger '${config.trigger}'. Must be one of: ${VALID_HOOK_TRIGGERS.join(", ")}.`
274
+ )
275
+ );
276
+ }
277
+ const configBody = extractConfigBody(target.source);
278
+ if (configBody) {
279
+ const keys = extractTopLevelConfigKeys(configBody);
280
+ for (const key of keys) {
281
+ if (!KNOWN_HOOK_CONFIG_KEYS.has(key)) {
282
+ errors.push(
283
+ error2(
284
+ target,
285
+ `Unknown config key '${key}'. Known keys: ${[...KNOWN_HOOK_CONFIG_KEYS].join(", ")}.`
286
+ )
287
+ );
288
+ }
289
+ }
290
+ if (keys.includes("enabled")) {
291
+ const m = configBody.match(/enabled\s*:\s*([^,}\n]+)/);
292
+ const raw = m ? m[1].trim().replace(/,\s*$/, "").trim() : "";
293
+ if (raw !== "true" && raw !== "false") {
294
+ errors.push(
295
+ error2(
296
+ target,
297
+ `Invalid 'enabled' value '${raw}'. Must be a literal \`true\` or \`false\`.`
298
+ )
299
+ );
300
+ }
301
+ }
302
+ if (keys.includes("metadata") && !config.metadata) {
303
+ errors.push(
304
+ error2(
305
+ target,
306
+ "Could not parse 'metadata'. It must be an inline object literal with simple key/value pairs (e.g. `{ threshold: 100 }`); nested objects and non-literal values are not supported."
307
+ )
308
+ );
309
+ }
310
+ }
311
+ if (errors.length > 0) return errors;
312
+ let script = extractHookScript(target.source);
313
+ if (ctx.appName) {
314
+ script = script.replaceAll("{{app}}", ctx.appName);
315
+ }
316
+ if (!ctx.api) {
317
+ return [];
318
+ }
319
+ try {
320
+ await ctx.api.compileHook(script);
321
+ } catch (err) {
322
+ const detail = err instanceof ApiError ? err.body.trim() : err instanceof Error ? err.message : String(err);
323
+ return [error2(target, `Hook failed to compile on the backend: ${detail}`)];
324
+ }
325
+ return [];
326
+ }
327
+ };
328
+
122
329
  // src/lib/lint/rules/llm-import.ts
123
330
  var FROM_LUMERA_IMPORT_LLM = /^\s*from\s+lumera\s+import\s+\(?\s*(?:[\w\s,]*?\b)?llm\b/;
124
331
  var LUMERA_LLM_SUBMODULE = /^\s*(?:from\s+lumera\.llm\b|import\s+lumera\.llm\b)/;
@@ -162,7 +369,7 @@ var llmImportRule = {
162
369
  };
163
370
 
164
371
  // src/lib/lint/registry.ts
165
- var ALL_RULES = [collectionSchemaRule, llmImportRule];
372
+ var ALL_RULES = [collectionSchemaRule, hookVerifyRule, llmImportRule];
166
373
 
167
374
  // src/lib/lint/format.ts
168
375
  import { relative } from "path";
@@ -207,13 +414,14 @@ function serializeLintWarnings(warnings, projectRoot) {
207
414
  }
208
415
 
209
416
  // src/lib/lint/index.ts
210
- function runLint(ctx) {
417
+ async function runLint(ctx) {
211
418
  const out = [];
212
419
  for (const rule of ALL_RULES) {
213
420
  for (const t of ctx.targets) {
214
421
  if (!rule.appliesTo.includes(t.kind)) continue;
215
422
  try {
216
- out.push(...rule.check(t, ctx));
423
+ const result = await rule.check(t, ctx);
424
+ out.push(...result);
217
425
  } catch (err) {
218
426
  if (process.env.LUMERA_DEBUG) {
219
427
  console.error(`[lint] rule "${rule.id}" threw on ${t.filePath}:`, err);
@@ -247,12 +455,31 @@ function buildCollectionTargets(platformDir, _filterName) {
247
455
  }
248
456
  return targets;
249
457
  }
458
+ function buildHookTargets(platformDir, filterName) {
459
+ const hooksDir = join(platformDir, "hooks");
460
+ if (!existsSync(hooksDir)) return [];
461
+ const targets = [];
462
+ for (const entry of readdirSync(hooksDir, { withFileTypes: true })) {
463
+ if (!entry.isFile()) continue;
464
+ if (!entry.name.endsWith(".js") && !entry.name.endsWith(".ts")) continue;
465
+ const base = entry.name.replace(/\.(js|ts)$/, "");
466
+ if (filterName && base !== filterName) continue;
467
+ const filePath = join(hooksDir, entry.name);
468
+ targets.push({
469
+ kind: "hook",
470
+ name: base,
471
+ filePath,
472
+ source: readFileSync(filePath, "utf-8")
473
+ });
474
+ }
475
+ return targets;
476
+ }
250
477
 
251
478
  // src/commands/resources.ts
252
479
  init_auth();
253
- function safeLint(projectRoot, localAutomations) {
480
+ async function safeLint(projectRoot, localAutomations) {
254
481
  try {
255
- return runLint({ projectRoot, targets: buildAutomationTargets(localAutomations) });
482
+ return await runLint({ projectRoot, targets: buildAutomationTargets(localAutomations) });
256
483
  } catch (err) {
257
484
  if (process.env.LUMERA_DEBUG) console.error("[lint] pass failed:", err);
258
485
  return [];
@@ -265,13 +492,22 @@ function safePrintLint(warnings, projectRoot) {
265
492
  if (process.env.LUMERA_DEBUG) console.error("[lint] print failed:", err);
266
493
  }
267
494
  }
268
- function lintCollectionFiles(projectRoot, platformDir, filterName) {
269
- const issues = runLint({ projectRoot, targets: buildCollectionTargets(platformDir, filterName) });
495
+ async function lintCollectionFiles(projectRoot, platformDir, filterName) {
496
+ const issues = await runLint({ projectRoot, targets: buildCollectionTargets(platformDir, filterName) });
270
497
  const errors = issues.filter((issue) => issue.severity === "error");
271
498
  if (errors.length === 0) return;
272
499
  printLintWarnings(issues, projectRoot);
273
500
  throw new Error(`Found ${errors.length} collection lint error(s)`);
274
501
  }
502
+ async function lintHookFiles(api, projectRoot, platformDir, appName, filterName) {
503
+ const targets = buildHookTargets(platformDir, filterName);
504
+ if (targets.length === 0) return;
505
+ const issues = await runLint({ projectRoot, targets, api, appName });
506
+ const errors = issues.filter((issue) => issue.severity === "error");
507
+ if (errors.length === 0) return;
508
+ printLintWarnings(issues, projectRoot);
509
+ throw new Error(`Found ${errors.length} hook lint error(s)`);
510
+ }
275
511
  var PACKAGE_MANAGERS = ["bun", "pnpm", "yarn", "npm"];
276
512
  var PACKAGE_MANAGER_VALUE_FLAGS = /* @__PURE__ */ new Set(["--package-manager"]);
277
513
  function isPackageManager(value) {
@@ -970,47 +1206,6 @@ function loadLocalHooks(platformDir, filterName, appName) {
970
1206
  }
971
1207
  return hooks;
972
1208
  }
973
- function parseHookConfig(content) {
974
- const configMatch = content.match(/export\s+const\s+config\s*[=:]\s*(\{[\s\S]*?\});?/);
975
- if (!configMatch) return null;
976
- const externalId = configMatch[1].match(/external_id\s*:\s*['"]([^'"]+)['"]/)?.[1];
977
- const collection = configMatch[1].match(/collection\s*:\s*['"]([^'"]+)['"]/)?.[1];
978
- const trigger = configMatch[1].match(/trigger\s*:\s*['"]([^'"]+)['"]/)?.[1];
979
- const enabled = configMatch[1].match(/enabled\s*:\s*(true|false)/)?.[1];
980
- const name = configMatch[1].match(/name\s*:\s*['"]([^'"]+)['"]/)?.[1];
981
- if (!collection || !trigger) return null;
982
- const hook = {
983
- external_id: externalId || "",
984
- collection,
985
- trigger,
986
- enabled: enabled !== "false"
987
- };
988
- if (name) hook.name = name;
989
- const metadataMatch = configMatch[1].match(/metadata\s*:\s*(\{[^}]*\})/);
990
- if (metadataMatch) {
991
- try {
992
- const metaStr = metadataMatch[1].replace(/'/g, '"').replace(/(\w+)\s*:/g, '"$1":').replace(/,\s*}/g, "}");
993
- hook.metadata = JSON.parse(metaStr);
994
- } catch {
995
- }
996
- }
997
- return hook;
998
- }
999
- function extractHookScript(content) {
1000
- const handlerMatch = content.match(
1001
- /export\s+default\s+(?:async\s+)?function\s*(?:\w+)?\s*\([^)]*\)\s*\{([\s\S]*)\}[\s\n]*$/
1002
- );
1003
- if (handlerMatch) {
1004
- return handlerMatch[1].trim();
1005
- }
1006
- const simpleMatch = content.match(
1007
- /export\s+default\s+async\s+function[^{]*\{([\s\S]*)\}[\s\n]*$/
1008
- );
1009
- if (simpleMatch) {
1010
- return simpleMatch[1].trim();
1011
- }
1012
- return content.replace(/export\s+const\s+config[\s\S]*?;/, "").trim();
1013
- }
1014
1209
  function convertCollectionToApiFormat(local) {
1015
1210
  const schema = local.fields.map((field) => {
1016
1211
  const apiField = {
@@ -2686,7 +2881,10 @@ async function plan(args) {
2686
2881
  console.log(pc2.dim(" Comparing local files to remote state..."));
2687
2882
  console.log();
2688
2883
  if (!type || type === "collections") {
2689
- lintCollectionFiles(projectRoot, platformDir, name || void 0);
2884
+ await lintCollectionFiles(projectRoot, platformDir, name || void 0);
2885
+ }
2886
+ if (!type || type === "hooks") {
2887
+ await lintHookFiles(api, projectRoot, platformDir, appName, name || void 0);
2690
2888
  }
2691
2889
  await syncDeps(projectRoot, { ignorePermissionDenied: true });
2692
2890
  const allChanges = [];
@@ -2733,7 +2931,7 @@ async function plan(args) {
2733
2931
  allChanges.push(...changes);
2734
2932
  }
2735
2933
  }
2736
- const lintWarnings = safeLint(projectRoot, localAutomations);
2934
+ const lintWarnings = await safeLint(projectRoot, localAutomations);
2737
2935
  if (allChanges.length === 0) {
2738
2936
  if (jsonOutput) {
2739
2937
  console.log(JSON.stringify({ changes: [], warnings: [], lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
@@ -2828,7 +3026,10 @@ async function apply(args) {
2828
3026
  return;
2829
3027
  }
2830
3028
  if (!type || type === "collections") {
2831
- lintCollectionFiles(projectRoot, platformDir, name || void 0);
3029
+ await lintCollectionFiles(projectRoot, platformDir, name || void 0);
3030
+ }
3031
+ if (!type || type === "hooks") {
3032
+ await lintHookFiles(api, projectRoot, platformDir, appName, name || void 0);
2832
3033
  }
2833
3034
  await syncDeps(projectRoot, { ignorePermissionDenied: true });
2834
3035
  let collections;
@@ -2870,7 +3071,7 @@ async function apply(args) {
2870
3071
  console.log();
2871
3072
  console.log(pc2.green(" \u2713 Nothing to apply \u2014 local and remote are in sync."));
2872
3073
  console.log();
2873
- safePrintLint(safeLint(projectRoot, localAutomations), projectRoot);
3074
+ safePrintLint(await safeLint(projectRoot, localAutomations), projectRoot);
2874
3075
  return;
2875
3076
  }
2876
3077
  console.log();
@@ -2898,7 +3099,7 @@ async function apply(args) {
2898
3099
  console.log(pc2.dim(" No infrastructure changes \u2014 deploying app only."));
2899
3100
  console.log();
2900
3101
  }
2901
- safePrintLint(safeLint(projectRoot, localAutomations), projectRoot);
3102
+ safePrintLint(await safeLint(projectRoot, localAutomations), projectRoot);
2902
3103
  if (!autoConfirm && allChanges.length > 0) {
2903
3104
  const { confirm } = await prompts({
2904
3105
  type: "confirm",
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-SMZESQV2.js";
6
+ } from "./chunk-EDRAYUWN.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.8",
3
+ "version": "0.19.9-dev.1",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {