@mondaydotcomorg/z2h-cli 0.33.2 → 0.33.3-feature-chezki-templates-poc-93101e2

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.
Files changed (39) hide show
  1. package/dist/backend/parser/parse.d.ts.map +1 -1
  2. package/dist/backend/parser/parse.js +2 -1
  3. package/dist/dashboard-templates.d.ts +2 -0
  4. package/dist/dashboard-templates.d.ts.map +1 -0
  5. package/dist/dashboard-templates.js +8 -0
  6. package/dist/esm/backend/parser/parse.d.ts.map +1 -1
  7. package/dist/esm/backend/parser/parse.mjs +2 -1
  8. package/dist/esm/dashboard-templates.d.ts +2 -0
  9. package/dist/esm/dashboard-templates.d.ts.map +1 -0
  10. package/dist/esm/dashboard-templates.mjs +1 -0
  11. package/dist/esm/index.mjs +15 -15
  12. package/dist/esm/types.d.ts +4 -5
  13. package/dist/esm/types.d.ts.map +1 -1
  14. package/dist/esm/util/logger.d.ts +2 -2
  15. package/dist/esm/util/logger.mjs +2 -2
  16. package/dist/esm/util/run-command.d.ts.map +1 -1
  17. package/dist/esm/util/run-command.mjs +10 -17
  18. package/dist/esm/util/tracker.d.ts +0 -8
  19. package/dist/esm/util/tracker.d.ts.map +1 -1
  20. package/dist/esm/util/tracker.mjs +1 -11
  21. package/dist/index.js +15 -15
  22. package/dist/types.d.ts +4 -5
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/util/logger.d.ts +2 -2
  25. package/dist/util/logger.js +2 -2
  26. package/dist/util/run-command.d.ts.map +1 -1
  27. package/dist/util/run-command.js +9 -16
  28. package/dist/util/tracker.d.ts +0 -8
  29. package/dist/util/tracker.d.ts.map +1 -1
  30. package/dist/util/tracker.js +0 -11
  31. package/package.json +7 -2
  32. package/src/backend/parser/parse.ts +2 -1
  33. package/src/dashboard-templates.ts +5 -0
  34. package/src/index.ts +21 -15
  35. package/src/types.ts +4 -5
  36. package/src/util/__tests__/run-command.test.ts +40 -19
  37. package/src/util/logger.ts +2 -2
  38. package/src/util/run-command.ts +11 -21
  39. package/src/util/tracker.ts +0 -11
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/backend/parser/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAkB,MAAM,OAAO,CAAC;AAkDjH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBhE"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/backend/parser/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAkB,MAAM,OAAO,CAAC;AAmDjH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBhE"}
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const acorn = require('acorn');
4
4
  const zod = require('zod');
5
+ const observability = require('@mondaydotcomorg/z2h-shared-utils/observability');
5
6
  const util_tracker = require('../../util/tracker.js');
6
7
  const backend_parser_handlerDeclaration = require('./handler-declaration.js');
7
8
  const backend_parser_messages = require('./messages.js');
@@ -29,7 +30,7 @@ const isImportLike = (node) => node.type === 'ImportDeclaration' ||
29
30
  const isExport = (node) => node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration';
30
31
  /** Reports the failure, then returns the error for the caller to throw. */
31
32
  function parseError(message) {
32
- void util_tracker.trackEvent('z2h_cli_handler_parse_error', { error: message });
33
+ void util_tracker.trackEvent(observability.Z2hCliEvents.handlerParseError, { error: message });
33
34
  return new Error(message);
34
35
  }
35
36
  /**
@@ -0,0 +1,2 @@
1
+ export { loadDashboardTemplatesCatalog, findTemplate, type DashboardTemplateEntry, } from './util/dashboard-templates/load-catalog';
2
+ //# sourceMappingURL=dashboard-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-templates.d.ts","sourceRoot":"","sources":["../src/dashboard-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,KAAK,sBAAsB,GAC5B,MAAM,yCAAyC,CAAC"}
@@ -0,0 +1,8 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const util_dashboardTemplates_loadCatalog = require('./util/dashboard-templates/load-catalog.js');
4
+
5
+
6
+
7
+ exports.findTemplate = util_dashboardTemplates_loadCatalog.findTemplate;
8
+ exports.loadDashboardTemplatesCatalog = util_dashboardTemplates_loadCatalog.loadDashboardTemplatesCatalog;
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../../src/backend/parser/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAkB,MAAM,OAAO,CAAC;AAkDjH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBhE"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../../src/backend/parser/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAkB,MAAM,OAAO,CAAC;AAmDjH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBhE"}
@@ -1,5 +1,6 @@
1
1
  import { parse } from 'acorn';
2
2
  import { z } from 'zod';
3
+ import { Z2hCliEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
3
4
  import { trackEvent } from '../../util/tracker.mjs';
4
5
  import { exportsHandler } from './handler-declaration.mjs';
5
6
  import { messages } from './messages.mjs';
@@ -27,7 +28,7 @@ const isImportLike = (node) => node.type === 'ImportDeclaration' ||
27
28
  const isExport = (node) => node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration';
28
29
  /** Reports the failure, then returns the error for the caller to throw. */
29
30
  function parseError(message) {
30
- void trackEvent('z2h_cli_handler_parse_error', { error: message });
31
+ void trackEvent(Z2hCliEvents.handlerParseError, { error: message });
31
32
  return new Error(message);
32
33
  }
33
34
  /**
@@ -0,0 +1,2 @@
1
+ export { loadDashboardTemplatesCatalog, findTemplate, type DashboardTemplateEntry, } from './util/dashboard-templates/load-catalog';
2
+ //# sourceMappingURL=dashboard-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-templates.d.ts","sourceRoot":"","sources":["../../src/dashboard-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,KAAK,sBAAsB,GAC5B,MAAM,yCAAyC,CAAC"}
@@ -0,0 +1 @@
1
+ export { findTemplate, loadDashboardTemplatesCatalog } from './util/dashboard-templates/load-catalog.mjs';
@@ -3,6 +3,7 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { Command } from 'commander';
5
5
  import { execa } from 'execa';
6
+ import { Z2hCliEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
6
7
  import { getWorkspaceHome } from './util/workspace.mjs';
7
8
  import { buildCommand } from './commands/build.mjs';
8
9
  import { cleanCommand } from './commands/clean.mjs';
@@ -23,7 +24,6 @@ import { backendValidateCommand, backendInvokeCommand } from './commands/backend
23
24
  import { doctorCommand } from './commands/doctor.mjs';
24
25
  import { fail, errorLog, setOutputMode, getOutputMode } from './util/logger.mjs';
25
26
  import { runCommand } from './util/run-command.mjs';
26
- import { successEvent } from './util/tracker.mjs';
27
27
  import { getCwdAppName } from './util/app-name.mjs';
28
28
 
29
29
  const { version } = createRequire(import.meta.url)('@mondaydotcomorg/z2h-cli/package.json');
@@ -77,14 +77,14 @@ async function main() {
77
77
  program
78
78
  .command('build')
79
79
  .description('Build the consumer into its .zth/ shadow and copy artifacts to ./build/')
80
- // A standalone build failure is the consumer's own code, not a CLI/infra problem — don't
81
- // track it. buildCommand still tags the reason on its thrown error, since deploy's own
82
- // (tracked) failure event benefits from knowing when a build broke it.
83
- .action(runCommand({ track: false, data: cwdApp, exitCode: 2 }, () => buildCommand()));
80
+ // A standalone build failure is the consumer's own code, not a CLI/infra problem — runCommand
81
+ // skips the error event for failures tagged `build_error` (buildCommand tags it on its thrown
82
+ // error). Any other failure here is still reported.
83
+ .action(runCommand({ event: Z2hCliEvents.buildSuccess, data: cwdApp, exitCode: 2 }, () => buildCommand()));
84
84
  program
85
85
  .command('clean')
86
86
  .description('Delete <consumer>/.zth/ and <consumer>/build/')
87
- .action(runCommand({ track: false, exitCode: 2 }, () => cleanCommand()));
87
+ .action(runCommand({ exitCode: 2 }, () => cleanCommand()));
88
88
  program
89
89
  .command('deploy')
90
90
  .description('Build, upload to S3, and register the app with the Z2H backend')
@@ -96,9 +96,7 @@ async function main() {
96
96
  .option('--description <text>', 'short (<=200 char) description of the app')
97
97
  .option('--prebuilt-dir <path>', 'internal — used by `generate-app-template`: skip the build step and deploy this already-built directory as-is')
98
98
  .action(runCommand({
99
- // A preview is a real deploy for observability, tracked as its own event since
100
- // the command name alone (`deploy`) can't distinguish it from a live deploy.
101
- event: ([opts]) => successEvent(opts.preview ? 'preview' : 'deploy'),
99
+ event: ([opts]) => opts.preview ? Z2hCliEvents.previewSuccess : Z2hCliEvents.deploySuccess,
102
100
  label: ([opts]) => (opts.preview ? 'preview' : 'deploy'),
103
101
  data: cwdApp,
104
102
  exitCode: 2,
@@ -113,7 +111,7 @@ async function main() {
113
111
  program
114
112
  .command('create-workspace')
115
113
  .description(`Bootstrap the ${getWorkspaceHome()}/ yarn workspace (one-time setup)`)
116
- .action(runCommand({ track: false }, () => createWorkspaceCommand()));
114
+ .action(runCommand({}, () => createWorkspaceCommand()));
117
115
  program
118
116
  .command('generate-z2h-token')
119
117
  .description('Open the bigbrain.me token page, capture the token via a loopback callback, and persist it to ~/.zth/auth.json')
@@ -128,6 +126,7 @@ async function main() {
128
126
  .option('--remote <type>', 'Source control remote: s3 (shared storage, recommended, auto-backed-up) or github (manage it yourself in your own repo)')
129
127
  .option('--repo-url <url>', 'GitHub repo URL — required when --remote=github')
130
128
  .action(runCommand({
129
+ event: Z2hCliEvents.createSuccess,
131
130
  data: ([appName, opts]) => ({
132
131
  app_name: appName,
133
132
  template: opts.template ?? 'default',
@@ -136,20 +135,21 @@ async function main() {
136
135
  }, (appName, opts) => createCommand(appName, opts)));
137
136
  program
138
137
  .command('templates')
139
- .description("List the BigBrain Studio (Z2H) template catalog — id, title, description, and each template's widget schema")
140
- .action(runCommand({}, () => templatesCommand()));
138
+ .description("List the BigBrain Studio (Z2H) template catalog — id, title, description, and each template's config schema")
139
+ .action(runCommand({ event: Z2hCliEvents.templatesSuccess }, () => templatesCommand()));
141
140
  program
142
141
  .command('generate-app-template <app-name>')
143
142
  .description(`Scaffold + preview a Z2H app/dashboard from a templates catalog entry inside ${getWorkspaceHome()}/ — copies a prebuilt template, patches in real widget data, and deploys a preview. No build, no yarn install, no live deploy — run \`z2h-cli deploy\` yourself from the app dir once you're happy with it.`)
144
143
  .requiredOption('--template <id>', 'catalog template id — see `z2h-cli templates --json`')
145
144
  .requiredOption('--input <json>', "JSON string — shape is template-defined, validated wholesale against the template's configSchema (see `z2h-cli templates --json`)")
146
145
  .action(runCommand({
146
+ event: Z2hCliEvents.generateAppTemplateSuccess,
147
147
  data: ([appName, opts]) => ({ app_name: appName, template: opts.template }),
148
148
  }, (appName, opts) => generateCommand(appName, opts)));
149
149
  program
150
150
  .command('edit <app-name>')
151
151
  .description(`Pull the latest version of an app into ${getWorkspaceHome()}/<app-name>, cloning it first if needed`)
152
- .action(runCommand({ data: ([appName]) => ({ app_name: appName }) }, (appName) => editCommand(appName)));
152
+ .action(runCommand({ event: Z2hCliEvents.editSuccess, data: ([appName]) => ({ app_name: appName }) }, (appName) => editCommand(appName)));
153
153
  program
154
154
  .command('grant [email]')
155
155
  .description('Grant access to the current app (email, --public, or both)')
@@ -177,7 +177,7 @@ async function main() {
177
177
  .command('delete')
178
178
  .description('Permanently delete the current app and take it offline (owners/admins only)')
179
179
  .option('--yes', 'skip the confirmation prompt (required for --json / non-interactive)')
180
- .action(runCommand({ data: cwdApp }, (opts) => deleteCommand(opts)));
180
+ .action(runCommand({ event: Z2hCliEvents.deleteSuccess, data: cwdApp }, (opts) => deleteCommand(opts)));
181
181
  const backend = program
182
182
  .command('backend')
183
183
  .description("Inspect the app's backend: validate handler sources or run a handler from the working tree");
@@ -208,7 +208,7 @@ async function main() {
208
208
  program
209
209
  .command('doctor')
210
210
  .description('Check that all Z2H tools, auth, workspace, and plugins are correctly set up')
211
- .action(runCommand({ track: false }, () => doctorCommand()));
211
+ .action(runCommand({}, () => doctorCommand()));
212
212
  await program.parseAsync(process.argv);
213
213
  }
214
214
  void main()
@@ -2,6 +2,7 @@
2
2
  * Shared domain types for the Z2H CLI. Command-scoped option types live
3
3
  * with their command; narrow single-use types stay local to their file.
4
4
  */
5
+ import type { Z2hCliEventName } from '@mondaydotcomorg/z2h-shared-utils/observability';
5
6
  export interface ConsumerPkg {
6
7
  name: string;
7
8
  version?: string;
@@ -60,12 +61,10 @@ export type GrantFailureReason = 'validation' | 'auth' | 'forbidden' | 'not_foun
60
61
  export type PerArgs<A extends unknown[], T> = T | ((args: A) => T);
61
62
  export interface CommandSpec<A extends unknown[]> {
62
63
  /**
63
- * Opt out of telemetry entirely — both the success event and the error event. Default `true`.
64
- * Set `false` for a purely local command with no product-usage signal worth a round-trip.
64
+ * Success-event name from `Z2hCliEvents` (a function when it depends on args, e.g. `deploy --preview`).
65
+ * Omit it to send no success event. Failures are reported either way.
65
66
  */
66
- track?: PerArgs<A, boolean>;
67
- /** Success-event name when it depends on args (e.g. `deploy --preview`); defaults to `successEvent(<command>)`. */
68
- event?: PerArgs<A, string>;
67
+ event?: PerArgs<A, Z2hCliEventName>;
69
68
  /** Prefix in the error message and the `action` dimension. Defaults to the command's own name. */
70
69
  label?: PerArgs<A, string>;
71
70
  /** Extra dimensions sent with both the success and the error event. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,8 @@ export declare function output(human: string, data: unknown): void;
18
18
  * Emit a structured failure payload (JSON mode) or a red error line (human mode),
19
19
  * then exit with the given code.
20
20
  *
21
- * `track: false` skips the telemetry event but still prints and exits. Commands with no
22
- * success event pass it, so their failures don't skew per-command error rates.
21
+ * `track: false` skips the telemetry event but still prints and exits. runCommand passes it
22
+ * for failures that aren't a CLI/infra problem (a consumer's own broken build).
23
23
  */
24
24
  export declare function fail(message: string, payload: Record<string, unknown>, exitCode?: number, track?: boolean): Promise<never>;
25
25
  //# sourceMappingURL=logger.d.ts.map
@@ -45,8 +45,8 @@ function output(human, data) {
45
45
  * Emit a structured failure payload (JSON mode) or a red error line (human mode),
46
46
  * then exit with the given code.
47
47
  *
48
- * `track: false` skips the telemetry event but still prints and exits. Commands with no
49
- * success event pass it, so their failures don't skew per-command error rates.
48
+ * `track: false` skips the telemetry event but still prints and exits. runCommand passes it
49
+ * for failures that aren't a CLI/infra problem (a consumer's own broken build).
50
50
  */
51
51
  async function fail(message, payload, exitCode = 1, track = true) {
52
52
  // Canonical fields go last so a payload key can never shadow `error`/`action`.
@@ -1 +1 @@
1
- {"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../../src/util/run-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,UAAU,CAAC;AAcrD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,EAC5C,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GACnC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAyB7C"}
1
+ {"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../../src/util/run-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,UAAU,CAAC;AAKrD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,EAC5C,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GACnC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAwB7C"}
@@ -1,14 +1,8 @@
1
1
  import { fail } from './logger.mjs';
2
- import { successEvent, trackDuration } from './tracker.mjs';
2
+ import { trackDuration } from './tracker.mjs';
3
3
  import { reasonOf, Z2hError, dataOf, messageOf } from './errors.mjs';
4
4
 
5
- /** A spec field is a constant, a callback over the args, or absent — in which case `fallback` applies. */
6
- const resolveSpecField = (field, args, fallback) => {
7
- if (field === undefined) {
8
- return fallback;
9
- }
10
- return typeof field === 'function' ? field(args) : field;
11
- };
5
+ const pick = (field, args) => typeof field === 'function' ? field(args) : field;
12
6
  /**
13
7
  * Wraps a command body with the whole telemetry and failure contract so no command
14
8
  * hand-rolls it. It times the happy path. On a throw it reads `reason` and `data` straight
@@ -25,20 +19,19 @@ function runCommand(spec, fn) {
25
19
  return async (...allArgs) => {
26
20
  const cmd = allArgs[allArgs.length - 1];
27
21
  const args = allArgs.slice(0, -1);
28
- const name = cmd.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
29
- const resolve = (v, fallback) => resolveSpecField(v, args, fallback);
30
- const track = resolve(spec.track, true);
31
- const label = resolve(spec.label, name);
32
- const event = resolve(spec.event, successEvent(name));
22
+ // Top-level commands hang off the root program (`z2h-cli`), which has no parent itself and isn't
23
+ // part of a command's name. Only a nested command (`backend invoke`) has a non-root parent to prefix.
24
+ const name = cmd.parent?.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
25
+ const label = spec.label ? pick(spec.label, args) : name;
26
+ const event = spec.event ? pick(spec.event, args) : null;
33
27
  const data = spec.data?.(args) ?? {};
34
28
  try {
35
- await (track ? trackDuration(event, () => fn(...args), data) : fn(...args));
29
+ await (event ? trackDuration(event, () => fn(...args), data) : fn(...args));
36
30
  }
37
31
  catch (err) {
38
32
  const reason = reasonOf(err);
39
- // A deploy/preview that failed because the consumer's own build broke isn't a CLI/infra
40
- // problem — don't report it at all, not even as `unknown`.
41
- const shouldTrack = track && reason !== 'build_error';
33
+ // A consumer's own broken build isn't a CLI/infra problem — don't report it at all, not even as `unknown`.
34
+ const shouldTrack = reason !== 'build_error';
42
35
  await fail(`${label} failed: ${messageOf(err)}`, { command: label, reason, ...data, ...dataOf(err) }, spec.exitCode ?? (err instanceof Z2hError ? err.exitCode : 1), shouldTrack);
43
36
  }
44
37
  };
@@ -1,10 +1,2 @@
1
1
  export declare const trackEvent: (name: string, data: Record<string, unknown>) => Promise<void>, trackDuration: <T>(name: string, fn: () => Promise<T>, data?: Record<string, unknown>) => Promise<T>;
2
- /**
3
- * `z2h_cli_<command>_success` — the success-event name for every command, derived by
4
- * util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
5
- * their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
6
- * command names its own event, `deploy --preview`, still goes through this function so the
7
- * convention holds everywhere. See CommandSpec's `event` field in types.ts.
8
- */
9
- export declare function successEvent(command: string): string;
10
2
  //# sourceMappingURL=tracker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../../src/util/tracker.ts"],"names":[],"mappings":"AAcA,eAAO,MAAQ,UAAU,kEAAE,aAAa,uFAItC,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../../src/util/tracker.ts"],"names":[],"mappings":"AAcA,eAAO,MAAQ,UAAU,kEAAE,aAAa,uFAItC,CAAC"}
@@ -12,15 +12,5 @@ const { trackEvent, trackDuration } = createHttpTracker({
12
12
  source: 'z2h-cli',
13
13
  getContext: () => ({ user_name: username, cli_version: cliVersion }),
14
14
  });
15
- /**
16
- * `z2h_cli_<command>_success` — the success-event name for every command, derived by
17
- * util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
18
- * their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
19
- * command names its own event, `deploy --preview`, still goes through this function so the
20
- * convention holds everywhere. See CommandSpec's `event` field in types.ts.
21
- */
22
- function successEvent(command) {
23
- return `z2h_cli_${command.replace(/[\s-]+/g, '_')}_success`;
24
- }
25
15
 
26
- export { successEvent, trackDuration, trackEvent };
16
+ export { trackDuration, trackEvent };
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ const os = require('node:os');
3
3
  const path = require('node:path');
4
4
  const commander = require('commander');
5
5
  const execa = require('execa');
6
+ const observability = require('@mondaydotcomorg/z2h-shared-utils/observability');
6
7
  const util_workspace = require('./util/workspace.js');
7
8
  const commands_build = require('./commands/build.js');
8
9
  const commands_clean = require('./commands/clean.js');
@@ -23,7 +24,6 @@ const commands_backend = require('./commands/backend.js');
23
24
  const commands_doctor = require('./commands/doctor.js');
24
25
  const util_logger = require('./util/logger.js');
25
26
  const util_runCommand = require('./util/run-command.js');
26
- const util_tracker = require('./util/tracker.js');
27
27
  const util_appName = require('./util/app-name.js');
28
28
 
29
29
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -83,14 +83,14 @@ async function main() {
83
83
  program
84
84
  .command('build')
85
85
  .description('Build the consumer into its .zth/ shadow and copy artifacts to ./build/')
86
- // A standalone build failure is the consumer's own code, not a CLI/infra problem — don't
87
- // track it. buildCommand still tags the reason on its thrown error, since deploy's own
88
- // (tracked) failure event benefits from knowing when a build broke it.
89
- .action(util_runCommand.runCommand({ track: false, data: cwdApp, exitCode: 2 }, () => commands_build.buildCommand()));
86
+ // A standalone build failure is the consumer's own code, not a CLI/infra problem — runCommand
87
+ // skips the error event for failures tagged `build_error` (buildCommand tags it on its thrown
88
+ // error). Any other failure here is still reported.
89
+ .action(util_runCommand.runCommand({ event: observability.Z2hCliEvents.buildSuccess, data: cwdApp, exitCode: 2 }, () => commands_build.buildCommand()));
90
90
  program
91
91
  .command('clean')
92
92
  .description('Delete <consumer>/.zth/ and <consumer>/build/')
93
- .action(util_runCommand.runCommand({ track: false, exitCode: 2 }, () => commands_clean.cleanCommand()));
93
+ .action(util_runCommand.runCommand({ exitCode: 2 }, () => commands_clean.cleanCommand()));
94
94
  program
95
95
  .command('deploy')
96
96
  .description('Build, upload to S3, and register the app with the Z2H backend')
@@ -102,9 +102,7 @@ async function main() {
102
102
  .option('--description <text>', 'short (<=200 char) description of the app')
103
103
  .option('--prebuilt-dir <path>', 'internal — used by `generate-app-template`: skip the build step and deploy this already-built directory as-is')
104
104
  .action(util_runCommand.runCommand({
105
- // A preview is a real deploy for observability, tracked as its own event since
106
- // the command name alone (`deploy`) can't distinguish it from a live deploy.
107
- event: ([opts]) => util_tracker.successEvent(opts.preview ? 'preview' : 'deploy'),
105
+ event: ([opts]) => opts.preview ? observability.Z2hCliEvents.previewSuccess : observability.Z2hCliEvents.deploySuccess,
108
106
  label: ([opts]) => (opts.preview ? 'preview' : 'deploy'),
109
107
  data: cwdApp,
110
108
  exitCode: 2,
@@ -119,7 +117,7 @@ async function main() {
119
117
  program
120
118
  .command('create-workspace')
121
119
  .description(`Bootstrap the ${util_workspace.getWorkspaceHome()}/ yarn workspace (one-time setup)`)
122
- .action(util_runCommand.runCommand({ track: false }, () => commands_createWorkspace.createWorkspaceCommand()));
120
+ .action(util_runCommand.runCommand({}, () => commands_createWorkspace.createWorkspaceCommand()));
123
121
  program
124
122
  .command('generate-z2h-token')
125
123
  .description('Open the bigbrain.me token page, capture the token via a loopback callback, and persist it to ~/.zth/auth.json')
@@ -134,6 +132,7 @@ async function main() {
134
132
  .option('--remote <type>', 'Source control remote: s3 (shared storage, recommended, auto-backed-up) or github (manage it yourself in your own repo)')
135
133
  .option('--repo-url <url>', 'GitHub repo URL — required when --remote=github')
136
134
  .action(util_runCommand.runCommand({
135
+ event: observability.Z2hCliEvents.createSuccess,
137
136
  data: ([appName, opts]) => ({
138
137
  app_name: appName,
139
138
  template: opts.template ?? 'default',
@@ -142,20 +141,21 @@ async function main() {
142
141
  }, (appName, opts) => commands_create.createCommand(appName, opts)));
143
142
  program
144
143
  .command('templates')
145
- .description("List the BigBrain Studio (Z2H) template catalog — id, title, description, and each template's widget schema")
146
- .action(util_runCommand.runCommand({}, () => commands_templates.templatesCommand()));
144
+ .description("List the BigBrain Studio (Z2H) template catalog — id, title, description, and each template's config schema")
145
+ .action(util_runCommand.runCommand({ event: observability.Z2hCliEvents.templatesSuccess }, () => commands_templates.templatesCommand()));
147
146
  program
148
147
  .command('generate-app-template <app-name>')
149
148
  .description(`Scaffold + preview a Z2H app/dashboard from a templates catalog entry inside ${util_workspace.getWorkspaceHome()}/ — copies a prebuilt template, patches in real widget data, and deploys a preview. No build, no yarn install, no live deploy — run \`z2h-cli deploy\` yourself from the app dir once you're happy with it.`)
150
149
  .requiredOption('--template <id>', 'catalog template id — see `z2h-cli templates --json`')
151
150
  .requiredOption('--input <json>', "JSON string — shape is template-defined, validated wholesale against the template's configSchema (see `z2h-cli templates --json`)")
152
151
  .action(util_runCommand.runCommand({
152
+ event: observability.Z2hCliEvents.generateAppTemplateSuccess,
153
153
  data: ([appName, opts]) => ({ app_name: appName, template: opts.template }),
154
154
  }, (appName, opts) => commands_generate.generateCommand(appName, opts)));
155
155
  program
156
156
  .command('edit <app-name>')
157
157
  .description(`Pull the latest version of an app into ${util_workspace.getWorkspaceHome()}/<app-name>, cloning it first if needed`)
158
- .action(util_runCommand.runCommand({ data: ([appName]) => ({ app_name: appName }) }, (appName) => commands_edit.editCommand(appName)));
158
+ .action(util_runCommand.runCommand({ event: observability.Z2hCliEvents.editSuccess, data: ([appName]) => ({ app_name: appName }) }, (appName) => commands_edit.editCommand(appName)));
159
159
  program
160
160
  .command('grant [email]')
161
161
  .description('Grant access to the current app (email, --public, or both)')
@@ -183,7 +183,7 @@ async function main() {
183
183
  .command('delete')
184
184
  .description('Permanently delete the current app and take it offline (owners/admins only)')
185
185
  .option('--yes', 'skip the confirmation prompt (required for --json / non-interactive)')
186
- .action(util_runCommand.runCommand({ data: cwdApp }, (opts) => commands_delete.deleteCommand(opts)));
186
+ .action(util_runCommand.runCommand({ event: observability.Z2hCliEvents.deleteSuccess, data: cwdApp }, (opts) => commands_delete.deleteCommand(opts)));
187
187
  const backend = program
188
188
  .command('backend')
189
189
  .description("Inspect the app's backend: validate handler sources or run a handler from the working tree");
@@ -214,7 +214,7 @@ async function main() {
214
214
  program
215
215
  .command('doctor')
216
216
  .description('Check that all Z2H tools, auth, workspace, and plugins are correctly set up')
217
- .action(util_runCommand.runCommand({ track: false }, () => commands_doctor.doctorCommand()));
217
+ .action(util_runCommand.runCommand({}, () => commands_doctor.doctorCommand()));
218
218
  await program.parseAsync(process.argv);
219
219
  }
220
220
  void main()
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * Shared domain types for the Z2H CLI. Command-scoped option types live
3
3
  * with their command; narrow single-use types stay local to their file.
4
4
  */
5
+ import type { Z2hCliEventName } from '@mondaydotcomorg/z2h-shared-utils/observability';
5
6
  export interface ConsumerPkg {
6
7
  name: string;
7
8
  version?: string;
@@ -60,12 +61,10 @@ export type GrantFailureReason = 'validation' | 'auth' | 'forbidden' | 'not_foun
60
61
  export type PerArgs<A extends unknown[], T> = T | ((args: A) => T);
61
62
  export interface CommandSpec<A extends unknown[]> {
62
63
  /**
63
- * Opt out of telemetry entirely — both the success event and the error event. Default `true`.
64
- * Set `false` for a purely local command with no product-usage signal worth a round-trip.
64
+ * Success-event name from `Z2hCliEvents` (a function when it depends on args, e.g. `deploy --preview`).
65
+ * Omit it to send no success event. Failures are reported either way.
65
66
  */
66
- track?: PerArgs<A, boolean>;
67
- /** Success-event name when it depends on args (e.g. `deploy --preview`); defaults to `successEvent(<command>)`. */
68
- event?: PerArgs<A, string>;
67
+ event?: PerArgs<A, Z2hCliEventName>;
69
68
  /** Prefix in the error message and the `action` dimension. Defaults to the command's own name. */
70
69
  label?: PerArgs<A, string>;
71
70
  /** Extra dimensions sent with both the success and the error event. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,8 @@ export declare function output(human: string, data: unknown): void;
18
18
  * Emit a structured failure payload (JSON mode) or a red error line (human mode),
19
19
  * then exit with the given code.
20
20
  *
21
- * `track: false` skips the telemetry event but still prints and exits. Commands with no
22
- * success event pass it, so their failures don't skew per-command error rates.
21
+ * `track: false` skips the telemetry event but still prints and exits. runCommand passes it
22
+ * for failures that aren't a CLI/infra problem (a consumer's own broken build).
23
23
  */
24
24
  export declare function fail(message: string, payload: Record<string, unknown>, exitCode?: number, track?: boolean): Promise<never>;
25
25
  //# sourceMappingURL=logger.d.ts.map
@@ -51,8 +51,8 @@ function output(human, data) {
51
51
  * Emit a structured failure payload (JSON mode) or a red error line (human mode),
52
52
  * then exit with the given code.
53
53
  *
54
- * `track: false` skips the telemetry event but still prints and exits. Commands with no
55
- * success event pass it, so their failures don't skew per-command error rates.
54
+ * `track: false` skips the telemetry event but still prints and exits. runCommand passes it
55
+ * for failures that aren't a CLI/infra problem (a consumer's own broken build).
56
56
  */
57
57
  async function fail(message, payload, exitCode = 1, track = true) {
58
58
  // Canonical fields go last so a payload key can never shadow `error`/`action`.
@@ -1 +1 @@
1
- {"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../src/util/run-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,UAAU,CAAC;AAcrD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,EAC5C,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GACnC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAyB7C"}
1
+ {"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../src/util/run-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,UAAU,CAAC;AAKrD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,EAC5C,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GACnC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAwB7C"}
@@ -4,13 +4,7 @@ const util_logger = require('./logger.js');
4
4
  const util_tracker = require('./tracker.js');
5
5
  const util_errors = require('./errors.js');
6
6
 
7
- /** A spec field is a constant, a callback over the args, or absent — in which case `fallback` applies. */
8
- const resolveSpecField = (field, args, fallback) => {
9
- if (field === undefined) {
10
- return fallback;
11
- }
12
- return typeof field === 'function' ? field(args) : field;
13
- };
7
+ const pick = (field, args) => typeof field === 'function' ? field(args) : field;
14
8
  /**
15
9
  * Wraps a command body with the whole telemetry and failure contract so no command
16
10
  * hand-rolls it. It times the happy path. On a throw it reads `reason` and `data` straight
@@ -27,20 +21,19 @@ function runCommand(spec, fn) {
27
21
  return async (...allArgs) => {
28
22
  const cmd = allArgs[allArgs.length - 1];
29
23
  const args = allArgs.slice(0, -1);
30
- const name = cmd.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
31
- const resolve = (v, fallback) => resolveSpecField(v, args, fallback);
32
- const track = resolve(spec.track, true);
33
- const label = resolve(spec.label, name);
34
- const event = resolve(spec.event, util_tracker.successEvent(name));
24
+ // Top-level commands hang off the root program (`z2h-cli`), which has no parent itself and isn't
25
+ // part of a command's name. Only a nested command (`backend invoke`) has a non-root parent to prefix.
26
+ const name = cmd.parent?.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
27
+ const label = spec.label ? pick(spec.label, args) : name;
28
+ const event = spec.event ? pick(spec.event, args) : null;
35
29
  const data = spec.data?.(args) ?? {};
36
30
  try {
37
- await (track ? util_tracker.trackDuration(event, () => fn(...args), data) : fn(...args));
31
+ await (event ? util_tracker.trackDuration(event, () => fn(...args), data) : fn(...args));
38
32
  }
39
33
  catch (err) {
40
34
  const reason = util_errors.reasonOf(err);
41
- // A deploy/preview that failed because the consumer's own build broke isn't a CLI/infra
42
- // problem — don't report it at all, not even as `unknown`.
43
- const shouldTrack = track && reason !== 'build_error';
35
+ // A consumer's own broken build isn't a CLI/infra problem — don't report it at all, not even as `unknown`.
36
+ const shouldTrack = reason !== 'build_error';
44
37
  await util_logger.fail(`${label} failed: ${util_errors.messageOf(err)}`, { command: label, reason, ...data, ...util_errors.dataOf(err) }, spec.exitCode ?? (err instanceof util_errors.Z2hError ? err.exitCode : 1), shouldTrack);
45
38
  }
46
39
  };
@@ -1,10 +1,2 @@
1
1
  export declare const trackEvent: (name: string, data: Record<string, unknown>) => Promise<void>, trackDuration: <T>(name: string, fn: () => Promise<T>, data?: Record<string, unknown>) => Promise<T>;
2
- /**
3
- * `z2h_cli_<command>_success` — the success-event name for every command, derived by
4
- * util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
5
- * their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
6
- * command names its own event, `deploy --preview`, still goes through this function so the
7
- * convention holds everywhere. See CommandSpec's `event` field in types.ts.
8
- */
9
- export declare function successEvent(command: string): string;
10
2
  //# sourceMappingURL=tracker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/util/tracker.ts"],"names":[],"mappings":"AAcA,eAAO,MAAQ,UAAU,kEAAE,aAAa,uFAItC,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/util/tracker.ts"],"names":[],"mappings":"AAcA,eAAO,MAAQ,UAAU,kEAAE,aAAa,uFAItC,CAAC"}
@@ -19,17 +19,6 @@ const { trackEvent, trackDuration } = observability.createHttpTracker({
19
19
  source: 'z2h-cli',
20
20
  getContext: () => ({ user_name: username, cli_version: cliVersion }),
21
21
  });
22
- /**
23
- * `z2h_cli_<command>_success` — the success-event name for every command, derived by
24
- * util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
25
- * their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
26
- * command names its own event, `deploy --preview`, still goes through this function so the
27
- * convention holds everywhere. See CommandSpec's `event` field in types.ts.
28
- */
29
- function successEvent(command) {
30
- return `z2h_cli_${command.replace(/[\s-]+/g, '_')}_success`;
31
- }
32
22
 
33
- exports.successEvent = successEvent;
34
23
  exports.trackDuration = trackDuration;
35
24
  exports.trackEvent = trackEvent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/z2h-cli",
3
- "version": "0.33.2",
3
+ "version": "0.33.3-feature-chezki-templates-poc-93101e2",
4
4
  "bin": "./bin/z2h-cli.js",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -25,6 +25,11 @@
25
25
  "require": "./dist/s3.js",
26
26
  "types": "./dist/s3.d.ts"
27
27
  },
28
+ "./dashboard-templates": {
29
+ "import": "./dist/esm/dashboard-templates.mjs",
30
+ "require": "./dist/dashboard-templates.js",
31
+ "types": "./dist/dashboard-templates.d.ts"
32
+ },
28
33
  "./package.json": "./package.json"
29
34
  },
30
35
  "nx": {
@@ -52,7 +57,7 @@
52
57
  "dependencies": {
53
58
  "@aws-sdk/client-s3": "^3.706.0",
54
59
  "@mondaydotcomorg/z2h-dashboard-templates": "^0.4.0",
55
- "@mondaydotcomorg/z2h-shared-utils": "^0.7.1",
60
+ "@mondaydotcomorg/z2h-shared-utils": "^0.7.2",
56
61
  "acorn": "^8.14.0",
57
62
  "acorn-walk": "^8.3.4",
58
63
  "chokidar": "^3.6.0",
@@ -1,5 +1,6 @@
1
1
  import { parse, type ExportNamedDeclaration, type ModuleDeclaration, type Program, type Statement } from 'acorn';
2
2
  import { z } from 'zod';
3
+ import { Z2hCliEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
3
4
  import { trackEvent } from '../../util/tracker';
4
5
  import { exportsHandler } from './handler-declaration';
5
6
  import { messages } from './messages';
@@ -44,7 +45,7 @@ const isExport = (node: Statement | ModuleDeclaration): node is ExportLike =>
44
45
 
45
46
  /** Reports the failure, then returns the error for the caller to throw. */
46
47
  function parseError(message: string): Error {
47
- void trackEvent('z2h_cli_handler_parse_error', { error: message });
48
+ void trackEvent(Z2hCliEvents.handlerParseError, { error: message });
48
49
  return new Error(message);
49
50
  }
50
51
 
@@ -0,0 +1,5 @@
1
+ export {
2
+ loadDashboardTemplatesCatalog,
3
+ findTemplate,
4
+ type DashboardTemplateEntry,
5
+ } from './util/dashboard-templates/load-catalog';
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { Command } from 'commander';
5
5
  import { execa } from 'execa';
6
+ import { Z2hCliEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
6
7
  import { getWorkspaceHome } from './util/workspace';
7
8
  import { buildCommand } from './commands/build';
8
9
  import { cleanCommand } from './commands/clean';
@@ -23,7 +24,6 @@ import { backendInvokeCommand, backendValidateCommand } from './commands/backend
23
24
  import { doctorCommand } from './commands/doctor';
24
25
  import { errorLog, fail, getOutputMode, setOutputMode } from './util/logger';
25
26
  import { runCommand } from './util/run-command';
26
- import { successEvent } from './util/tracker';
27
27
  import { getCwdAppName } from './util/app-name';
28
28
 
29
29
  const { version } = createRequire(import.meta.url)('@mondaydotcomorg/z2h-cli/package.json') as { version: string };
@@ -104,15 +104,15 @@ async function main(): Promise<void> {
104
104
  program
105
105
  .command('build')
106
106
  .description('Build the consumer into its .zth/ shadow and copy artifacts to ./build/')
107
- // A standalone build failure is the consumer's own code, not a CLI/infra problem — don't
108
- // track it. buildCommand still tags the reason on its thrown error, since deploy's own
109
- // (tracked) failure event benefits from knowing when a build broke it.
110
- .action(runCommand({ track: false, data: cwdApp, exitCode: 2 }, () => buildCommand()));
107
+ // A standalone build failure is the consumer's own code, not a CLI/infra problem — runCommand
108
+ // skips the error event for failures tagged `build_error` (buildCommand tags it on its thrown
109
+ // error). Any other failure here is still reported.
110
+ .action(runCommand({ event: Z2hCliEvents.buildSuccess, data: cwdApp, exitCode: 2 }, () => buildCommand()));
111
111
 
112
112
  program
113
113
  .command('clean')
114
114
  .description('Delete <consumer>/.zth/ and <consumer>/build/')
115
- .action(runCommand({ track: false, exitCode: 2 }, () => cleanCommand()));
115
+ .action(runCommand({ exitCode: 2 }, () => cleanCommand()));
116
116
 
117
117
  program
118
118
  .command('deploy')
@@ -139,9 +139,8 @@ async function main(): Promise<void> {
139
139
  .action(
140
140
  runCommand(
141
141
  {
142
- // A preview is a real deploy for observability, tracked as its own event since
143
- // the command name alone (`deploy`) can't distinguish it from a live deploy.
144
- event: ([opts]: [DeployCliOptions]) => successEvent(opts.preview ? 'preview' : 'deploy'),
142
+ event: ([opts]: [DeployCliOptions]) =>
143
+ opts.preview ? Z2hCliEvents.previewSuccess : Z2hCliEvents.deploySuccess,
145
144
  label: ([opts]: [DeployCliOptions]) => (opts.preview ? 'preview' : 'deploy'),
146
145
  data: cwdApp,
147
146
  exitCode: 2,
@@ -166,7 +165,7 @@ async function main(): Promise<void> {
166
165
  program
167
166
  .command('create-workspace')
168
167
  .description(`Bootstrap the ${getWorkspaceHome()}/ yarn workspace (one-time setup)`)
169
- .action(runCommand({ track: false }, () => createWorkspaceCommand()));
168
+ .action(runCommand({}, () => createWorkspaceCommand()));
170
169
 
171
170
  program
172
171
  .command('generate-z2h-token')
@@ -192,6 +191,7 @@ async function main(): Promise<void> {
192
191
  .action(
193
192
  runCommand(
194
193
  {
194
+ event: Z2hCliEvents.createSuccess,
195
195
  data: ([appName, opts]: [string, CreateCliOptions]) => ({
196
196
  app_name: appName,
197
197
  template: opts.template ?? 'default',
@@ -205,9 +205,9 @@ async function main(): Promise<void> {
205
205
  program
206
206
  .command('templates')
207
207
  .description(
208
- "List the BigBrain Studio (Z2H) template catalog — id, title, description, and each template's widget schema"
208
+ "List the BigBrain Studio (Z2H) template catalog — id, title, description, and each template's config schema"
209
209
  )
210
- .action(runCommand({}, () => templatesCommand()));
210
+ .action(runCommand({ event: Z2hCliEvents.templatesSuccess }, () => templatesCommand()));
211
211
 
212
212
  program
213
213
  .command('generate-app-template <app-name>')
@@ -222,6 +222,7 @@ async function main(): Promise<void> {
222
222
  .action(
223
223
  runCommand(
224
224
  {
225
+ event: Z2hCliEvents.generateAppTemplateSuccess,
225
226
  data: ([appName, opts]: [string, GenerateOptions]) => ({ app_name: appName, template: opts.template }),
226
227
  },
227
228
  (appName: string, opts: GenerateOptions) => generateCommand(appName, opts)
@@ -232,7 +233,10 @@ async function main(): Promise<void> {
232
233
  .command('edit <app-name>')
233
234
  .description(`Pull the latest version of an app into ${getWorkspaceHome()}/<app-name>, cloning it first if needed`)
234
235
  .action(
235
- runCommand({ data: ([appName]: [string]) => ({ app_name: appName }) }, (appName: string) => editCommand(appName))
236
+ runCommand(
237
+ { event: Z2hCliEvents.editSuccess, data: ([appName]: [string]) => ({ app_name: appName }) },
238
+ (appName: string) => editCommand(appName)
239
+ )
236
240
  );
237
241
 
238
242
  program
@@ -275,7 +279,9 @@ async function main(): Promise<void> {
275
279
  .command('delete')
276
280
  .description('Permanently delete the current app and take it offline (owners/admins only)')
277
281
  .option('--yes', 'skip the confirmation prompt (required for --json / non-interactive)')
278
- .action(runCommand({ data: cwdApp }, (opts: { yes?: boolean }) => deleteCommand(opts)));
282
+ .action(
283
+ runCommand({ event: Z2hCliEvents.deleteSuccess, data: cwdApp }, (opts: { yes?: boolean }) => deleteCommand(opts))
284
+ );
279
285
 
280
286
  const backend = program
281
287
  .command('backend')
@@ -324,7 +330,7 @@ async function main(): Promise<void> {
324
330
  program
325
331
  .command('doctor')
326
332
  .description('Check that all Z2H tools, auth, workspace, and plugins are correctly set up')
327
- .action(runCommand({ track: false }, () => doctorCommand()));
333
+ .action(runCommand({}, () => doctorCommand()));
328
334
 
329
335
  await program.parseAsync(process.argv);
330
336
  }
package/src/types.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * Shared domain types for the Z2H CLI. Command-scoped option types live
3
3
  * with their command; narrow single-use types stay local to their file.
4
4
  */
5
+ import type { Z2hCliEventName } from '@mondaydotcomorg/z2h-shared-utils/observability';
5
6
 
6
7
  // ─────────── Consumer pkg management ───────────
7
8
  export interface ConsumerPkg {
@@ -96,12 +97,10 @@ export type PerArgs<A extends unknown[], T> = T | ((args: A) => T);
96
97
 
97
98
  export interface CommandSpec<A extends unknown[]> {
98
99
  /**
99
- * Opt out of telemetry entirely — both the success event and the error event. Default `true`.
100
- * Set `false` for a purely local command with no product-usage signal worth a round-trip.
100
+ * Success-event name from `Z2hCliEvents` (a function when it depends on args, e.g. `deploy --preview`).
101
+ * Omit it to send no success event. Failures are reported either way.
101
102
  */
102
- track?: PerArgs<A, boolean>;
103
- /** Success-event name when it depends on args (e.g. `deploy --preview`); defaults to `successEvent(<command>)`. */
104
- event?: PerArgs<A, string>;
103
+ event?: PerArgs<A, Z2hCliEventName>;
105
104
  /** Prefix in the error message and the `action` dimension. Defaults to the command's own name. */
106
105
  label?: PerArgs<A, string>;
107
106
  /** Extra dimensions sent with both the success and the error event. */
@@ -1,5 +1,6 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest';
2
2
  import { Command } from 'commander';
3
+ import { Z2hCliEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
3
4
  import { runCommand } from '../run-command';
4
5
  import { DeployError } from '../errors';
5
6
  import { setOutputMode } from '../logger';
@@ -8,12 +9,12 @@ import { trackEvent, trackDuration } from '../tracker';
8
9
  vi.mock('../tracker', () => ({
9
10
  trackEvent: vi.fn().mockResolvedValue(undefined),
10
11
  trackDuration: vi.fn((_name: string, fn: () => Promise<unknown>) => fn()),
11
- successEvent: (command: string) => `z2h_cli_${command.replace(/[\s-]+/g, '_')}_success`,
12
12
  }));
13
13
 
14
14
  describe('runCommand: build_error is never reported, other reasons still are', () => {
15
15
  afterEach(() => {
16
16
  vi.mocked(trackEvent).mockClear();
17
+ vi.mocked(trackDuration).mockClear();
17
18
  setOutputMode('human');
18
19
  });
19
20
 
@@ -23,7 +24,7 @@ describe('runCommand: build_error is never reported, other reasons still are', (
23
24
  throw new Error('__exit__');
24
25
  });
25
26
  vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
26
- const handler = runCommand({}, () => {
27
+ const handler = runCommand({ event: Z2hCliEvents.deploySuccess }, () => {
27
28
  throw err;
28
29
  });
29
30
  await expect(handler(cmd)).rejects.toThrow('__exit__');
@@ -49,24 +50,44 @@ describe('runCommand: naming/tracking convention', () => {
49
50
  setOutputMode('human');
50
51
  });
51
52
 
52
- it('derives its name (and label) from the Command instance, no name param needed', async () => {
53
- const cmd = new Command('migrate');
54
- const handler = runCommand({}, () => Promise.resolve('ok'));
55
- await handler(cmd);
56
- expect(trackDuration).toHaveBeenCalledWith('z2h_cli_migrate_success', expect.any(Function), {});
53
+ // index.ts hangs every command off a root program named `z2h-cli`.
54
+ const program = new Command().name('z2h-cli');
55
+ const failWith = async (cmd: Command) => {
56
+ vi.spyOn(process, 'exit').mockImplementation(() => {
57
+ throw new Error('__exit__');
58
+ });
59
+ vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
60
+ await expect(
61
+ runCommand({ event: Z2hCliEvents.createSuccess }, () => {
62
+ throw new Error('boom');
63
+ })(cmd)
64
+ ).rejects.toThrow('__exit__');
65
+ vi.mocked(process.exit).mockRestore();
66
+ vi.mocked(process.stderr.write).mockRestore();
67
+ };
68
+
69
+ it('names a top-level command without the root program', async () => {
70
+ await failWith(program.command('create'));
71
+ expect(trackEvent).toHaveBeenCalledWith(
72
+ Z2hCliEvents.error,
73
+ expect.objectContaining({ action: 'create', error: 'create failed: boom' })
74
+ );
57
75
  });
58
76
 
59
77
  it('composes a nested command\'s name from its parent, e.g. "backend invoke"', async () => {
60
- const backend = new Command('backend');
61
- const invoke = backend.command('invoke');
62
- const handler = runCommand({}, () => Promise.resolve('ok'));
63
- await handler(invoke);
64
- expect(trackDuration).toHaveBeenCalledWith('z2h_cli_backend_invoke_success', expect.any(Function), {});
78
+ await failWith(program.command('backend').command('invoke'));
79
+ expect(trackEvent).toHaveBeenCalledWith(Z2hCliEvents.error, expect.objectContaining({ action: 'backend invoke' }));
65
80
  });
66
81
 
67
- it('track: false skips telemetry entirely, success or failure', async () => {
82
+ it('sends the given event when tracked', async () => {
83
+ const cmd = new Command('create');
84
+ await runCommand({ event: Z2hCliEvents.createSuccess }, () => Promise.resolve('ok'))(cmd);
85
+ expect(trackDuration).toHaveBeenCalledWith('z2h_app_created', expect.any(Function), {});
86
+ });
87
+
88
+ it('the success event is opt-in, but failures are reported either way', async () => {
68
89
  const cmd = new Command('doctor');
69
- await runCommand({ track: false }, () => Promise.resolve('ok'))(cmd);
90
+ await runCommand({}, () => Promise.resolve('ok'))(cmd);
70
91
  expect(trackDuration).not.toHaveBeenCalled();
71
92
  expect(trackEvent).not.toHaveBeenCalled();
72
93
 
@@ -74,25 +95,25 @@ describe('runCommand: naming/tracking convention', () => {
74
95
  throw new Error('__exit__');
75
96
  });
76
97
  vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
77
- const failing = runCommand({ track: false }, () => {
98
+ const failing = runCommand({}, () => {
78
99
  throw new Error('boom');
79
100
  });
80
101
  await expect(failing(cmd)).rejects.toThrow('__exit__');
81
- expect(trackEvent).not.toHaveBeenCalled();
102
+ expect(trackEvent).toHaveBeenCalledWith(Z2hCliEvents.error, expect.objectContaining({ action: 'doctor' }));
82
103
  exit.mockRestore();
83
104
  vi.mocked(process.stderr.write).mockRestore();
84
105
  });
85
106
 
86
- it('a per-args event override beats the derived name, e.g. deploy --preview', async () => {
107
+ it('a per-args event picks the name, e.g. deploy --preview', async () => {
87
108
  const cmd = new Command('deploy');
88
109
  const handler = runCommand(
89
110
  {
90
111
  event: ([opts]: [{ preview?: boolean }]) =>
91
- opts.preview ? 'z2h_cli_preview_success' : 'z2h_cli_deploy_success',
112
+ opts.preview ? Z2hCliEvents.previewSuccess : Z2hCliEvents.deploySuccess,
92
113
  },
93
114
  (_opts: { preview?: boolean }) => Promise.resolve('ok')
94
115
  );
95
116
  await handler({ preview: true }, cmd);
96
- expect(trackDuration).toHaveBeenCalledWith('z2h_cli_preview_success', expect.any(Function), {});
117
+ expect(trackDuration).toHaveBeenCalledWith('z2h_app_preview_success', expect.any(Function), {});
97
118
  });
98
119
  });
@@ -53,8 +53,8 @@ export function output(human: string, data: unknown): void {
53
53
  * Emit a structured failure payload (JSON mode) or a red error line (human mode),
54
54
  * then exit with the given code.
55
55
  *
56
- * `track: false` skips the telemetry event but still prints and exits. Commands with no
57
- * success event pass it, so their failures don't skew per-command error rates.
56
+ * `track: false` skips the telemetry event but still prints and exits. runCommand passes it
57
+ * for failures that aren't a CLI/infra problem (a consumer's own broken build).
58
58
  */
59
59
  export async function fail(
60
60
  message: string,
@@ -1,20 +1,11 @@
1
1
  import { fail } from './logger';
2
- import { trackDuration, successEvent } from './tracker';
2
+ import { trackDuration } from './tracker';
3
3
  import { Z2hError, reasonOf, dataOf, messageOf } from './errors';
4
4
  import type { Command } from 'commander';
5
5
  import type { CommandSpec, PerArgs } from '../types';
6
6
 
7
- type SpecFieldFn<A extends unknown[], T> = (args: A) => T;
8
- type SpecFieldResolver<A extends unknown[]> = <T>(field: PerArgs<A, T> | undefined, fallback: T) => T;
9
- type ResolveSpecField = <A extends unknown[], T>(field: PerArgs<A, T> | undefined, args: A, fallback: T) => T;
10
-
11
- /** A spec field is a constant, a callback over the args, or absent — in which case `fallback` applies. */
12
- const resolveSpecField: ResolveSpecField = (field, args, fallback) => {
13
- if (field === undefined) {
14
- return fallback;
15
- }
16
- return typeof field === 'function' ? (field as SpecFieldFn<typeof args, typeof fallback>)(args) : field;
17
- };
7
+ const pick = <A extends unknown[], T>(field: PerArgs<A, T>, args: A): T =>
8
+ typeof field === 'function' ? (field as (args: A) => T)(args) : field;
18
9
 
19
10
  /**
20
11
  * Wraps a command body with the whole telemetry and failure contract so no command
@@ -35,19 +26,18 @@ export function runCommand<A extends unknown[]>(
35
26
  return async (...allArgs: [...A, Command]): Promise<void> => {
36
27
  const cmd = allArgs[allArgs.length - 1] as Command;
37
28
  const args = allArgs.slice(0, -1) as A;
38
- const name = cmd.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
39
- const resolve: SpecFieldResolver<A> = (v, fallback) => resolveSpecField(v, args, fallback);
40
- const track = resolve(spec.track, true);
41
- const label = resolve(spec.label, name);
42
- const event = resolve(spec.event, successEvent(name));
29
+ // Top-level commands hang off the root program (`z2h-cli`), which has no parent itself and isn't
30
+ // part of a command's name. Only a nested command (`backend invoke`) has a non-root parent to prefix.
31
+ const name = cmd.parent?.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
32
+ const label = spec.label ? pick(spec.label, args) : name;
33
+ const event = spec.event ? pick(spec.event, args) : null;
43
34
  const data = spec.data?.(args) ?? {};
44
35
  try {
45
- await (track ? trackDuration(event, () => fn(...args), data) : fn(...args));
36
+ await (event ? trackDuration(event, () => fn(...args), data) : fn(...args));
46
37
  } catch (err) {
47
38
  const reason = reasonOf(err);
48
- // A deploy/preview that failed because the consumer's own build broke isn't a CLI/infra
49
- // problem — don't report it at all, not even as `unknown`.
50
- const shouldTrack = track && reason !== 'build_error';
39
+ // A consumer's own broken build isn't a CLI/infra problem — don't report it at all, not even as `unknown`.
40
+ const shouldTrack = reason !== 'build_error';
51
41
  await fail(
52
42
  `${label} failed: ${messageOf(err)}`,
53
43
  { command: label, reason, ...data, ...dataOf(err) },
@@ -17,14 +17,3 @@ export const { trackEvent, trackDuration } = createHttpTracker({
17
17
  source: 'z2h-cli',
18
18
  getContext: () => ({ user_name: username, cli_version: cliVersion }),
19
19
  });
20
-
21
- /**
22
- * `z2h_cli_<command>_success` — the success-event name for every command, derived by
23
- * util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
24
- * their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
25
- * command names its own event, `deploy --preview`, still goes through this function so the
26
- * convention holds everywhere. See CommandSpec's `event` field in types.ts.
27
- */
28
- export function successEvent(command: string): string {
29
- return `z2h_cli_${command.replace(/[\s-]+/g, '_')}_success`;
30
- }