@mondaydotcomorg/z2h-cli 0.15.2-feat-eitantsh-z2h-admin-panel-package-0b3010b → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/commands/__tests__/deploy.test.d.ts +2 -0
  2. package/dist/commands/__tests__/deploy.test.d.ts.map +1 -0
  3. package/dist/commands/build.d.ts +8 -1
  4. package/dist/commands/build.d.ts.map +1 -1
  5. package/dist/commands/build.js +12 -0
  6. package/dist/commands/create.d.ts.map +1 -1
  7. package/dist/commands/create.js +1 -12
  8. package/dist/commands/deploy.d.ts +3 -0
  9. package/dist/commands/deploy.d.ts.map +1 -1
  10. package/dist/commands/deploy.js +69 -72
  11. package/dist/commands/doctor.d.ts.map +1 -1
  12. package/dist/commands/doctor.js +1 -11
  13. package/dist/constants.d.ts +2 -0
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +10 -5
  16. package/dist/esm/commands/__tests__/deploy.test.d.ts +2 -0
  17. package/dist/esm/commands/__tests__/deploy.test.d.ts.map +1 -0
  18. package/dist/esm/commands/build.d.ts +8 -1
  19. package/dist/esm/commands/build.d.ts.map +1 -1
  20. package/dist/esm/commands/build.mjs +9 -1
  21. package/dist/esm/commands/create.d.ts.map +1 -1
  22. package/dist/esm/commands/create.mjs +2 -13
  23. package/dist/esm/commands/deploy.d.ts +3 -0
  24. package/dist/esm/commands/deploy.d.ts.map +1 -1
  25. package/dist/esm/commands/deploy.mjs +72 -74
  26. package/dist/esm/commands/doctor.d.ts.map +1 -1
  27. package/dist/esm/commands/doctor.mjs +1 -11
  28. package/dist/esm/constants.d.ts +2 -0
  29. package/dist/esm/constants.d.ts.map +1 -1
  30. package/dist/esm/constants.mjs +9 -6
  31. package/dist/esm/index.mjs +9 -15
  32. package/dist/esm/migrations/006-remove-start-script.d.ts +3 -0
  33. package/dist/esm/migrations/006-remove-start-script.d.ts.map +1 -0
  34. package/dist/esm/migrations/006-remove-start-script.mjs +22 -0
  35. package/dist/esm/util/__tests__/app-name.test.d.ts +2 -0
  36. package/dist/esm/util/__tests__/app-name.test.d.ts.map +1 -0
  37. package/dist/esm/util/app-name.d.ts +17 -0
  38. package/dist/esm/util/app-name.d.ts.map +1 -1
  39. package/dist/esm/util/app-name.mjs +45 -1
  40. package/dist/esm/util/auth/broker-credential-provider.d.ts +7 -5
  41. package/dist/esm/util/auth/broker-credential-provider.d.ts.map +1 -1
  42. package/dist/esm/util/auth/broker-credential-provider.mjs +28 -16
  43. package/dist/esm/util/broker/__tests__/app.test.d.ts +2 -0
  44. package/dist/esm/util/broker/__tests__/app.test.d.ts.map +1 -0
  45. package/dist/esm/util/broker/app.d.ts +15 -0
  46. package/dist/esm/util/broker/app.d.ts.map +1 -1
  47. package/dist/esm/util/broker/app.mjs +44 -1
  48. package/dist/esm/util/prompt-visibility.d.ts +2 -0
  49. package/dist/esm/util/prompt-visibility.d.ts.map +1 -0
  50. package/dist/esm/util/prompt-visibility.mjs +19 -0
  51. package/dist/index.js +9 -15
  52. package/dist/migrations/006-remove-start-script.d.ts +3 -0
  53. package/dist/migrations/006-remove-start-script.d.ts.map +1 -0
  54. package/dist/migrations/006-remove-start-script.js +24 -0
  55. package/dist/util/__tests__/app-name.test.d.ts +2 -0
  56. package/dist/util/__tests__/app-name.test.d.ts.map +1 -0
  57. package/dist/util/app-name.d.ts +17 -0
  58. package/dist/util/app-name.d.ts.map +1 -1
  59. package/dist/util/app-name.js +49 -2
  60. package/dist/util/auth/broker-credential-provider.d.ts +7 -5
  61. package/dist/util/auth/broker-credential-provider.d.ts.map +1 -1
  62. package/dist/util/auth/broker-credential-provider.js +28 -16
  63. package/dist/util/broker/__tests__/app.test.d.ts +2 -0
  64. package/dist/util/broker/__tests__/app.test.d.ts.map +1 -0
  65. package/dist/util/broker/app.d.ts +15 -0
  66. package/dist/util/broker/app.d.ts.map +1 -1
  67. package/dist/util/broker/app.js +44 -0
  68. package/dist/util/prompt-visibility.d.ts +2 -0
  69. package/dist/util/prompt-visibility.d.ts.map +1 -0
  70. package/dist/util/prompt-visibility.js +21 -0
  71. package/migrations.json +5 -0
  72. package/package.json +1 -1
  73. package/src/commands/__tests__/create.test.ts +5 -0
  74. package/src/commands/__tests__/deploy.test.ts +215 -0
  75. package/src/commands/__tests__/doctor.test.ts +0 -12
  76. package/src/commands/build.ts +20 -2
  77. package/src/commands/create.ts +2 -18
  78. package/src/commands/deploy.ts +113 -93
  79. package/src/commands/doctor.ts +1 -20
  80. package/src/constants.ts +10 -5
  81. package/src/index.ts +31 -25
  82. package/src/migrations/006-remove-start-script.ts +24 -0
  83. package/src/shadow/__tests__/merge-package-json.test.ts +0 -1
  84. package/src/templates/app/CLAUDE.md +3 -3
  85. package/src/templates/app/package.json.tmpl +0 -1
  86. package/src/templates/workspace/README.md +2 -2
  87. package/src/util/__tests__/app-name.test.ts +78 -0
  88. package/src/util/app-name.ts +55 -0
  89. package/src/util/auth/__tests__/broker-credential-provider.test.ts +53 -1
  90. package/src/util/auth/broker-credential-provider.ts +41 -16
  91. package/src/util/broker/__tests__/app.test.ts +104 -0
  92. package/src/util/broker/app.ts +68 -0
  93. package/src/util/prompt-visibility.ts +18 -0
  94. package/dist/commands/dev.d.ts +0 -5
  95. package/dist/commands/dev.d.ts.map +0 -1
  96. package/dist/commands/dev.js +0 -92
  97. package/dist/esm/commands/dev.d.ts +0 -5
  98. package/dist/esm/commands/dev.d.ts.map +0 -1
  99. package/dist/esm/commands/dev.mjs +0 -85
  100. package/src/commands/dev.ts +0 -93
@@ -1,5 +1,7 @@
1
1
  import { brokerBaseUrl, routingHeaders } from '../../constants.mjs';
2
2
  import { readZthAuth } from '../auth/auth-store.mjs';
3
+ import { info } from '../logger.mjs';
4
+ import { resolveAppVisibility } from '../prompt-visibility.mjs';
3
5
 
4
6
  /**
5
7
  * Fetches a single app's current state from the broker.
@@ -23,5 +25,46 @@ async function fetchApp(appName) {
23
25
  }
24
26
  return res.json();
25
27
  }
28
+ async function registerApp(appName, token, version, entry, visibility, gitRemote) {
29
+ const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/register`, {
30
+ method: 'POST',
31
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
32
+ body: JSON.stringify({ appName, version, entry, visibility, gitRemote }),
33
+ });
34
+ if (!res.ok) {
35
+ const text = await res.text();
36
+ throw new Error(`App registration failed (${res.status}): ${text.slice(0, 1024)}`);
37
+ }
38
+ }
39
+ /** Releases a new version onto an app that already exists. */
40
+ async function updateApp(appName, token, version, entry, gitRemote) {
41
+ const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/update`, {
42
+ method: 'POST',
43
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
44
+ body: JSON.stringify({ appName, version, entry, gitRemote }),
45
+ });
46
+ if (!res.ok) {
47
+ const text = await res.text();
48
+ throw new Error(`App update failed (${res.status}): ${text.slice(0, 1024)}`);
49
+ }
50
+ }
51
+ /**
52
+ * v1 creates the app row (+ owner grant, prompting for visibility); later
53
+ * versions release onto it via update instead — the two are separate
54
+ * endpoints, so this picks based on the version it's handed. Shared by
55
+ * deploy and preview so this branching logic lives in exactly one place.
56
+ */
57
+ async function registerOrUpdateApp(opts) {
58
+ const { appName, token, nextVersion, entry, gitRemote } = opts;
59
+ if (nextVersion === 1) {
60
+ const visibility = await resolveAppVisibility(appName);
61
+ info(`registering "${appName}" → v${nextVersion} (${visibility})`);
62
+ await registerApp(appName, token, nextVersion, entry, visibility, gitRemote);
63
+ }
64
+ else {
65
+ info(`updating "${appName}" → v${nextVersion}`);
66
+ await updateApp(appName, token, nextVersion, entry, gitRemote);
67
+ }
68
+ }
26
69
 
27
- export { fetchApp };
70
+ export { fetchApp, registerOrUpdateApp };
@@ -0,0 +1,2 @@
1
+ export declare function resolveAppVisibility(appName: string): Promise<'public' | 'private'>;
2
+ //# sourceMappingURL=prompt-visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-visibility.d.ts","sourceRoot":"","sources":["../../../src/util/prompt-visibility.ts"],"names":[],"mappings":"AAMA,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAWzF"}
@@ -0,0 +1,19 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { getOutputMode } from './logger.mjs';
3
+
4
+ // First registration only: decide whether the app is born public or private. The
5
+ // choice is passed to the register endpoint (default private). Non-interactive
6
+ // runs (--json or no TTY) always default to private.
7
+ async function resolveAppVisibility(appName) {
8
+ if (getOutputMode() === 'json' || !process.stdout.isTTY) {
9
+ return 'private';
10
+ }
11
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
12
+ const answer = await new Promise(resolve => {
13
+ rl.question(`Make "${appName}" public to all monday.com employees? (y/N) `, resolve);
14
+ });
15
+ rl.close();
16
+ return answer.trim().toLowerCase() === 'y' ? 'public' : 'private';
17
+ }
18
+
19
+ export { resolveAppVisibility };
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@ const path = require('node:path');
4
4
  const commander = require('commander');
5
5
  const execa = require('execa');
6
6
  const util_workspace = require('./util/workspace.js');
7
- const commands_dev = require('./commands/dev.js');
8
7
  const commands_build = require('./commands/build.js');
9
8
  const commands_clean = require('./commands/clean.js');
10
9
  const commands_deploy = require('./commands/deploy.js');
@@ -85,18 +84,6 @@ async function main() {
85
84
  await util_logger.fail(`build failed: ${err.message}`, { command: 'build' }, 2);
86
85
  }
87
86
  });
88
- program
89
- .command('dev')
90
- .description('Start the Trident dev server against .zth/ with native Vite HMR')
91
- .option('--open', 'open the browser automatically when the dev server is ready')
92
- .action(async (opts) => {
93
- try {
94
- await commands_dev.devCommand(opts);
95
- }
96
- catch (err) {
97
- await util_logger.fail(`dev failed: ${err.message}`, { command: 'dev' }, 2);
98
- }
99
- });
100
87
  program
101
88
  .command('clean')
102
89
  .description('Delete <consumer>/.zth/ and <consumer>/build/')
@@ -112,16 +99,23 @@ async function main() {
112
99
  .command('deploy')
113
100
  .description('Build, upload to S3, and register the app with the Z2H backend')
114
101
  .option('--dry-run', 'print what would happen without uploading anything')
102
+ .option('--preview [previewId]', 'build an ephemeral, shareable preview instead of deploying — no git required, no deploy lock, never touches the live version; pass an existing previewId to update it instead of minting a new one')
103
+ .option('--open', 'open the resulting app/preview URL in the browser on success')
115
104
  .option('--remote <type>', 'First deploy only: source control remote to initialise — s3 (shared storage) or github (your repo). Error if the app already has a git remote.')
116
105
  .option('--repo-url <url>', 'First deploy only: GitHub repo URL — required when --remote=github. Error if the app already has a git remote.')
117
106
  .action(async (opts) => {
118
107
  try {
119
- await util_tracker.trackDuration('z2h_app_deploy_success', () => commands_deploy.deployCommand(opts), {
108
+ await util_tracker.trackDuration(opts.preview ? 'z2h_app_preview_success' : 'z2h_app_deploy_success', () => commands_deploy.deployCommand({
109
+ ...opts,
110
+ preview: Boolean(opts.preview),
111
+ previewId: typeof opts.preview === 'string' ? opts.preview : undefined,
112
+ }), {
120
113
  app_name: util_appName.getCwdAppName(),
121
114
  });
122
115
  }
123
116
  catch (err) {
124
- await util_logger.fail(`deploy failed: ${err.message}`, { command: 'deploy' }, 2);
117
+ const label = opts.preview ? 'preview' : 'deploy';
118
+ await util_logger.fail(`${label} failed: ${err.message}`, { command: label }, 2);
125
119
  }
126
120
  });
127
121
  program
@@ -0,0 +1,3 @@
1
+ import type { Tree } from '@nx/devkit';
2
+ export default function removeStartScript(tree: Tree): void;
3
+ //# sourceMappingURL=006-remove-start-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"006-remove-start-script.d.ts","sourceRoot":"","sources":["../../src/migrations/006-remove-start-script.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMvC,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAiB1D"}
@@ -0,0 +1,24 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+
3
+ // `dev` is gone from the CLI (superseded by `deploy --preview`), so any
4
+ // consumer's `start` script still pointing at it is now a dangling command.
5
+ // Only removes the exact value the template ever wrote — leaves a
6
+ // consumer-customized `start` script alone.
7
+ function removeStartScript(tree) {
8
+ for (const entry of tree.children('.')) {
9
+ if (entry.startsWith('.')) {
10
+ continue;
11
+ }
12
+ const pkgPath = `${entry}/package.json`;
13
+ if (!tree.exists(pkgPath)) {
14
+ continue;
15
+ }
16
+ const pkg = JSON.parse(tree.read(pkgPath, 'utf-8'));
17
+ if (pkg.scripts?.start === 'z2h-cli dev') {
18
+ delete pkg.scripts.start;
19
+ tree.write(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
20
+ }
21
+ }
22
+ }
23
+
24
+ exports.default = removeStartScript;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=app-name.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-name.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/app-name.test.ts"],"names":[],"mappings":""}
@@ -10,4 +10,21 @@ export declare function getCwdAppName(): string | undefined;
10
10
  * emitted bundle is `MF-mf-<app>`).
11
11
  */
12
12
  export declare function mfAppName(name: string): string;
13
+ /**
14
+ * Validates a de-scoped, kebab-case app name against every rule shared by
15
+ * `create` and `deploy` (incl. `deploy --preview`) — pattern, reserved
16
+ * platform names, and the preview-prefix reservation. Throws with a
17
+ * friendly message on the first violation.
18
+ */
19
+ export declare function validateAppName(appName: string): void;
20
+ /**
21
+ * Builds the slug for a preview's synthetic RDS app row:
22
+ * `zth-preview-<shortid>-<realAppName>`, pre-`mf-`-prefixing. Uniqueness
23
+ * comes entirely from `shortId` (derived from the previewId, already a
24
+ * randomUUID() — 32 bits of entropy); truncating the human-readable tail to
25
+ * fit the cap is safe on its own, a shortId collision is astronomically
26
+ * unlikely at POC scale and would just 409 on registerApp for a retry.
27
+ */
28
+ export declare function buildPreviewSlug(appName: string, previewId: string): string;
29
+ export declare function isPreviewAppName(appName: string): boolean;
13
30
  //# sourceMappingURL=app-name.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-name.d.ts","sourceRoot":"","sources":["../../src/util/app-name.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C"}
1
+ {"version":3,"file":"app-name.d.ts","sourceRoot":"","sources":["../../src/util/app-name.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAOD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAiBrD;AAMD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
- const fs = require('node:fs');
3
+ const node_fs = require('node:fs');
4
+ const constants = require('../constants.js');
4
5
 
5
6
  /**
6
7
  * Strip any npm scope (`@scope/`) from a package name.
@@ -11,7 +12,7 @@ function cleanAppName(name) {
11
12
  }
12
13
  function getCwdAppName() {
13
14
  try {
14
- return JSON.parse(fs.readFileSync('package.json', 'utf8')).name;
15
+ return JSON.parse(node_fs.readFileSync('package.json', 'utf8')).name;
15
16
  }
16
17
  catch {
17
18
  return undefined;
@@ -25,7 +26,53 @@ function getCwdAppName() {
25
26
  function mfAppName(name) {
26
27
  return `mf-${cleanAppName(name)}`;
27
28
  }
29
+ // Names reserved by the host MF (mf-bigbrain-zth) for platform pages under
30
+ // /bigbrain-vibe/<name>. Creating an app with one of these would shadow the
31
+ // platform page in the URL, so we refuse upfront.
32
+ const RESERVED_APP_NAMES = new Set(['generate-z2h-token']);
33
+ /**
34
+ * Validates a de-scoped, kebab-case app name against every rule shared by
35
+ * `create` and `deploy` (incl. `deploy --preview`) — pattern, reserved
36
+ * platform names, and the preview-prefix reservation. Throws with a
37
+ * friendly message on the first violation.
38
+ */
39
+ function validateAppName(appName) {
40
+ if (RESERVED_APP_NAMES.has(appName)) {
41
+ throw new Error(`App name "${appName}" is reserved by the Z2H host for a platform page under /bigbrain-vibe/${appName}. Pick a different name.`);
42
+ }
43
+ if (!constants.APP_NAME_PATTERN.test(appName)) {
44
+ throw new Error(`App name "${appName}" is invalid. Use lowercase kebab-case (a-z, 0-9, -), ` +
45
+ `2-50 chars, starting with a letter.`);
46
+ }
47
+ if (appName.startsWith(constants.PREVIEW_APP_NAME_PREFIX)) {
48
+ throw new Error(`App names starting with "${constants.PREVIEW_APP_NAME_PREFIX}" are reserved for the ephemeral preview mechanism. Pick a different name.`);
49
+ }
50
+ }
51
+ // Preview slugs are capped here so the mf-prefixed name (`mf-` + slug) stays
52
+ // within APP_NAME_PATTERN's 50-char limit (3 chars reserved for `mf-`).
53
+ const PREVIEW_SLUG_MAX_LEN = 47;
54
+ /**
55
+ * Builds the slug for a preview's synthetic RDS app row:
56
+ * `zth-preview-<shortid>-<realAppName>`, pre-`mf-`-prefixing. Uniqueness
57
+ * comes entirely from `shortId` (derived from the previewId, already a
58
+ * randomUUID() — 32 bits of entropy); truncating the human-readable tail to
59
+ * fit the cap is safe on its own, a shortId collision is astronomically
60
+ * unlikely at POC scale and would just 409 on registerApp for a retry.
61
+ */
62
+ function buildPreviewSlug(appName, previewId) {
63
+ const shortId = previewId.replace(/-/g, '').slice(0, 8);
64
+ const base = `${constants.PREVIEW_APP_NAME_PREFIX}${shortId}-${cleanAppName(appName)}`;
65
+ // Trailing hyphens are stripped post-slice — a cut that lands right after a
66
+ // hyphen in appName would otherwise leave one dangling.
67
+ return base.slice(0, PREVIEW_SLUG_MAX_LEN).replace(/-+$/, '');
68
+ }
69
+ function isPreviewAppName(appName) {
70
+ return appName.startsWith(`mf-${constants.PREVIEW_APP_NAME_PREFIX}`);
71
+ }
28
72
 
73
+ exports.buildPreviewSlug = buildPreviewSlug;
29
74
  exports.cleanAppName = cleanAppName;
30
75
  exports.getCwdAppName = getCwdAppName;
76
+ exports.isPreviewAppName = isPreviewAppName;
31
77
  exports.mfAppName = mfAppName;
78
+ exports.validateAppName = validateAppName;
@@ -8,12 +8,14 @@ export declare const DEPLOY_VERSION_PATH = "/z2h-cli/apps/version";
8
8
  */
9
9
  export declare function fetchDeployVersion(appName: string): Promise<number>;
10
10
  /**
11
- * AWS SDK credential provider that fetches short-lived deploy credentials
12
- * from the bigbrain-zth STS broker for a specific version. Credentials are
13
- * cached in memory until 5 minutes before expiry; concurrent calls share a
14
- * single in-flight fetch.
11
+ * AWS SDK credential provider that fetches short-lived credentials from the
12
+ * bigbrain-zth STS broker, scoped to `appName` — or, when `previewId` is
13
+ * given, to a single `previews/<appName>/<previewId>/*` slot instead of the
14
+ * full deploy path. Credentials are cached in memory until 5 minutes before
15
+ * expiry; concurrent calls share a single in-flight fetch.
15
16
  */
16
- export declare function brokerCredentialProvider({ appName }: {
17
+ export declare function brokerCredentialProvider({ appName, previewId, }: {
17
18
  appName: string;
19
+ previewId?: string;
18
20
  }): AwsCredentialIdentityProvider;
19
21
  //# sourceMappingURL=broker-credential-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"broker-credential-provider.d.ts","sourceRoot":"","sources":["../../../src/util/auth/broker-credential-provider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAyB,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAE3F,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAC1D,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AA4C3D;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzE;AAuBD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,6BAA6B,CAuBxG"}
1
+ {"version":3,"file":"broker-credential-provider.d.ts","sourceRoot":"","sources":["../../../src/util/auth/broker-credential-provider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAyB,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAE3F,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAC1D,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AA4C3D;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzE;AA4DD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,SAAS,GACV,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,6BAA6B,CAIhC"}
@@ -47,31 +47,33 @@ async function fetchDeployVersion(appName) {
47
47
  const body = (await res.json());
48
48
  return body.version ?? 0;
49
49
  }
50
- async function fetchStsOnce(appName) {
51
- const res = await postToBroker(STS_BROKER_PATH, { appName });
50
+ async function requestSts(path, body, errorLabel) {
51
+ const res = await postToBroker(path, body);
52
52
  assertBrokerOk(res);
53
53
  if (!res.ok) {
54
54
  const text = await res.text();
55
- throw new Error(`Z2H deploy broker returned ${res.status}: ${text.slice(0, 1024)}`);
55
+ throw new Error(`Z2H ${errorLabel} broker returned ${res.status}: ${text.slice(0, 1024)}`);
56
56
  }
57
- const body = (await res.json());
58
- if (!body.accessKeyId || !body.secretAccessKey || !body.sessionToken || !body.expiration) {
59
- throw new Error('Z2H deploy broker response missing required credential fields');
57
+ const responseBody = (await res.json());
58
+ if (!responseBody.accessKeyId ||
59
+ !responseBody.secretAccessKey ||
60
+ !responseBody.sessionToken ||
61
+ !responseBody.expiration) {
62
+ throw new Error(`Z2H ${errorLabel} broker response missing required credential fields`);
60
63
  }
61
64
  return {
62
- accessKeyId: body.accessKeyId,
63
- secretAccessKey: body.secretAccessKey,
64
- sessionToken: body.sessionToken,
65
- expiration: new Date(body.expiration),
65
+ accessKeyId: responseBody.accessKeyId,
66
+ secretAccessKey: responseBody.secretAccessKey,
67
+ sessionToken: responseBody.sessionToken,
68
+ expiration: new Date(responseBody.expiration),
66
69
  };
67
70
  }
68
71
  /**
69
- * AWS SDK credential provider that fetches short-lived deploy credentials
70
- * from the bigbrain-zth STS broker for a specific version. Credentials are
71
- * cached in memory until 5 minutes before expiry; concurrent calls share a
72
- * single in-flight fetch.
72
+ * Wraps a raw STS fetch in the shared cache/coalescing state machine:
73
+ * credentials are cached in memory until 5 minutes before expiry, and
74
+ * concurrent calls share a single in-flight fetch.
73
75
  */
74
- function brokerCredentialProvider({ appName }) {
76
+ function createCachedCredentialProvider(fetchOnce) {
75
77
  let cached;
76
78
  let inflight;
77
79
  return async () => {
@@ -82,7 +84,7 @@ function brokerCredentialProvider({ appName }) {
82
84
  if (inflight) {
83
85
  return inflight;
84
86
  }
85
- inflight = fetchStsOnce(appName)
87
+ inflight = fetchOnce()
86
88
  .then(credentials => {
87
89
  cached = credentials;
88
90
  return credentials;
@@ -93,6 +95,16 @@ function brokerCredentialProvider({ appName }) {
93
95
  return inflight;
94
96
  };
95
97
  }
98
+ /**
99
+ * AWS SDK credential provider that fetches short-lived credentials from the
100
+ * bigbrain-zth STS broker, scoped to `appName` — or, when `previewId` is
101
+ * given, to a single `previews/<appName>/<previewId>/*` slot instead of the
102
+ * full deploy path. Credentials are cached in memory until 5 minutes before
103
+ * expiry; concurrent calls share a single in-flight fetch.
104
+ */
105
+ function brokerCredentialProvider({ appName, previewId, }) {
106
+ return createCachedCredentialProvider(() => requestSts(STS_BROKER_PATH, { appName, previewId }, previewId ? 'preview' : 'deploy'));
107
+ }
96
108
 
97
109
  exports.DEPLOY_VERSION_PATH = DEPLOY_VERSION_PATH;
98
110
  exports.STS_BROKER_PATH = STS_BROKER_PATH;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=app.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.test.d.ts","sourceRoot":"","sources":["../../../../src/util/broker/__tests__/app.test.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
+ import type { ManifestEntry } from '../manifest';
1
2
  export interface AppInfo {
2
3
  appName: string;
3
4
  version: number;
@@ -11,4 +12,18 @@ export interface AppInfo {
11
12
  * Returns null if the app has never been deployed (404).
12
13
  */
13
14
  export declare function fetchApp(appName: string): Promise<AppInfo | null>;
15
+ export interface RegisterOrUpdateOptions {
16
+ appName: string;
17
+ token: string;
18
+ nextVersion: number;
19
+ entry: ManifestEntry;
20
+ gitRemote: string;
21
+ }
22
+ /**
23
+ * v1 creates the app row (+ owner grant, prompting for visibility); later
24
+ * versions release onto it via update instead — the two are separate
25
+ * endpoints, so this picks based on the version it's handed. Shared by
26
+ * deploy and preview so this branching logic lives in exactly one place.
27
+ */
28
+ export declare function registerOrUpdateApp(opts: RegisterOrUpdateOptions): Promise<void>;
14
29
  //# sourceMappingURL=app.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/util/broker/app.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAiBvE"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/util/broker/app.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAiBvE;AAwCD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtF"}
@@ -2,6 +2,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const constants = require('../../constants.js');
4
4
  const util_auth_authStore = require('../auth/auth-store.js');
5
+ const util_logger = require('../logger.js');
6
+ const util_promptVisibility = require('../prompt-visibility.js');
5
7
 
6
8
  /**
7
9
  * Fetches a single app's current state from the broker.
@@ -25,5 +27,47 @@ async function fetchApp(appName) {
25
27
  }
26
28
  return res.json();
27
29
  }
30
+ async function registerApp(appName, token, version, entry, visibility, gitRemote) {
31
+ const res = await fetch(`${constants.brokerBaseUrl()}/z2h-cli/apps/register`, {
32
+ method: 'POST',
33
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...constants.routingHeaders() },
34
+ body: JSON.stringify({ appName, version, entry, visibility, gitRemote }),
35
+ });
36
+ if (!res.ok) {
37
+ const text = await res.text();
38
+ throw new Error(`App registration failed (${res.status}): ${text.slice(0, 1024)}`);
39
+ }
40
+ }
41
+ /** Releases a new version onto an app that already exists. */
42
+ async function updateApp(appName, token, version, entry, gitRemote) {
43
+ const res = await fetch(`${constants.brokerBaseUrl()}/z2h-cli/apps/update`, {
44
+ method: 'POST',
45
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...constants.routingHeaders() },
46
+ body: JSON.stringify({ appName, version, entry, gitRemote }),
47
+ });
48
+ if (!res.ok) {
49
+ const text = await res.text();
50
+ throw new Error(`App update failed (${res.status}): ${text.slice(0, 1024)}`);
51
+ }
52
+ }
53
+ /**
54
+ * v1 creates the app row (+ owner grant, prompting for visibility); later
55
+ * versions release onto it via update instead — the two are separate
56
+ * endpoints, so this picks based on the version it's handed. Shared by
57
+ * deploy and preview so this branching logic lives in exactly one place.
58
+ */
59
+ async function registerOrUpdateApp(opts) {
60
+ const { appName, token, nextVersion, entry, gitRemote } = opts;
61
+ if (nextVersion === 1) {
62
+ const visibility = await util_promptVisibility.resolveAppVisibility(appName);
63
+ util_logger.info(`registering "${appName}" → v${nextVersion} (${visibility})`);
64
+ await registerApp(appName, token, nextVersion, entry, visibility, gitRemote);
65
+ }
66
+ else {
67
+ util_logger.info(`updating "${appName}" → v${nextVersion}`);
68
+ await updateApp(appName, token, nextVersion, entry, gitRemote);
69
+ }
70
+ }
28
71
 
29
72
  exports.fetchApp = fetchApp;
73
+ exports.registerOrUpdateApp = registerOrUpdateApp;
@@ -0,0 +1,2 @@
1
+ export declare function resolveAppVisibility(appName: string): Promise<'public' | 'private'>;
2
+ //# sourceMappingURL=prompt-visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-visibility.d.ts","sourceRoot":"","sources":["../../src/util/prompt-visibility.ts"],"names":[],"mappings":"AAMA,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAWzF"}
@@ -0,0 +1,21 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const node_readline = require('node:readline');
4
+ const util_logger = require('./logger.js');
5
+
6
+ // First registration only: decide whether the app is born public or private. The
7
+ // choice is passed to the register endpoint (default private). Non-interactive
8
+ // runs (--json or no TTY) always default to private.
9
+ async function resolveAppVisibility(appName) {
10
+ if (util_logger.getOutputMode() === 'json' || !process.stdout.isTTY) {
11
+ return 'private';
12
+ }
13
+ const rl = node_readline.createInterface({ input: process.stdin, output: process.stdout });
14
+ const answer = await new Promise(resolve => {
15
+ rl.question(`Make "${appName}" public to all monday.com employees? (y/N) `, resolve);
16
+ });
17
+ rl.close();
18
+ return answer.trim().toLowerCase() === 'y' ? 'public' : 'private';
19
+ }
20
+
21
+ exports.resolveAppVisibility = resolveAppVisibility;
package/migrations.json CHANGED
@@ -24,6 +24,11 @@
24
24
  "version": "0.11.5",
25
25
  "description": "Fix shadow .zth/package.json trident-toolkit \"*\" → workspace-root pinned range to prevent react version conflict",
26
26
  "migrate": "./dist/migrations/005-fix-shadow-toolkit-wildcard"
27
+ },
28
+ "remove-start-script": {
29
+ "version": "0.15.3",
30
+ "description": "Remove the start script (z2h-cli dev) from consumer package.json — dev is gone, superseded by deploy --preview",
31
+ "migrate": "./dist/migrations/006-remove-start-script"
27
32
  }
28
33
  }
29
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/z2h-cli",
3
- "version": "0.15.2-feat-eitantsh-z2h-admin-panel-package-0b3010b",
3
+ "version": "0.16.0",
4
4
  "bin": "./bin/z2h-cli.js",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -68,6 +68,11 @@ describe('create', () => {
68
68
  await expect(createCommand('1starts-with-digit')).rejects.toThrow(/lowercase kebab-case/);
69
69
  });
70
70
 
71
+ it('rejects app names starting with the reserved preview prefix', async () => {
72
+ await makeWorkspace(tmp);
73
+ await expect(createCommand('zth-preview-my-app')).rejects.toThrow(/reserved for the ephemeral preview mechanism/);
74
+ });
75
+
71
76
  it('refuses when the target app dir already exists and is non-empty', async () => {
72
77
  await makeWorkspace(tmp);
73
78
  const appDir = path.join(tmp, 'demo-app');