@lumerahq/cli 0.19.9-dev.0 → 0.19.9-dev.2

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
@@ -219,25 +219,25 @@ async function main() {
219
219
  switch (command) {
220
220
  // Resource commands
221
221
  case "plan":
222
- await import("./resources-VGUMSRND.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-ZFGJITDH.js").then((m) => m.plan(args.slice(1)));
223
223
  break;
224
224
  case "apply":
225
- await import("./resources-VGUMSRND.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-ZFGJITDH.js").then((m) => m.apply(args.slice(1)));
226
226
  break;
227
227
  case "pull":
228
- await import("./resources-VGUMSRND.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-ZFGJITDH.js").then((m) => m.pull(args.slice(1)));
229
229
  break;
230
230
  case "destroy":
231
- await import("./resources-VGUMSRND.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-ZFGJITDH.js").then((m) => m.destroy(args.slice(1)));
232
232
  break;
233
233
  case "list":
234
- await import("./resources-VGUMSRND.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-ZFGJITDH.js").then((m) => m.list(args.slice(1)));
235
235
  break;
236
236
  case "show":
237
- await import("./resources-VGUMSRND.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-ZFGJITDH.js").then((m) => m.show(args.slice(1)));
238
238
  break;
239
239
  case "diff":
240
- await import("./resources-VGUMSRND.js").then((m) => m.diff(args.slice(1)));
240
+ await import("./resources-ZFGJITDH.js").then((m) => m.diff(args.slice(1)));
241
241
  break;
242
242
  // Development
243
243
  case "dev":
@@ -121,8 +121,106 @@ var collectionSchemaRule = {
121
121
  };
122
122
 
123
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 stripJsComments(source) {
146
+ let out = "";
147
+ let inString = null;
148
+ let prev = "";
149
+ let i = 0;
150
+ while (i < source.length) {
151
+ const ch = source[i];
152
+ const next = source[i + 1];
153
+ if (inString) {
154
+ out += ch;
155
+ if (ch === inString && prev !== "\\") inString = null;
156
+ prev = ch;
157
+ i++;
158
+ continue;
159
+ }
160
+ if (ch === '"' || ch === "'" || ch === "`") {
161
+ inString = ch;
162
+ out += ch;
163
+ prev = ch;
164
+ i++;
165
+ continue;
166
+ }
167
+ if (ch === "/" && next === "/") {
168
+ while (i < source.length && source[i] !== "\n") i++;
169
+ continue;
170
+ }
171
+ if (ch === "/" && next === "*") {
172
+ i += 2;
173
+ while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) i++;
174
+ i += 2;
175
+ continue;
176
+ }
177
+ out += ch;
178
+ prev = ch;
179
+ i++;
180
+ }
181
+ return out;
182
+ }
183
+ function extractTopLevelConfigKeys(configBody) {
184
+ if (!configBody.startsWith("{") || !configBody.endsWith("}")) return [];
185
+ const inner = stripJsComments(configBody.slice(1, -1));
186
+ let stripped = "";
187
+ let depth = 0;
188
+ let inString = null;
189
+ let prev = "";
190
+ for (const ch of inner) {
191
+ if (inString) {
192
+ if (ch === inString && prev !== "\\") inString = null;
193
+ prev = ch;
194
+ continue;
195
+ }
196
+ if (ch === '"' || ch === "'" || ch === "`") {
197
+ inString = ch;
198
+ prev = ch;
199
+ continue;
200
+ }
201
+ if (ch === "{" || ch === "[") {
202
+ depth++;
203
+ prev = ch;
204
+ continue;
205
+ }
206
+ if (ch === "}" || ch === "]") {
207
+ depth--;
208
+ prev = ch;
209
+ continue;
210
+ }
211
+ if (depth === 0) stripped += ch;
212
+ prev = ch;
213
+ }
214
+ const keys = [];
215
+ const keyRe = /(?:^|,)\s*(?:['"]([^'"]+)['"]|(\w+))\s*:/g;
216
+ let m;
217
+ while ((m = keyRe.exec(stripped)) !== null) {
218
+ keys.push(m[1] || m[2]);
219
+ }
220
+ return keys;
221
+ }
124
222
  function parseHookConfig(content) {
125
- const configMatch = content.match(/export\s+const\s+config\s*[=:]\s*(\{[\s\S]*?\});?/);
223
+ const configMatch = content.match(CONFIG_BLOCK_RE);
126
224
  if (!configMatch) return null;
127
225
  const externalId = configMatch[1].match(/external_id\s*:\s*['"]([^'"]+)['"]/)?.[1];
128
226
  const collection = configMatch[1].match(/collection\s*:\s*['"]([^'"]+)['"]/)?.[1];
@@ -177,7 +275,7 @@ function error2(target, message) {
177
275
  }
178
276
  var hookVerifyRule = {
179
277
  id: "hook-verify",
180
- description: "Validates hook file structure (config export + default handler) and verifies the script compiles via the backend.",
278
+ description: "Validates hook file structure (config export, trigger, known keys, default handler) and verifies the script compiles via the backend.",
181
279
  appliesTo: ["hook"],
182
280
  async check(target, ctx) {
183
281
  const config = parseHookConfig(target.source);
@@ -189,22 +287,66 @@ var hookVerifyRule = {
189
287
  )
190
288
  ];
191
289
  }
290
+ const errors = [];
192
291
  if (!config.external_id && !ctx.appName) {
193
- return [
292
+ errors.push(
194
293
  error2(
195
294
  target,
196
295
  "Hook config is missing `external_id` and no app name is configured to derive one from the file name."
197
296
  )
198
- ];
297
+ );
199
298
  }
200
299
  if (!hasDefaultHandler(target.source)) {
201
- return [
300
+ errors.push(
202
301
  error2(
203
302
  target,
204
303
  "Missing default exported handler. Hook files must `export default function(ctx) { ... }` (async allowed)."
205
304
  )
206
- ];
305
+ );
306
+ }
307
+ if (!VALID_HOOK_TRIGGERS.includes(config.trigger)) {
308
+ errors.push(
309
+ error2(
310
+ target,
311
+ `Invalid trigger '${config.trigger}'. Must be one of: ${VALID_HOOK_TRIGGERS.join(", ")}.`
312
+ )
313
+ );
314
+ }
315
+ const configBody = extractConfigBody(target.source);
316
+ if (configBody) {
317
+ const keys = extractTopLevelConfigKeys(configBody);
318
+ for (const key of keys) {
319
+ if (!KNOWN_HOOK_CONFIG_KEYS.has(key)) {
320
+ errors.push(
321
+ error2(
322
+ target,
323
+ `Unknown config key '${key}'. Known keys: ${[...KNOWN_HOOK_CONFIG_KEYS].join(", ")}.`
324
+ )
325
+ );
326
+ }
327
+ }
328
+ if (keys.includes("enabled")) {
329
+ const m = configBody.match(/enabled\s*:\s*([^,}\n]+)/);
330
+ const raw = m ? m[1].trim().replace(/,\s*$/, "").trim() : "";
331
+ if (raw !== "true" && raw !== "false") {
332
+ errors.push(
333
+ error2(
334
+ target,
335
+ `Invalid 'enabled' value '${raw}'. Must be a literal \`true\` or \`false\`.`
336
+ )
337
+ );
338
+ }
339
+ }
340
+ if (keys.includes("metadata") && !config.metadata) {
341
+ errors.push(
342
+ error2(
343
+ target,
344
+ "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."
345
+ )
346
+ );
347
+ }
207
348
  }
349
+ if (errors.length > 0) return errors;
208
350
  let script = extractHookScript(target.source);
209
351
  if (ctx.appName) {
210
352
  script = script.replaceAll("{{app}}", ctx.appName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.19.9-dev.0",
3
+ "version": "0.19.9-dev.2",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {