@walkeros/cli 1.1.2 → 1.2.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @walkeros/cli
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cc68f50: Add validate command for events, flows, and mappings
8
+ - `walkeros validate event` - validates event structure using
9
+ PartialEventSchema
10
+ - `walkeros validate flow` - validates flow configurations using SetupSchema
11
+ - `walkeros validate mapping` - validates mapping event patterns
12
+
13
+ Includes programmatic API via `import { validate } from '@walkeros/cli'`
14
+
15
+ ## 1.1.3
16
+
17
+ ### Patch Changes
18
+
19
+ - 6fcfaf5: Fix chain property handling for all component types in bundler.
20
+ Sources now correctly output `next` property for pre-collector transformer
21
+ chains. Unified inline code generation for sources, destinations, and
22
+ transformers. Standardized transformer `next` as top-level property
23
+ (consistent with destination `before`).
24
+
3
25
  ## 1.1.2
4
26
 
5
27
  ### Patch Changes
@@ -250,7 +250,10 @@
250
250
  "this",
251
251
  {
252
252
  "map": {
253
- "item_id": "data.id",
253
+ "item_id": [
254
+ { "key": "data.sku" },
255
+ { "key": "data.id" }
256
+ ],
254
257
  "item_name": "data.name",
255
258
  "item_category": "data.category",
256
259
  "price": "data.price"
@@ -322,6 +325,12 @@
322
325
  "url": "$var.apiUrl",
323
326
  "batch": 5
324
327
  },
328
+ "data": {
329
+ "map": {
330
+ "sent_at": { "fn": "$code:() => Date.now()" },
331
+ "flow_version": "$var.flowVersion"
332
+ }
333
+ },
325
334
  "mapping": {
326
335
  "order": {
327
336
  "complete": {
@@ -71,7 +71,7 @@ npx walkeros serve packages/cli/examples/flow-complete.json --flow web
71
71
 
72
72
  ## Feature Inventory
73
73
 
74
- ### Features Used (51)
74
+ ### Features Used (53)
75
75
 
76
76
  #### Mapping - Value Extraction
77
77
 
@@ -81,6 +81,7 @@ npx walkeros serve packages/cli/examples/flow-complete.json --flow web
81
81
  | Static value | Meta ViewContent | `"content_type": { "value": "product" }` |
82
82
  | Key with fallback | GA4 add_to_cart | `{ "key": "data.currency", "value": "$variables.currency" }` |
83
83
  | Nested key (deep) | dataLayer mapping | `"items.0.item_id"` |
84
+ | Fallback array | GA4 view_item | `[{ "key": "data.sku" }, { "key": "data.id" }]` |
84
85
 
85
86
  #### Mapping - Structure
86
87
 
@@ -92,6 +93,7 @@ npx walkeros serve packages/cli/examples/flow-complete.json --flow web
92
93
  | Set (single value) | Meta ViewContent | `"content_ids": { "set": ["data.id"] }` |
93
94
  | Set (multiple values) | Meta settings | `"external_id": { "set": ["user.device", "user.session"] }` |
94
95
  | Direct passthrough | Meta PageView | `"data": "data"` |
96
+ | Config-level data | API destination | `"data": { "map": { "sent_at": {...} } }` |
95
97
 
96
98
  #### Mapping - Control
97
99
 
@@ -175,36 +177,33 @@ npx walkeros serve packages/cli/examples/flow-complete.json --flow web
175
177
 
176
178
  ---
177
179
 
178
- ### Features NOT Used (15)
180
+ ### Features NOT Used (6)
179
181
 
180
- #### Requires JavaScript (7)
182
+ #### Now Available via $code: Prefix ✅
181
183
 
182
- These features cannot be used in pure JSON configurations:
184
+ These features are now fully supported in JSON via `$code:` prefix (and ARE used
185
+ in this example):
183
186
 
184
- | Feature | Reason |
185
- | --------------------------- | ----------------------------- |
186
- | `fn:` function | Requires JavaScript callback |
187
- | `condition:` | Requires JavaScript predicate |
188
- | Conditional mapping (array) | Requires condition functions |
189
- | Custom transformer code | Requires JavaScript |
190
- | Custom source code | Requires JavaScript |
191
- | Custom destination code | Requires JavaScript |
192
- | Event handler callbacks | Requires JavaScript |
187
+ | Feature | Status |
188
+ | --------------------------- | ----------------------------------- |
189
+ | `fn:` function | Used via `$code:` in GA4 value |
190
+ | `condition:` | Used via `$code:` in definitions |
191
+ | Conditional mapping (array) | Used in serverValidator |
192
+ | Custom transformer code | Used in enricher, filter |
193
+ | Custom destination code | Used in debug logger |
193
194
 
194
- #### Omitted for Clarity (8)
195
+ #### Omitted for Clarity (6)
195
196
 
196
197
  These features could be added but were omitted to keep the example focused:
197
198
 
198
- | Feature | Why Omitted |
199
- | ------------------------- | ----------------------------- |
200
- | Multiple named flows (3+) | Two flows sufficient for demo |
201
- | Queue config | Advanced batching scenario |
202
- | Retry config | Advanced error handling |
203
- | Custom fetch options | API destination advanced |
204
- | Dynamic routing | Requires condition logic |
205
- | Transform before send | Covered by policy |
206
- | Custom headers in API | Would add complexity |
207
- | Multiple validators | One per flow sufficient |
199
+ | Feature | Why Omitted |
200
+ | ------------------------- | ------------------------------ |
201
+ | Multiple named flows (3+) | Two flows sufficient for demo |
202
+ | Queue config | Advanced batching scenario |
203
+ | Retry config | Advanced error handling |
204
+ | Custom fetch options | API destination advanced |
205
+ | Custom headers in API | Would add complexity |
206
+ | `validate:` function | Could add via $code: if needed |
208
207
 
209
208
  ---
210
209
 
package/dist/index.d.ts CHANGED
@@ -419,4 +419,32 @@ declare function runCommand(mode: string, options: RunCommandOptions): Promise<v
419
419
  */
420
420
  declare function run(mode: RunMode, options: RunOptions): Promise<RunResult>;
421
421
 
422
- export { type BuildOptions, type BundleStats, type CLIBuildOptions, type GlobalOptions, type MinifyOptions, type RunCommandOptions, type RunMode, type RunOptions, type RunResult, type SimulationResult, bundle, bundleCommand, pushCommand, run, runCommand, simulate, simulateCommand };
422
+ type ValidationType = 'event' | 'flow' | 'mapping';
423
+ interface ValidationError {
424
+ path: string;
425
+ message: string;
426
+ value?: unknown;
427
+ code?: string;
428
+ }
429
+ interface ValidationWarning {
430
+ path: string;
431
+ message: string;
432
+ suggestion?: string;
433
+ }
434
+ interface ValidateResult {
435
+ valid: boolean;
436
+ type: ValidationType;
437
+ errors: ValidationError[];
438
+ warnings: ValidationWarning[];
439
+ details: Record<string, unknown>;
440
+ }
441
+
442
+ /**
443
+ * Programmatic API for validation.
444
+ * Can be called directly from code or MCP server.
445
+ */
446
+ declare function validate(type: ValidationType, input: unknown, options?: {
447
+ flow?: string;
448
+ }): Promise<ValidateResult>;
449
+
450
+ export { type BuildOptions, type BundleStats, type CLIBuildOptions, type GlobalOptions, type MinifyOptions, type RunCommandOptions, type RunMode, type RunOptions, type RunResult, type SimulationResult, type ValidateResult, type ValidationError, type ValidationType, type ValidationWarning, bundle, bundleCommand, pushCommand, run, runCommand, simulate, simulateCommand, validate };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk2 from "chalk";
5
+ import chalk3 from "chalk";
6
6
 
7
7
  // src/version.ts
8
8
  import { readFileSync } from "fs";
@@ -822,34 +822,34 @@ function validateReference(type, name, ref) {
822
822
  throw new Error(`${type} "${name}": Must specify either package or code.`);
823
823
  }
824
824
  }
825
- function generateInlineCode(inline, config, env) {
825
+ function generateInlineCode(inline, config, env, chain, chainPropertyName, isDestination) {
826
826
  const pushFn = inline.push.replace("$code:", "");
827
827
  const initFn = inline.init ? inline.init.replace("$code:", "") : void 0;
828
828
  const typeLine = inline.type ? `type: '${inline.type}',` : "";
829
- return `{
830
- code: async (context) => ({
829
+ const chainLine = chain && chainPropertyName ? `${chainPropertyName}: ${JSON.stringify(chain)},` : "";
830
+ if (isDestination) {
831
+ return `{
832
+ code: {
831
833
  ${typeLine}
832
- config: context.config,
834
+ config: ${JSON.stringify(config || {})},
833
835
  ${initFn ? `init: ${initFn},` : ""}
834
836
  push: ${pushFn}
835
- }),
837
+ },
836
838
  config: ${JSON.stringify(config || {})},
837
- env: ${JSON.stringify(env || {})}
839
+ env: ${JSON.stringify(env || {})}${chain ? `,
840
+ ${chainLine.slice(0, -1)}` : ""}
838
841
  }`;
839
- }
840
- function generateInlineDestinationCode(inline, config, env) {
841
- const pushFn = inline.push.replace("$code:", "");
842
- const initFn = inline.init ? inline.init.replace("$code:", "") : void 0;
843
- const typeLine = inline.type ? `type: '${inline.type}',` : "";
842
+ }
844
843
  return `{
845
- code: {
844
+ code: async (context) => ({
846
845
  ${typeLine}
847
- config: ${JSON.stringify(config || {})},
846
+ config: context.config,
848
847
  ${initFn ? `init: ${initFn},` : ""}
849
848
  push: ${pushFn}
850
- },
849
+ }),
851
850
  config: ${JSON.stringify(config || {})},
852
- env: ${JSON.stringify(env || {})}
851
+ env: ${JSON.stringify(env || {})}${chain ? `,
852
+ ${chainLine.slice(0, -1)}` : ""}
853
853
  }`;
854
854
  }
855
855
  async function copyIncludes(includes, sourceDir, outputDir, logger2) {
@@ -1406,7 +1406,7 @@ function buildConfigObject(flowConfig, explicitCodeImports) {
1406
1406
  ([, source]) => source.code !== true && (source.package || isInlineCode(source.code))
1407
1407
  ).map(([key, source]) => {
1408
1408
  if (isInlineCode(source.code)) {
1409
- return ` ${key}: ${generateInlineCode(source.code, source.config || {}, source.env)}`;
1409
+ return ` ${key}: ${generateInlineCode(source.code, source.config || {}, source.env, source.next, "next")}`;
1410
1410
  }
1411
1411
  let codeVar;
1412
1412
  if (source.code && typeof source.code === "string" && explicitCodeImports.has(source.package)) {
@@ -1417,16 +1417,18 @@ function buildConfigObject(flowConfig, explicitCodeImports) {
1417
1417
  const configStr = source.config ? processConfigValue(source.config) : "{}";
1418
1418
  const envStr = source.env ? `,
1419
1419
  env: ${processConfigValue(source.env)}` : "";
1420
+ const nextStr = source.next ? `,
1421
+ next: ${JSON.stringify(source.next)}` : "";
1420
1422
  return ` ${key}: {
1421
1423
  code: ${codeVar},
1422
- config: ${configStr}${envStr}
1424
+ config: ${configStr}${envStr}${nextStr}
1423
1425
  }`;
1424
1426
  });
1425
1427
  const destinationsEntries = Object.entries(destinations).filter(
1426
1428
  ([, dest]) => dest.code !== true && (dest.package || isInlineCode(dest.code))
1427
1429
  ).map(([key, dest]) => {
1428
1430
  if (isInlineCode(dest.code)) {
1429
- return ` ${key}: ${generateInlineDestinationCode(dest.code, dest.config || {}, dest.env)}`;
1431
+ return ` ${key}: ${generateInlineCode(dest.code, dest.config || {}, dest.env, dest.before, "before", true)}`;
1430
1432
  }
1431
1433
  let codeVar;
1432
1434
  if (dest.code && typeof dest.code === "string" && explicitCodeImports.has(dest.package)) {
@@ -1437,20 +1439,18 @@ function buildConfigObject(flowConfig, explicitCodeImports) {
1437
1439
  const configStr = dest.config ? processConfigValue(dest.config) : "{}";
1438
1440
  const envStr = dest.env ? `,
1439
1441
  env: ${processConfigValue(dest.env)}` : "";
1442
+ const beforeStr = dest.before ? `,
1443
+ before: ${JSON.stringify(dest.before)}` : "";
1440
1444
  return ` ${key}: {
1441
1445
  code: ${codeVar},
1442
- config: ${configStr}${envStr}
1446
+ config: ${configStr}${envStr}${beforeStr}
1443
1447
  }`;
1444
1448
  });
1445
1449
  const transformersEntries = Object.entries(transformers).filter(
1446
1450
  ([, transformer]) => transformer.code !== true && (transformer.package || isInlineCode(transformer.code))
1447
1451
  ).map(([key, transformer]) => {
1448
1452
  if (isInlineCode(transformer.code)) {
1449
- const configWithNext2 = transformer.next ? {
1450
- ...transformer.config || {},
1451
- next: transformer.next
1452
- } : transformer.config || {};
1453
- return ` ${key}: ${generateInlineCode(transformer.code, configWithNext2, transformer.env)}`;
1453
+ return ` ${key}: ${generateInlineCode(transformer.code, transformer.config || {}, transformer.env, transformer.next, "next")}`;
1454
1454
  }
1455
1455
  let codeVar;
1456
1456
  if (transformer.code && typeof transformer.code === "string" && explicitCodeImports.has(transformer.package)) {
@@ -1458,13 +1458,14 @@ function buildConfigObject(flowConfig, explicitCodeImports) {
1458
1458
  } else {
1459
1459
  codeVar = packageNameToVariable(transformer.package);
1460
1460
  }
1461
- const configWithNext = transformer.next ? { ...transformer.config || {}, next: transformer.next } : transformer.config;
1462
- const configStr = configWithNext ? processConfigValue(configWithNext) : "{}";
1461
+ const configStr = transformer.config ? processConfigValue(transformer.config) : "{}";
1463
1462
  const envStr = transformer.env ? `,
1464
1463
  env: ${processConfigValue(transformer.env)}` : "";
1464
+ const nextStr = transformer.next ? `,
1465
+ next: ${JSON.stringify(transformer.next)}` : "";
1465
1466
  return ` ${key}: {
1466
1467
  code: ${codeVar},
1467
- config: ${configStr}${envStr}
1468
+ config: ${configStr}${envStr}${nextStr}
1468
1469
  }`;
1469
1470
  });
1470
1471
  const collectorStr = flowWithProps.collector ? `,
@@ -2904,6 +2905,288 @@ async function run(mode, options) {
2904
2905
  }
2905
2906
  }
2906
2907
 
2908
+ // src/commands/validate/index.ts
2909
+ import chalk2 from "chalk";
2910
+
2911
+ // src/commands/validate/validators/event.ts
2912
+ import { schemas as schemas3 } from "@walkeros/core/dev";
2913
+ var { PartialEventSchema } = schemas3;
2914
+ function validateEvent(input) {
2915
+ const errors = [];
2916
+ const warnings = [];
2917
+ const details = {};
2918
+ const event = typeof input === "object" && input !== null ? input : {};
2919
+ if (!("name" in event) || event.name === void 0) {
2920
+ errors.push({
2921
+ path: "name",
2922
+ message: "Event must have a name field",
2923
+ code: "MISSING_EVENT_NAME"
2924
+ });
2925
+ } else if (typeof event.name !== "string" || event.name.trim() === "") {
2926
+ errors.push({
2927
+ path: "name",
2928
+ message: "Event name cannot be empty",
2929
+ value: event.name,
2930
+ code: "EMPTY_EVENT_NAME"
2931
+ });
2932
+ } else {
2933
+ const name = event.name;
2934
+ if (!name.includes(" ")) {
2935
+ errors.push({
2936
+ path: "name",
2937
+ message: 'Event name must be "entity action" format with space (e.g., "page view")',
2938
+ value: name,
2939
+ code: "INVALID_EVENT_NAME"
2940
+ });
2941
+ details.entity = null;
2942
+ details.action = null;
2943
+ } else {
2944
+ const parts = name.trim().split(/\s+/);
2945
+ const action = parts.pop();
2946
+ const entity = parts.join(" ");
2947
+ details.entity = entity;
2948
+ details.action = action;
2949
+ }
2950
+ }
2951
+ const zodResult = PartialEventSchema.safeParse(input);
2952
+ if (!zodResult.success) {
2953
+ for (const issue of zodResult.error.issues) {
2954
+ const path14 = issue.path.join(".");
2955
+ if (path14 === "name") continue;
2956
+ errors.push({
2957
+ path: path14 || "root",
2958
+ message: issue.message,
2959
+ code: "SCHEMA_VALIDATION"
2960
+ });
2961
+ }
2962
+ }
2963
+ if (!event.consent) {
2964
+ warnings.push({
2965
+ path: "consent",
2966
+ message: "No consent object provided",
2967
+ suggestion: "Consider adding a consent object for GDPR/privacy compliance"
2968
+ });
2969
+ }
2970
+ details.hasConsent = !!event.consent;
2971
+ details.hasData = !!event.data;
2972
+ details.hasContext = !!event.context;
2973
+ return {
2974
+ valid: errors.length === 0,
2975
+ type: "event",
2976
+ errors,
2977
+ warnings,
2978
+ details
2979
+ };
2980
+ }
2981
+
2982
+ // src/commands/validate/validators/flow.ts
2983
+ import { schemas as schemas4 } from "@walkeros/core/dev";
2984
+ var { SetupSchema } = schemas4;
2985
+ function validateFlow(input, options = {}) {
2986
+ const errors = [];
2987
+ const warnings = [];
2988
+ const details = {};
2989
+ const config = typeof input === "object" && input !== null ? input : {};
2990
+ const zodResult = SetupSchema.safeParse(input);
2991
+ if (!zodResult.success) {
2992
+ for (const issue of zodResult.error.issues) {
2993
+ const path14 = issue.path.join(".");
2994
+ errors.push({
2995
+ path: path14 || "root",
2996
+ message: issue.message,
2997
+ code: "SCHEMA_VALIDATION"
2998
+ });
2999
+ }
3000
+ }
3001
+ const flows = config.flows;
3002
+ if (flows && typeof flows === "object" && Object.keys(flows).length === 0) {
3003
+ errors.push({
3004
+ path: "flows",
3005
+ message: "At least one flow is required",
3006
+ code: "EMPTY_FLOWS"
3007
+ });
3008
+ }
3009
+ if (flows && typeof flows === "object") {
3010
+ const flowNames = Object.keys(flows);
3011
+ details.flowNames = flowNames;
3012
+ details.flowCount = flowNames.length;
3013
+ if (options.flow) {
3014
+ if (!flowNames.includes(options.flow)) {
3015
+ errors.push({
3016
+ path: "flows",
3017
+ message: `Flow "${options.flow}" not found. Available: ${flowNames.join(", ")}`,
3018
+ code: "FLOW_NOT_FOUND"
3019
+ });
3020
+ } else {
3021
+ details.validatedFlow = options.flow;
3022
+ }
3023
+ }
3024
+ }
3025
+ const packages = config.packages;
3026
+ if (packages && typeof packages === "object") {
3027
+ for (const [pkgName, pkgConfig] of Object.entries(packages)) {
3028
+ if (!pkgConfig.version && !pkgConfig.path) {
3029
+ warnings.push({
3030
+ path: `packages.${pkgName}`,
3031
+ message: `Package "${pkgName}" has no version specified`,
3032
+ suggestion: "Consider specifying a version for reproducible builds"
3033
+ });
3034
+ }
3035
+ }
3036
+ details.packageCount = Object.keys(packages).length;
3037
+ }
3038
+ return {
3039
+ valid: errors.length === 0,
3040
+ type: "flow",
3041
+ errors,
3042
+ warnings,
3043
+ details
3044
+ };
3045
+ }
3046
+
3047
+ // src/commands/validate/validators/mapping.ts
3048
+ function validateMapping(input) {
3049
+ const errors = [];
3050
+ const warnings = [];
3051
+ const details = {};
3052
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
3053
+ errors.push({
3054
+ path: "root",
3055
+ message: "Mapping must be an object with event patterns as keys",
3056
+ code: "INVALID_MAPPING_TYPE"
3057
+ });
3058
+ return { valid: false, type: "mapping", errors, warnings, details };
3059
+ }
3060
+ const mapping = input;
3061
+ const patterns = Object.keys(mapping);
3062
+ details.eventPatterns = patterns;
3063
+ details.patternCount = patterns.length;
3064
+ patterns.forEach((pattern, index) => {
3065
+ const isWildcard = pattern.includes("*");
3066
+ const hasSpace = pattern.includes(" ");
3067
+ if (!isWildcard && !hasSpace) {
3068
+ errors.push({
3069
+ path: pattern,
3070
+ message: `Invalid event pattern "${pattern}". Must be "entity action" format or contain wildcard (*)`,
3071
+ code: "INVALID_EVENT_PATTERN"
3072
+ });
3073
+ }
3074
+ if (pattern === "*" && index !== patterns.length - 1) {
3075
+ warnings.push({
3076
+ path: "*",
3077
+ message: "Catch-all pattern (*) should be last",
3078
+ suggestion: "Move the catch-all pattern (*) to last position for predictable matching"
3079
+ });
3080
+ }
3081
+ const rule = mapping[pattern];
3082
+ const isValidRule = Array.isArray(rule) ? rule.every((r) => typeof r === "object" && r !== null) : typeof rule === "object" && rule !== null;
3083
+ if (!isValidRule) {
3084
+ errors.push({
3085
+ path: pattern,
3086
+ message: "Mapping rule must be an object or array of objects",
3087
+ code: "INVALID_RULE_TYPE"
3088
+ });
3089
+ }
3090
+ });
3091
+ return {
3092
+ valid: errors.length === 0,
3093
+ type: "mapping",
3094
+ errors,
3095
+ warnings,
3096
+ details
3097
+ };
3098
+ }
3099
+
3100
+ // src/commands/validate/index.ts
3101
+ async function validate(type, input, options = {}) {
3102
+ switch (type) {
3103
+ case "event":
3104
+ return validateEvent(input);
3105
+ case "flow":
3106
+ return validateFlow(input, { flow: options.flow });
3107
+ case "mapping":
3108
+ return validateMapping(input);
3109
+ default:
3110
+ throw new Error(`Unknown validation type: ${type}`);
3111
+ }
3112
+ }
3113
+ function formatResult(result, options) {
3114
+ if (options.json) {
3115
+ return JSON.stringify(result, null, 2);
3116
+ }
3117
+ const lines = [];
3118
+ lines.push("");
3119
+ lines.push(`Validating ${result.type}...`);
3120
+ lines.push("");
3121
+ if (options.verbose && Object.keys(result.details).length > 0) {
3122
+ lines.push("Details:");
3123
+ for (const [key, value] of Object.entries(result.details)) {
3124
+ lines.push(` ${key}: ${JSON.stringify(value)}`);
3125
+ }
3126
+ lines.push("");
3127
+ }
3128
+ lines.push("Validation Results:");
3129
+ for (const error of result.errors) {
3130
+ lines.push(chalk2.red(` \u2717 ${error.path}: ${error.message}`));
3131
+ }
3132
+ for (const warning of result.warnings) {
3133
+ lines.push(chalk2.yellow(` \u26A0 ${warning.path}: ${warning.message}`));
3134
+ if (warning.suggestion) {
3135
+ lines.push(chalk2.gray(` \u2192 ${warning.suggestion}`));
3136
+ }
3137
+ }
3138
+ if (result.valid) {
3139
+ lines.push(chalk2.green(` \u2713 All checks passed`));
3140
+ }
3141
+ lines.push("");
3142
+ lines.push(
3143
+ `Summary: ${result.errors.length} error(s), ${result.warnings.length} warning(s)`
3144
+ );
3145
+ return lines.join("\n");
3146
+ }
3147
+ async function validateCommand(options) {
3148
+ const logger2 = createCommandLogger(options);
3149
+ try {
3150
+ const input = await loadJsonFromSource(options.input, {
3151
+ name: options.type,
3152
+ required: true
3153
+ });
3154
+ const result = await validate(options.type, input, {
3155
+ flow: options.flow
3156
+ });
3157
+ const output = formatResult(result, {
3158
+ json: options.json,
3159
+ verbose: options.verbose
3160
+ });
3161
+ if (options.json) {
3162
+ console.log(output);
3163
+ } else {
3164
+ logger2.log(output);
3165
+ }
3166
+ if (!result.valid) {
3167
+ process.exit(1);
3168
+ }
3169
+ if (options.strict && result.warnings.length > 0) {
3170
+ process.exit(2);
3171
+ }
3172
+ process.exit(0);
3173
+ } catch (error) {
3174
+ const errorMessage = getErrorMessage(error);
3175
+ if (options.json) {
3176
+ logger2.json({
3177
+ valid: false,
3178
+ type: options.type,
3179
+ errors: [{ path: "input", message: errorMessage, code: "INPUT_ERROR" }],
3180
+ warnings: [],
3181
+ details: {}
3182
+ });
3183
+ } else {
3184
+ logger2.error(`Error: ${errorMessage}`);
3185
+ }
3186
+ process.exit(3);
3187
+ }
3188
+ }
3189
+
2907
3190
  // src/commands/cache.ts
2908
3191
  import fs13 from "fs-extra";
2909
3192
  function registerCacheCommand(program2) {
@@ -2954,7 +3237,7 @@ program.name("walkeros").description("walkerOS CLI - Bundle and deploy walkerOS
2954
3237
  program.hook("preAction", (thisCommand, actionCommand) => {
2955
3238
  const options = actionCommand.opts();
2956
3239
  if (!options.silent && !options.json) {
2957
- console.log(`${chalk2.hex("#01b5e2")("walkerOS")} v${VERSION}`);
3240
+ console.log(`${chalk3.hex("#01b5e2")("walkerOS")} v${VERSION}`);
2958
3241
  }
2959
3242
  });
2960
3243
  program.command("bundle [file]").description("Bundle NPM packages with custom code").option("--flow <name>", "flow name for multi-flow configs").option("--all", "build all flows for multi-flow configs").option("--stats", "show bundle statistics").option("--json", "output as JSON (implies --stats)").option("--no-cache", "disable package caching").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").option(
@@ -3001,6 +3284,17 @@ program.command("push [file]").description("Push an event through the flow with
3001
3284
  silent: options.silent
3002
3285
  });
3003
3286
  });
3287
+ program.command("validate <type> [input]").description("Validate event, flow, or mapping configuration").option("--flow <name>", "flow name for multi-flow configs").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").option("--strict", "fail on warnings").action(async (type, input, options) => {
3288
+ await validateCommand({
3289
+ type,
3290
+ input,
3291
+ flow: options.flow,
3292
+ json: options.json,
3293
+ verbose: options.verbose,
3294
+ silent: options.silent,
3295
+ strict: options.strict
3296
+ });
3297
+ });
3004
3298
  var runCmd = program.command("run").description("Run walkerOS flows in collect or serve mode");
3005
3299
  runCmd.command("collect [file]").description(
3006
3300
  "Run collector mode (event collection endpoint). Defaults to server-collect.mjs if no file specified."
@@ -3038,6 +3332,7 @@ export {
3038
3332
  run,
3039
3333
  runCommand,
3040
3334
  simulate,
3041
- simulateCommand
3335
+ simulateCommand,
3336
+ validate
3042
3337
  };
3043
3338
  //# sourceMappingURL=index.js.map