@lumerahq/cli 0.19.9-dev.0 → 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.
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-5ELZGJOH.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-5ELZGJOH.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-5ELZGJOH.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-5ELZGJOH.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-5ELZGJOH.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-5ELZGJOH.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-5ELZGJOH.js").then((m) => m.diff(args.slice(1)));
241
241
  break;
242
242
  // Development
243
243
  case "dev":
@@ -121,8 +121,68 @@ 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 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
+ }
124
184
  function parseHookConfig(content) {
125
- const configMatch = content.match(/export\s+const\s+config\s*[=:]\s*(\{[\s\S]*?\});?/);
185
+ const configMatch = content.match(CONFIG_BLOCK_RE);
126
186
  if (!configMatch) return null;
127
187
  const externalId = configMatch[1].match(/external_id\s*:\s*['"]([^'"]+)['"]/)?.[1];
128
188
  const collection = configMatch[1].match(/collection\s*:\s*['"]([^'"]+)['"]/)?.[1];
@@ -177,7 +237,7 @@ function error2(target, message) {
177
237
  }
178
238
  var hookVerifyRule = {
179
239
  id: "hook-verify",
180
- description: "Validates hook file structure (config export + default handler) and verifies the script compiles via the backend.",
240
+ description: "Validates hook file structure (config export, trigger, known keys, default handler) and verifies the script compiles via the backend.",
181
241
  appliesTo: ["hook"],
182
242
  async check(target, ctx) {
183
243
  const config = parseHookConfig(target.source);
@@ -189,22 +249,66 @@ var hookVerifyRule = {
189
249
  )
190
250
  ];
191
251
  }
252
+ const errors = [];
192
253
  if (!config.external_id && !ctx.appName) {
193
- return [
254
+ errors.push(
194
255
  error2(
195
256
  target,
196
257
  "Hook config is missing `external_id` and no app name is configured to derive one from the file name."
197
258
  )
198
- ];
259
+ );
199
260
  }
200
261
  if (!hasDefaultHandler(target.source)) {
201
- return [
262
+ errors.push(
202
263
  error2(
203
264
  target,
204
265
  "Missing default exported handler. Hook files must `export default function(ctx) { ... }` (async allowed)."
205
266
  )
206
- ];
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
+ }
207
310
  }
311
+ if (errors.length > 0) return errors;
208
312
  let script = extractHookScript(target.source);
209
313
  if (ctx.appName) {
210
314
  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.1",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {