@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.
- package/dist/backend/parser/parse.d.ts.map +1 -1
- package/dist/backend/parser/parse.js +2 -1
- package/dist/dashboard-templates.d.ts +2 -0
- package/dist/dashboard-templates.d.ts.map +1 -0
- package/dist/dashboard-templates.js +8 -0
- package/dist/esm/backend/parser/parse.d.ts.map +1 -1
- package/dist/esm/backend/parser/parse.mjs +2 -1
- package/dist/esm/dashboard-templates.d.ts +2 -0
- package/dist/esm/dashboard-templates.d.ts.map +1 -0
- package/dist/esm/dashboard-templates.mjs +1 -0
- package/dist/esm/index.mjs +15 -15
- package/dist/esm/types.d.ts +4 -5
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/util/logger.d.ts +2 -2
- package/dist/esm/util/logger.mjs +2 -2
- package/dist/esm/util/run-command.d.ts.map +1 -1
- package/dist/esm/util/run-command.mjs +10 -17
- package/dist/esm/util/tracker.d.ts +0 -8
- package/dist/esm/util/tracker.d.ts.map +1 -1
- package/dist/esm/util/tracker.mjs +1 -11
- package/dist/index.js +15 -15
- package/dist/types.d.ts +4 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/util/logger.d.ts +2 -2
- package/dist/util/logger.js +2 -2
- package/dist/util/run-command.d.ts.map +1 -1
- package/dist/util/run-command.js +9 -16
- package/dist/util/tracker.d.ts +0 -8
- package/dist/util/tracker.d.ts.map +1 -1
- package/dist/util/tracker.js +0 -11
- package/package.json +7 -2
- package/src/backend/parser/parse.ts +2 -1
- package/src/dashboard-templates.ts +5 -0
- package/src/index.ts +21 -15
- package/src/types.ts +4 -5
- package/src/util/__tests__/run-command.test.ts +40 -19
- package/src/util/logger.ts +2 -2
- package/src/util/run-command.ts +11 -21
- 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;
|
|
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(
|
|
33
|
+
void util_tracker.trackEvent(observability.Z2hCliEvents.handlerParseError, { error: message });
|
|
33
34
|
return new Error(message);
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
@@ -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;
|
|
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(
|
|
31
|
+
void trackEvent(Z2hCliEvents.handlerParseError, { error: message });
|
|
31
32
|
return new Error(message);
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
@@ -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';
|
package/dist/esm/index.mjs
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.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 —
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
.action(runCommand({
|
|
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({
|
|
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
|
-
|
|
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({
|
|
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
|
|
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({
|
|
211
|
+
.action(runCommand({}, () => doctorCommand()));
|
|
212
212
|
await program.parseAsync(process.argv);
|
|
213
213
|
}
|
|
214
214
|
void main()
|
package/dist/esm/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
|
-
*
|
|
64
|
-
*
|
|
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
|
-
|
|
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. */
|
package/dist/esm/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
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.
|
|
22
|
-
*
|
|
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
|
package/dist/esm/util/logger.mjs
CHANGED
|
@@ -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.
|
|
49
|
-
*
|
|
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;
|
|
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 {
|
|
2
|
+
import { trackDuration } from './tracker.mjs';
|
|
3
3
|
import { reasonOf, Z2hError, dataOf, messageOf } from './errors.mjs';
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const label =
|
|
32
|
-
const event =
|
|
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 (
|
|
29
|
+
await (event ? trackDuration(event, () => fn(...args), data) : fn(...args));
|
|
36
30
|
}
|
|
37
31
|
catch (err) {
|
|
38
32
|
const reason = reasonOf(err);
|
|
39
|
-
// A
|
|
40
|
-
|
|
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
|
|
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 {
|
|
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 —
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
.action(util_runCommand.runCommand({
|
|
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({
|
|
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
|
-
|
|
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({
|
|
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
|
|
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({
|
|
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
|
-
*
|
|
64
|
-
*
|
|
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
|
-
|
|
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. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/util/logger.d.ts
CHANGED
|
@@ -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.
|
|
22
|
-
*
|
|
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
|
package/dist/util/logger.js
CHANGED
|
@@ -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.
|
|
55
|
-
*
|
|
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;
|
|
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"}
|
package/dist/util/run-command.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const label =
|
|
34
|
-
const event =
|
|
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 (
|
|
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
|
|
42
|
-
|
|
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
|
};
|
package/dist/util/tracker.d.ts
CHANGED
|
@@ -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
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/util/tracker.ts"],"names":[],"mappings":"AAcA,eAAO,MAAQ,UAAU,kEAAE,aAAa,uFAItC,CAAC"}
|
package/dist/util/tracker.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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(
|
|
48
|
+
void trackEvent(Z2hCliEvents.handlerParseError, { error: message });
|
|
48
49
|
return new Error(message);
|
|
49
50
|
}
|
|
50
51
|
|
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 —
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
.action(runCommand({
|
|
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({
|
|
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
|
-
|
|
143
|
-
|
|
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({
|
|
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
|
|
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(
|
|
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(
|
|
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({
|
|
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
|
-
*
|
|
100
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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('
|
|
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({
|
|
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({
|
|
98
|
+
const failing = runCommand({}, () => {
|
|
78
99
|
throw new Error('boom');
|
|
79
100
|
});
|
|
80
101
|
await expect(failing(cmd)).rejects.toThrow('__exit__');
|
|
81
|
-
expect(trackEvent).
|
|
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
|
|
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 ?
|
|
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('
|
|
117
|
+
expect(trackDuration).toHaveBeenCalledWith('z2h_app_preview_success', expect.any(Function), {});
|
|
97
118
|
});
|
|
98
119
|
});
|
package/src/util/logger.ts
CHANGED
|
@@ -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.
|
|
57
|
-
*
|
|
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,
|
package/src/util/run-command.ts
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import { fail } from './logger';
|
|
2
|
-
import { trackDuration
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const label =
|
|
42
|
-
const event =
|
|
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 (
|
|
36
|
+
await (event ? trackDuration(event, () => fn(...args), data) : fn(...args));
|
|
46
37
|
} catch (err) {
|
|
47
38
|
const reason = reasonOf(err);
|
|
48
|
-
// A
|
|
49
|
-
|
|
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) },
|
package/src/util/tracker.ts
CHANGED
|
@@ -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
|
-
}
|