@mondaydotcomorg/z2h-cli 0.15.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/__tests__/deploy.test.d.ts +2 -0
- package/dist/commands/__tests__/deploy.test.d.ts.map +1 -0
- package/dist/commands/build.d.ts +8 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +12 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +1 -12
- package/dist/commands/deploy.d.ts +3 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +69 -72
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +1 -11
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -5
- package/dist/esm/commands/__tests__/deploy.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/deploy.test.d.ts.map +1 -0
- package/dist/esm/commands/build.d.ts +8 -1
- package/dist/esm/commands/build.d.ts.map +1 -1
- package/dist/esm/commands/build.mjs +9 -1
- package/dist/esm/commands/create.d.ts.map +1 -1
- package/dist/esm/commands/create.mjs +2 -13
- package/dist/esm/commands/deploy.d.ts +3 -0
- package/dist/esm/commands/deploy.d.ts.map +1 -1
- package/dist/esm/commands/deploy.mjs +72 -74
- package/dist/esm/commands/doctor.d.ts.map +1 -1
- package/dist/esm/commands/doctor.mjs +1 -11
- package/dist/esm/constants.d.ts +2 -0
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.mjs +9 -6
- package/dist/esm/index.mjs +9 -15
- package/dist/esm/migrations/006-remove-start-script.d.ts +3 -0
- package/dist/esm/migrations/006-remove-start-script.d.ts.map +1 -0
- package/dist/esm/migrations/006-remove-start-script.mjs +22 -0
- package/dist/esm/util/__tests__/app-name.test.d.ts +2 -0
- package/dist/esm/util/__tests__/app-name.test.d.ts.map +1 -0
- package/dist/esm/util/app-name.d.ts +17 -0
- package/dist/esm/util/app-name.d.ts.map +1 -1
- package/dist/esm/util/app-name.mjs +45 -1
- package/dist/esm/util/auth/broker-credential-provider.d.ts +7 -5
- package/dist/esm/util/auth/broker-credential-provider.d.ts.map +1 -1
- package/dist/esm/util/auth/broker-credential-provider.mjs +28 -16
- package/dist/esm/util/broker/__tests__/app.test.d.ts +2 -0
- package/dist/esm/util/broker/__tests__/app.test.d.ts.map +1 -0
- package/dist/esm/util/broker/app.d.ts +15 -0
- package/dist/esm/util/broker/app.d.ts.map +1 -1
- package/dist/esm/util/broker/app.mjs +44 -1
- package/dist/esm/util/prompt-visibility.d.ts +2 -0
- package/dist/esm/util/prompt-visibility.d.ts.map +1 -0
- package/dist/esm/util/prompt-visibility.mjs +19 -0
- package/dist/index.js +9 -15
- package/dist/migrations/006-remove-start-script.d.ts +3 -0
- package/dist/migrations/006-remove-start-script.d.ts.map +1 -0
- package/dist/migrations/006-remove-start-script.js +24 -0
- package/dist/util/__tests__/app-name.test.d.ts +2 -0
- package/dist/util/__tests__/app-name.test.d.ts.map +1 -0
- package/dist/util/app-name.d.ts +17 -0
- package/dist/util/app-name.d.ts.map +1 -1
- package/dist/util/app-name.js +49 -2
- package/dist/util/auth/broker-credential-provider.d.ts +7 -5
- package/dist/util/auth/broker-credential-provider.d.ts.map +1 -1
- package/dist/util/auth/broker-credential-provider.js +28 -16
- package/dist/util/broker/__tests__/app.test.d.ts +2 -0
- package/dist/util/broker/__tests__/app.test.d.ts.map +1 -0
- package/dist/util/broker/app.d.ts +15 -0
- package/dist/util/broker/app.d.ts.map +1 -1
- package/dist/util/broker/app.js +44 -0
- package/dist/util/prompt-visibility.d.ts +2 -0
- package/dist/util/prompt-visibility.d.ts.map +1 -0
- package/dist/util/prompt-visibility.js +21 -0
- package/migrations.json +5 -0
- package/package.json +1 -1
- package/src/commands/__tests__/create.test.ts +5 -0
- package/src/commands/__tests__/deploy.test.ts +215 -0
- package/src/commands/__tests__/doctor.test.ts +0 -12
- package/src/commands/build.ts +20 -2
- package/src/commands/create.ts +2 -18
- package/src/commands/deploy.ts +113 -93
- package/src/commands/doctor.ts +1 -20
- package/src/constants.ts +10 -5
- package/src/index.ts +31 -25
- package/src/migrations/006-remove-start-script.ts +24 -0
- package/src/shadow/__tests__/merge-package-json.test.ts +0 -1
- package/src/templates/app/CLAUDE.md +3 -3
- package/src/templates/app/package.json.tmpl +0 -1
- package/src/templates/workspace/README.md +2 -2
- package/src/util/__tests__/app-name.test.ts +78 -0
- package/src/util/app-name.ts +55 -0
- package/src/util/auth/__tests__/broker-credential-provider.test.ts +53 -1
- package/src/util/auth/broker-credential-provider.ts +41 -16
- package/src/util/broker/__tests__/app.test.ts +104 -0
- package/src/util/broker/app.ts +68 -0
- package/src/util/prompt-visibility.ts +18 -0
- package/dist/commands/dev.d.ts +0 -5
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -92
- package/dist/esm/commands/dev.d.ts +0 -5
- package/dist/esm/commands/dev.d.ts.map +0 -1
- package/dist/esm/commands/dev.mjs +0 -85
- package/src/commands/dev.ts +0 -93
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import os from 'node:os';
|
|
3
|
-
import {
|
|
4
|
-
import { readJson
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { readJson } from 'fs-extra';
|
|
5
4
|
import { info, output, getOutputMode, warn } from '../util/logger.mjs';
|
|
6
5
|
import { resolveConsumerPaths } from '../shadow/paths.mjs';
|
|
7
6
|
import { fetchAppTags, fetchTagCatalog, setAppTags } from '../util/tags.mjs';
|
|
8
7
|
import { uploadDir } from '../util/s3/upload.mjs';
|
|
9
|
-
import { BUCKET_WRITE_NAME, brokerBaseUrl, routingHeaders, BUCKET_READ_DNS_URL } from '../constants.mjs';
|
|
8
|
+
import { BUCKET_WRITE_NAME, brokerBaseUrl, routingHeaders, S3_PREVIEWS_FOLDER, BUCKET_READ_DNS_URL } from '../constants.mjs';
|
|
10
9
|
import { fetchDeployVersion, brokerCredentialProvider } from '../util/auth/broker-credential-provider.mjs';
|
|
11
10
|
import { readZthAuth } from '../util/auth/auth-store.mjs';
|
|
12
|
-
import {
|
|
13
|
-
import { getMachineAppName } from '../util/machine-identity.mjs';
|
|
11
|
+
import { validateAppName, cleanAppName, mfAppName, buildPreviewSlug } from '../util/app-name.mjs';
|
|
14
12
|
import { getRemoteUrl, initRepo, guardCleanTree, pullAndResolve, pushToRemote } from '../util/git/repo.mjs';
|
|
15
13
|
import { resolveGitEnv } from '../util/git/git-env.mjs';
|
|
16
14
|
import { validateRemoteOptions, resolveRemoteUrl } from '../util/git/remote-options.mjs';
|
|
17
15
|
import { setupS3Client } from '../util/s3/client.mjs';
|
|
18
|
-
import {
|
|
16
|
+
import { getMachineAppName } from '../util/machine-identity.mjs';
|
|
17
|
+
import { registerOrUpdateApp } from '../util/broker/app.mjs';
|
|
18
|
+
import { openInBrowser } from '../util/open-browser.mjs';
|
|
19
19
|
import { buildCommand } from './build.mjs';
|
|
20
|
+
import { promptForTags } from './tag.mjs';
|
|
20
21
|
|
|
21
22
|
// Should be run from the consumer app directory — uses process.cwd() to locate it.
|
|
22
23
|
async function deployCommand(opts = {}) {
|
|
@@ -25,10 +26,68 @@ async function deployCommand(opts = {}) {
|
|
|
25
26
|
if (!consumerPkg.name) {
|
|
26
27
|
throw new Error('Consumer package.json must have a "name" field');
|
|
27
28
|
}
|
|
29
|
+
validateAppName(cleanAppName(consumerPkg.name));
|
|
28
30
|
const appName = mfAppName(consumerPkg.name);
|
|
29
31
|
// Validate the user's deploy token is on disk before doing any expensive
|
|
30
32
|
// work. Throws Z2hAuthMissingError with a remediation hint if not.
|
|
31
33
|
const { token } = await readZthAuth();
|
|
34
|
+
const ctx = { paths, consumerName: consumerPkg.name, appName, token, opts };
|
|
35
|
+
if (opts.preview) {
|
|
36
|
+
await deployPreview(ctx);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
await deployLive(ctx);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Runs the build step and turns its output into a ManifestEntry ready to
|
|
44
|
+
* register/update — the one step a real deploy and a preview both need
|
|
45
|
+
* identically: build, then stamp version/url/deployedBy/gitRemote onto the
|
|
46
|
+
* asset manifest Trident emitted.
|
|
47
|
+
*/
|
|
48
|
+
async function buildAndStamp(opts) {
|
|
49
|
+
const { assetManifest } = await buildCommand({ publicUrl: opts.publicUrl });
|
|
50
|
+
const deployedBy = getMachineAppName() ?? os.userInfo().username;
|
|
51
|
+
return {
|
|
52
|
+
...assetManifest,
|
|
53
|
+
version: opts.nextVersion,
|
|
54
|
+
url: opts.publicUrl,
|
|
55
|
+
deployedBy,
|
|
56
|
+
gitRemote: opts.gitRemote,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function deployPreview({ paths, consumerName, appName, token, opts }) {
|
|
60
|
+
const previewId = opts.previewId ?? randomUUID().slice(0, 8);
|
|
61
|
+
const previewSlug = buildPreviewSlug(consumerName, previewId);
|
|
62
|
+
const previewAppName = mfAppName(previewSlug);
|
|
63
|
+
const keyPrefix = `${S3_PREVIEWS_FOLDER}/${appName}/${previewId}/MF`;
|
|
64
|
+
// Full CDN URL, not a relative path — trident-skeleton uses a manifest
|
|
65
|
+
// entry's `url` verbatim as the asset base, never joined with its own mfUrl.
|
|
66
|
+
const url = `${BUCKET_READ_DNS_URL}/${keyPrefix}`;
|
|
67
|
+
const nextVersion = (await fetchDeployVersion(previewAppName)) + 1;
|
|
68
|
+
info('building');
|
|
69
|
+
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote: '' });
|
|
70
|
+
const creds = await brokerCredentialProvider({ appName, previewId })();
|
|
71
|
+
setupS3Client(creds);
|
|
72
|
+
info(`uploading preview build → s3://${BUCKET_WRITE_NAME}/${keyPrefix}/`);
|
|
73
|
+
await uploadDir(paths.shadowBuild, keyPrefix);
|
|
74
|
+
await registerOrUpdateApp({ appName: previewAppName, token, nextVersion, entry, gitRemote: '' });
|
|
75
|
+
const previewUrl = `https://bigbrain.me/bigbrain-vibe/${previewSlug}`;
|
|
76
|
+
output(`[z2h-cli] preview ready ✅\n app: ${appName}\n previewAppName: ${previewAppName}\n open: ${previewUrl}`, {
|
|
77
|
+
ok: true,
|
|
78
|
+
appName,
|
|
79
|
+
previewId,
|
|
80
|
+
previewAppName,
|
|
81
|
+
bucket: BUCKET_WRITE_NAME,
|
|
82
|
+
keyPrefix,
|
|
83
|
+
url,
|
|
84
|
+
previewUrl,
|
|
85
|
+
});
|
|
86
|
+
if (opts.open) {
|
|
87
|
+
void openInBrowser(previewUrl);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
32
91
|
let gitRemote = await getRemoteUrl(process.cwd());
|
|
33
92
|
if (gitRemote) {
|
|
34
93
|
if (opts.remote || opts.repoUrl) {
|
|
@@ -47,7 +106,7 @@ async function deployCommand(opts = {}) {
|
|
|
47
106
|
validateRemoteOptions(opts);
|
|
48
107
|
// Pass the raw consumer name (not the mf-prefixed appName) so defaultS3Remote
|
|
49
108
|
// produces the correct s3://bucket/mf-<name>/git path without double prefix.
|
|
50
|
-
const remote = resolveRemoteUrl(opts.remote,
|
|
109
|
+
const remote = resolveRemoteUrl(opts.remote, consumerName, opts.repoUrl);
|
|
51
110
|
info(`initializing git repo with ${opts.remote} remote...`);
|
|
52
111
|
await initRepo(process.cwd(), remote);
|
|
53
112
|
gitRemote = remote;
|
|
@@ -67,24 +126,7 @@ async function deployCommand(opts = {}) {
|
|
|
67
126
|
const keyPrefix = `${appName}/${nextVersion}`;
|
|
68
127
|
const url = `${BUCKET_READ_DNS_URL}/${keyPrefix}`;
|
|
69
128
|
info('building');
|
|
70
|
-
await
|
|
71
|
-
if (!(await pathExists(paths.shadowBuild))) {
|
|
72
|
-
throw new Error(`No build output at ${paths.shadowBuild} after build step.`);
|
|
73
|
-
}
|
|
74
|
-
// Trident emits the canonical per-MF manifest at build/asset-manifest.json.
|
|
75
|
-
const assetManifestPath = path.join(paths.shadowBuild, 'asset-manifest.json');
|
|
76
|
-
if (!(await pathExists(assetManifestPath))) {
|
|
77
|
-
throw new Error(`Missing asset-manifest.json at ${assetManifestPath}.`);
|
|
78
|
-
}
|
|
79
|
-
const assetManifest = (await readJson(assetManifestPath));
|
|
80
|
-
const deployedBy = getMachineAppName() ?? os.userInfo().username;
|
|
81
|
-
const entry = {
|
|
82
|
-
...assetManifest,
|
|
83
|
-
version: nextVersion,
|
|
84
|
-
url,
|
|
85
|
-
deployedBy,
|
|
86
|
-
gitRemote,
|
|
87
|
-
};
|
|
129
|
+
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote });
|
|
88
130
|
if (opts.dryRun) {
|
|
89
131
|
output(`[z2h-cli] dry-run: would upload to s3://${BUCKET_WRITE_NAME}/${keyPrefix}/ as v${version}`, {
|
|
90
132
|
ok: true,
|
|
@@ -114,18 +156,7 @@ async function deployCommand(opts = {}) {
|
|
|
114
156
|
}
|
|
115
157
|
info(`uploading build → s3://${BUCKET_WRITE_NAME}/${keyPrefix}/`);
|
|
116
158
|
await uploadDir(paths.shadowBuild, keyPrefix);
|
|
117
|
-
|
|
118
|
-
// The two are separate endpoints, so the CLI picks based on the version it
|
|
119
|
-
// was handed.
|
|
120
|
-
if (nextVersion === 1) {
|
|
121
|
-
const visibility = await resolveFirstDeployVisibility(appName);
|
|
122
|
-
info(`registering app "${appName}" → v${nextVersion} (${visibility})`);
|
|
123
|
-
await registerApp(appName, token, nextVersion, entry, visibility, gitRemote);
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
info(`releasing "${appName}" → v${nextVersion}`);
|
|
127
|
-
await updateApp(appName, token, nextVersion, entry, gitRemote);
|
|
128
|
-
}
|
|
159
|
+
await registerOrUpdateApp({ appName, token, nextVersion, entry, gitRemote });
|
|
129
160
|
}
|
|
130
161
|
finally {
|
|
131
162
|
await releaseLockBestEffort(appName, token);
|
|
@@ -136,7 +167,7 @@ async function deployCommand(opts = {}) {
|
|
|
136
167
|
// The host MF parses the app name from the URL and prepends `mf-` itself
|
|
137
168
|
// before looking it up in the manifest. So the user-facing URL must use
|
|
138
169
|
// the consumer's de-scoped package name (no `mf-` prefix, no `@scope/`).
|
|
139
|
-
const liveUrl = `https://bigbrain.me/bigbrain-vibe/${cleanAppName(
|
|
170
|
+
const liveUrl = `https://bigbrain.me/bigbrain-vibe/${cleanAppName(consumerName)}`;
|
|
140
171
|
output(`[z2h-cli] deployed ✅\n app: ${appName}\n version: ${nextVersion}\n open: ${liveUrl}`, {
|
|
141
172
|
ok: true,
|
|
142
173
|
appName,
|
|
@@ -147,16 +178,8 @@ async function deployCommand(opts = {}) {
|
|
|
147
178
|
liveUrl,
|
|
148
179
|
entry,
|
|
149
180
|
});
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/register`, {
|
|
153
|
-
method: 'POST',
|
|
154
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
155
|
-
body: JSON.stringify({ appName, version, entry, visibility, gitRemote }),
|
|
156
|
-
});
|
|
157
|
-
if (!res.ok) {
|
|
158
|
-
const text = await res.text();
|
|
159
|
-
throw new Error(`App registration failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
181
|
+
if (opts.open) {
|
|
182
|
+
void openInBrowser(liveUrl);
|
|
160
183
|
}
|
|
161
184
|
}
|
|
162
185
|
const TAG_NUDGE = "run 'z2h-cli tag' to categorize this app (app type & team)";
|
|
@@ -197,31 +220,6 @@ async function acquireDeployLock(appName, token) {
|
|
|
197
220
|
}
|
|
198
221
|
info(`deploy lock acquired for ${appName}`);
|
|
199
222
|
}
|
|
200
|
-
async function updateApp(appName, token, version, entry, gitRemote) {
|
|
201
|
-
const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/update`, {
|
|
202
|
-
method: 'POST',
|
|
203
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
204
|
-
body: JSON.stringify({ appName, version, entry, gitRemote }),
|
|
205
|
-
});
|
|
206
|
-
if (!res.ok) {
|
|
207
|
-
const text = await res.text();
|
|
208
|
-
throw new Error(`App update failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
// First deploy only: decide whether the app is born public or private. The
|
|
212
|
-
// choice is passed to the register endpoint (default private). Non-interactive
|
|
213
|
-
// runs (--json or no TTY) always default to private.
|
|
214
|
-
async function resolveFirstDeployVisibility(appName) {
|
|
215
|
-
if (getOutputMode() === 'json' || !process.stdout.isTTY) {
|
|
216
|
-
return 'private';
|
|
217
|
-
}
|
|
218
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
219
|
-
const answer = await new Promise(resolve => {
|
|
220
|
-
rl.question(`Make "${appName}" public to all monday.com employees? (y/N) `, resolve);
|
|
221
|
-
});
|
|
222
|
-
rl.close();
|
|
223
|
-
return answer.trim().toLowerCase() === 'y' ? 'public' : 'private';
|
|
224
|
-
}
|
|
225
223
|
// best-effort — the 2-min TTL backstop handles failures
|
|
226
224
|
async function releaseLockBestEffort(appName, token) {
|
|
227
225
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor.ts"],"names":[],"mappings":"AAwSA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAqEnD"}
|
|
@@ -16,7 +16,6 @@ const Z2H_REPO_ROOT = path.join(HOME, 'Development', 'bigbrain-z2h');
|
|
|
16
16
|
const BOOTSTRAP_CMD = `bash ${path.join(Z2H_REPO_ROOT, 'z2h/setup/bootstrap.sh')}`;
|
|
17
17
|
const UPDATE_CMD = `bash ${path.join(Z2H_REPO_ROOT, 'z2h/setup/update.sh')}`;
|
|
18
18
|
const CLAUDE_PLUGINS_JSON = path.join(HOME, '.claude', 'plugins', 'installed_plugins.json');
|
|
19
|
-
const SSL_CERT_PATH = path.join(HOME, 'Library', 'Application Support', 'devcert', 'domains', 'webpack.llama.fan', 'certificate.crt');
|
|
20
19
|
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
21
20
|
async function checkTool(label, opts) {
|
|
22
21
|
const { cmd, args, failStatus = 'fail', getVersion, fix } = opts;
|
|
@@ -210,15 +209,6 @@ async function checkReactVersion() {
|
|
|
210
209
|
detail: reactVersion,
|
|
211
210
|
};
|
|
212
211
|
}
|
|
213
|
-
async function checkSslCert() {
|
|
214
|
-
const ok = await pathExists(SSL_CERT_PATH);
|
|
215
|
-
return {
|
|
216
|
-
label: 'SSL cert',
|
|
217
|
-
status: ok ? 'ok' : 'warn',
|
|
218
|
-
detail: ok ? 'webpack.llama.fan' : 'not found — auto-installs on first z2h-cli dev',
|
|
219
|
-
fix: ok ? undefined : 'z2h-cli dev',
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
212
|
async function checkPlugin(pluginKey, label, isMandatory, pluginsJson) {
|
|
223
213
|
const missingStatus = isMandatory ? 'fail' : 'warn';
|
|
224
214
|
if (!pluginsJson) {
|
|
@@ -265,7 +255,7 @@ async function doctorCommand() {
|
|
|
265
255
|
}
|
|
266
256
|
const systemChecks = await Promise.all([checkNvm(), checkNode(), checkYarn(), checkAws(), checkGitRemoteS3()]);
|
|
267
257
|
const cliChecks = await Promise.all([checkZ2hCli(), checkAuthToken(), checkNpmTokenInProfile()]);
|
|
268
|
-
const workspaceChecks = await Promise.all([checkWorkspace(), checkReactVersion()
|
|
258
|
+
const workspaceChecks = await Promise.all([checkWorkspace(), checkReactVersion()]);
|
|
269
259
|
const pluginChecks = await Promise.all([
|
|
270
260
|
checkPlugin('z2h@bigbrain-z2h', 'z2h plugin', true, pluginsJson),
|
|
271
261
|
checkPlugin('cf-external-claude-plugin@client-foundations-ai-tools', 'cf-external plugin', false, pluginsJson),
|
package/dist/esm/constants.d.ts
CHANGED
|
@@ -13,9 +13,11 @@ export declare const REGION = "us-east-1";
|
|
|
13
13
|
export declare const BUCKET_WRITE_URL = "https://prod-use1-bigbrain-zth-mf-assets.s3.us-east-1.amazonaws.com";
|
|
14
14
|
export declare const BUCKET_READ_DNS_NAME = "bigbrain-zth-mf-assets";
|
|
15
15
|
export declare const BUCKET_READ_DNS_URL = "https://bigbrain-zth-mf-assets.bigbrain.me";
|
|
16
|
+
export declare const S3_PREVIEWS_FOLDER = "previews";
|
|
16
17
|
export declare const MANIFEST_KEY = "mf-assets";
|
|
17
18
|
export declare const MANIFEST_READ_URL = "https://bigbrain-zth-mf-assets.bigbrain.me/mf-assets";
|
|
18
19
|
export declare const APP_NAME_PATTERN: RegExp;
|
|
20
|
+
export declare const PREVIEW_APP_NAME_PREFIX = "zth-preview-";
|
|
19
21
|
export declare const DEFAULT_BROKER_BASE_URL = "https://bigbrain-zth.bigbrain.me";
|
|
20
22
|
/**
|
|
21
23
|
* Resolves the broker origin (no trailing slash). Honors
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEjD,CAAC;AAKF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAElD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAQ3C,eAAO,MAAM,iBAAiB,qCAAqC,CAAC;AACpE,eAAO,MAAM,MAAM,cAAc,CAAC;AAClC,eAAO,MAAM,gBAAgB,wEAA4D,CAAC;AAE1F,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAC7D,eAAO,MAAM,mBAAmB,+CAAgD,CAAC;AAIjF,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAK7C,eAAO,MAAM,YAAY,cAAc,CAAC;AACxC,eAAO,MAAM,iBAAiB,yDAA2C,CAAC;AAM1E,eAAO,MAAM,gBAAgB,QAA2B,CAAC;AAEzD,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAKtD,eAAO,MAAM,uBAAuB,qCAAqC,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAID,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGvD;AAKD,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAG9C,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAGnD,eAAO,MAAM,uBAAuB,gCAAgC,CAAC;AAMrE,eAAO,MAAM,iBAAiB,EAAG,eAAwB,CAAC;AAE1D;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
|
package/dist/esm/constants.mjs
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
const SHADOW_DIR_NAME = '.zth';
|
|
7
7
|
// ─────────── Shadow scripts ───────
|
|
8
8
|
const SHADOW_SCRIPTS = {
|
|
9
|
-
start: 'trident start',
|
|
10
9
|
build: 'trident build',
|
|
11
10
|
};
|
|
12
11
|
// ─────────── Shadow dev deps ───────
|
|
@@ -15,10 +14,10 @@ const SHADOW_SCRIPTS = {
|
|
|
15
14
|
const SHADOW_DEV_DEPS = {
|
|
16
15
|
'@mondaydotcomorg/trident-toolkit': '^2.9.25',
|
|
17
16
|
};
|
|
18
|
-
// ─────────── Auto-allocated
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
17
|
+
// ─────────── Auto-allocated shadow port range ────
|
|
18
|
+
// Baked into every shadow's .tridentrc.js regardless of whether a dev server
|
|
19
|
+
// ever binds it. High enough to dodge common dev defaults (3000 Next, 5173
|
|
20
|
+
// Vite, 8080). Collisions within the 7k-port pool are rare for POC.
|
|
22
21
|
const MIN_PORT = 13000;
|
|
23
22
|
const MAX_PORT = 19999;
|
|
24
23
|
// ─────────── Gitignore entries auto-appended to the consumer's file ────
|
|
@@ -33,6 +32,9 @@ const REGION = 'us-east-1';
|
|
|
33
32
|
const BUCKET_WRITE_URL = `https://${BUCKET_WRITE_NAME}.s3.${REGION}.amazonaws.com`;
|
|
34
33
|
const BUCKET_READ_DNS_NAME = 'bigbrain-zth-mf-assets';
|
|
35
34
|
const BUCKET_READ_DNS_URL = `https://${BUCKET_READ_DNS_NAME}.bigbrain.me`;
|
|
35
|
+
// Root prefix for ephemeral preview uploads: `previews/<appName>/<previewId>/MF/...`.
|
|
36
|
+
// Shared by the CLI (upload path) and bigbrain-zth (STS policy + manifest overlay).
|
|
37
|
+
const S3_PREVIEWS_FOLDER = 'previews';
|
|
36
38
|
// No `.json` extension — TridentSkeleton's `mfUrl` API appends `/mf-assets`
|
|
37
39
|
// to the bucket base when fetching, so the manifest must live at this exact
|
|
38
40
|
// key. See microfrontends/mf-bigbrain-zth/.../ZeroToHeroHost.tsx.
|
|
@@ -43,6 +45,7 @@ const MANIFEST_READ_URL = `${BUCKET_READ_DNS_URL}/${MANIFEST_KEY}`;
|
|
|
43
45
|
// backend STS broker rejects anything else with 400; the CLI can validate
|
|
44
46
|
// upfront for friendlier errors.
|
|
45
47
|
const APP_NAME_PATTERN = /^[a-z][a-z0-9-]{1,49}$/;
|
|
48
|
+
const PREVIEW_APP_NAME_PREFIX = 'zth-preview-';
|
|
46
49
|
// ─────────── Broker origin + npm registry (shared §3 contract) ─────
|
|
47
50
|
// The bigbrain-zth broker fronts both the STS credential route and the npm
|
|
48
51
|
// proxy. `Z2H_BROKER_BASE_URL` overrides the origin for tests/staging.
|
|
@@ -84,4 +87,4 @@ function brokerNpmRegistryUrl() {
|
|
|
84
87
|
return `${brokerBaseUrl()}${NPM_BROKER_PATH}`;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
export { APP_NAME_PATTERN, BROKER_NPM_SCOPE, BUCKET_READ_DNS_NAME, BUCKET_READ_DNS_URL, BUCKET_WRITE_NAME, BUCKET_WRITE_URL, DEFAULT_BROKER_BASE_URL, GITIGNORE_ENTRIES, MANIFEST_KEY, MANIFEST_READ_URL, MAX_PORT, MIN_PORT, NPM_BROKER_PATH, PUBLIC_NPM_REGISTRY_URL, REGION, SHADOW_DEV_DEPS, SHADOW_DIR_NAME, SHADOW_SCRIPTS, Z2H_NPM_TOKEN_ENV, brokerBaseUrl, brokerNpmRegistryUrl, routingHeaders };
|
|
90
|
+
export { APP_NAME_PATTERN, BROKER_NPM_SCOPE, BUCKET_READ_DNS_NAME, BUCKET_READ_DNS_URL, BUCKET_WRITE_NAME, BUCKET_WRITE_URL, DEFAULT_BROKER_BASE_URL, GITIGNORE_ENTRIES, MANIFEST_KEY, MANIFEST_READ_URL, MAX_PORT, MIN_PORT, NPM_BROKER_PATH, PREVIEW_APP_NAME_PREFIX, PUBLIC_NPM_REGISTRY_URL, REGION, S3_PREVIEWS_FOLDER, SHADOW_DEV_DEPS, SHADOW_DIR_NAME, SHADOW_SCRIPTS, Z2H_NPM_TOKEN_ENV, brokerBaseUrl, brokerNpmRegistryUrl, routingHeaders };
|
package/dist/esm/index.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import path from 'node:path';
|
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import { execa } from 'execa';
|
|
6
6
|
import { getWorkspaceHome } from './util/workspace.mjs';
|
|
7
|
-
import { devCommand } from './commands/dev.mjs';
|
|
8
7
|
import { buildCommand } from './commands/build.mjs';
|
|
9
8
|
import { cleanCommand } from './commands/clean.mjs';
|
|
10
9
|
import { deployCommand } from './commands/deploy.mjs';
|
|
@@ -79,18 +78,6 @@ async function main() {
|
|
|
79
78
|
await fail(`build failed: ${err.message}`, { command: 'build' }, 2);
|
|
80
79
|
}
|
|
81
80
|
});
|
|
82
|
-
program
|
|
83
|
-
.command('dev')
|
|
84
|
-
.description('Start the Trident dev server against .zth/ with native Vite HMR')
|
|
85
|
-
.option('--open', 'open the browser automatically when the dev server is ready')
|
|
86
|
-
.action(async (opts) => {
|
|
87
|
-
try {
|
|
88
|
-
await devCommand(opts);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
await fail(`dev failed: ${err.message}`, { command: 'dev' }, 2);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
81
|
program
|
|
95
82
|
.command('clean')
|
|
96
83
|
.description('Delete <consumer>/.zth/ and <consumer>/build/')
|
|
@@ -106,16 +93,23 @@ async function main() {
|
|
|
106
93
|
.command('deploy')
|
|
107
94
|
.description('Build, upload to S3, and register the app with the Z2H backend')
|
|
108
95
|
.option('--dry-run', 'print what would happen without uploading anything')
|
|
96
|
+
.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')
|
|
97
|
+
.option('--open', 'open the resulting app/preview URL in the browser on success')
|
|
109
98
|
.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.')
|
|
110
99
|
.option('--repo-url <url>', 'First deploy only: GitHub repo URL — required when --remote=github. Error if the app already has a git remote.')
|
|
111
100
|
.action(async (opts) => {
|
|
112
101
|
try {
|
|
113
|
-
await trackDuration('z2h_app_deploy_success', () => deployCommand(
|
|
102
|
+
await trackDuration(opts.preview ? 'z2h_app_preview_success' : 'z2h_app_deploy_success', () => deployCommand({
|
|
103
|
+
...opts,
|
|
104
|
+
preview: Boolean(opts.preview),
|
|
105
|
+
previewId: typeof opts.preview === 'string' ? opts.preview : undefined,
|
|
106
|
+
}), {
|
|
114
107
|
app_name: getCwdAppName(),
|
|
115
108
|
});
|
|
116
109
|
}
|
|
117
110
|
catch (err) {
|
|
118
|
-
|
|
111
|
+
const label = opts.preview ? 'preview' : 'deploy';
|
|
112
|
+
await fail(`${label} failed: ${err.message}`, { command: label }, 2);
|
|
119
113
|
}
|
|
120
114
|
});
|
|
121
115
|
program
|
|
@@ -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,22 @@
|
|
|
1
|
+
// `dev` is gone from the CLI (superseded by `deploy --preview`), so any
|
|
2
|
+
// consumer's `start` script still pointing at it is now a dangling command.
|
|
3
|
+
// Only removes the exact value the template ever wrote — leaves a
|
|
4
|
+
// consumer-customized `start` script alone.
|
|
5
|
+
function removeStartScript(tree) {
|
|
6
|
+
for (const entry of tree.children('.')) {
|
|
7
|
+
if (entry.startsWith('.')) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const pkgPath = `${entry}/package.json`;
|
|
11
|
+
if (!tree.exists(pkgPath)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const pkg = JSON.parse(tree.read(pkgPath, 'utf-8'));
|
|
15
|
+
if (pkg.scripts?.start === 'z2h-cli dev') {
|
|
16
|
+
delete pkg.scripts.start;
|
|
17
|
+
tree.write(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { removeStartScript as default };
|
|
@@ -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":"
|
|
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,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { APP_NAME_PATTERN, PREVIEW_APP_NAME_PREFIX } from '../constants.mjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Strip any npm scope (`@scope/`) from a package name.
|
|
@@ -23,5 +24,48 @@ function getCwdAppName() {
|
|
|
23
24
|
function mfAppName(name) {
|
|
24
25
|
return `mf-${cleanAppName(name)}`;
|
|
25
26
|
}
|
|
27
|
+
// Names reserved by the host MF (mf-bigbrain-zth) for platform pages under
|
|
28
|
+
// /bigbrain-vibe/<name>. Creating an app with one of these would shadow the
|
|
29
|
+
// platform page in the URL, so we refuse upfront.
|
|
30
|
+
const RESERVED_APP_NAMES = new Set(['generate-z2h-token']);
|
|
31
|
+
/**
|
|
32
|
+
* Validates a de-scoped, kebab-case app name against every rule shared by
|
|
33
|
+
* `create` and `deploy` (incl. `deploy --preview`) — pattern, reserved
|
|
34
|
+
* platform names, and the preview-prefix reservation. Throws with a
|
|
35
|
+
* friendly message on the first violation.
|
|
36
|
+
*/
|
|
37
|
+
function validateAppName(appName) {
|
|
38
|
+
if (RESERVED_APP_NAMES.has(appName)) {
|
|
39
|
+
throw new Error(`App name "${appName}" is reserved by the Z2H host for a platform page under /bigbrain-vibe/${appName}. Pick a different name.`);
|
|
40
|
+
}
|
|
41
|
+
if (!APP_NAME_PATTERN.test(appName)) {
|
|
42
|
+
throw new Error(`App name "${appName}" is invalid. Use lowercase kebab-case (a-z, 0-9, -), ` +
|
|
43
|
+
`2-50 chars, starting with a letter.`);
|
|
44
|
+
}
|
|
45
|
+
if (appName.startsWith(PREVIEW_APP_NAME_PREFIX)) {
|
|
46
|
+
throw new Error(`App names starting with "${PREVIEW_APP_NAME_PREFIX}" are reserved for the ephemeral preview mechanism. Pick a different name.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Preview slugs are capped here so the mf-prefixed name (`mf-` + slug) stays
|
|
50
|
+
// within APP_NAME_PATTERN's 50-char limit (3 chars reserved for `mf-`).
|
|
51
|
+
const PREVIEW_SLUG_MAX_LEN = 47;
|
|
52
|
+
/**
|
|
53
|
+
* Builds the slug for a preview's synthetic RDS app row:
|
|
54
|
+
* `zth-preview-<shortid>-<realAppName>`, pre-`mf-`-prefixing. Uniqueness
|
|
55
|
+
* comes entirely from `shortId` (derived from the previewId, already a
|
|
56
|
+
* randomUUID() — 32 bits of entropy); truncating the human-readable tail to
|
|
57
|
+
* fit the cap is safe on its own, a shortId collision is astronomically
|
|
58
|
+
* unlikely at POC scale and would just 409 on registerApp for a retry.
|
|
59
|
+
*/
|
|
60
|
+
function buildPreviewSlug(appName, previewId) {
|
|
61
|
+
const shortId = previewId.replace(/-/g, '').slice(0, 8);
|
|
62
|
+
const base = `${PREVIEW_APP_NAME_PREFIX}${shortId}-${cleanAppName(appName)}`;
|
|
63
|
+
// Trailing hyphens are stripped post-slice — a cut that lands right after a
|
|
64
|
+
// hyphen in appName would otherwise leave one dangling.
|
|
65
|
+
return base.slice(0, PREVIEW_SLUG_MAX_LEN).replace(/-+$/, '');
|
|
66
|
+
}
|
|
67
|
+
function isPreviewAppName(appName) {
|
|
68
|
+
return appName.startsWith(`mf-${PREVIEW_APP_NAME_PREFIX}`);
|
|
69
|
+
}
|
|
26
70
|
|
|
27
|
-
export { cleanAppName, getCwdAppName, mfAppName };
|
|
71
|
+
export { buildPreviewSlug, cleanAppName, getCwdAppName, isPreviewAppName, mfAppName, 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
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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;
|
|
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"}
|
|
@@ -45,31 +45,33 @@ async function fetchDeployVersion(appName) {
|
|
|
45
45
|
const body = (await res.json());
|
|
46
46
|
return body.version ?? 0;
|
|
47
47
|
}
|
|
48
|
-
async function
|
|
49
|
-
const res = await postToBroker(
|
|
48
|
+
async function requestSts(path, body, errorLabel) {
|
|
49
|
+
const res = await postToBroker(path, body);
|
|
50
50
|
assertBrokerOk(res);
|
|
51
51
|
if (!res.ok) {
|
|
52
52
|
const text = await res.text();
|
|
53
|
-
throw new Error(`Z2H
|
|
53
|
+
throw new Error(`Z2H ${errorLabel} broker returned ${res.status}: ${text.slice(0, 1024)}`);
|
|
54
54
|
}
|
|
55
|
-
const
|
|
56
|
-
if (!
|
|
57
|
-
|
|
55
|
+
const responseBody = (await res.json());
|
|
56
|
+
if (!responseBody.accessKeyId ||
|
|
57
|
+
!responseBody.secretAccessKey ||
|
|
58
|
+
!responseBody.sessionToken ||
|
|
59
|
+
!responseBody.expiration) {
|
|
60
|
+
throw new Error(`Z2H ${errorLabel} broker response missing required credential fields`);
|
|
58
61
|
}
|
|
59
62
|
return {
|
|
60
|
-
accessKeyId:
|
|
61
|
-
secretAccessKey:
|
|
62
|
-
sessionToken:
|
|
63
|
-
expiration: new Date(
|
|
63
|
+
accessKeyId: responseBody.accessKeyId,
|
|
64
|
+
secretAccessKey: responseBody.secretAccessKey,
|
|
65
|
+
sessionToken: responseBody.sessionToken,
|
|
66
|
+
expiration: new Date(responseBody.expiration),
|
|
64
67
|
};
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* single in-flight fetch.
|
|
70
|
+
* Wraps a raw STS fetch in the shared cache/coalescing state machine:
|
|
71
|
+
* credentials are cached in memory until 5 minutes before expiry, and
|
|
72
|
+
* concurrent calls share a single in-flight fetch.
|
|
71
73
|
*/
|
|
72
|
-
function
|
|
74
|
+
function createCachedCredentialProvider(fetchOnce) {
|
|
73
75
|
let cached;
|
|
74
76
|
let inflight;
|
|
75
77
|
return async () => {
|
|
@@ -80,7 +82,7 @@ function brokerCredentialProvider({ appName }) {
|
|
|
80
82
|
if (inflight) {
|
|
81
83
|
return inflight;
|
|
82
84
|
}
|
|
83
|
-
inflight =
|
|
85
|
+
inflight = fetchOnce()
|
|
84
86
|
.then(credentials => {
|
|
85
87
|
cached = credentials;
|
|
86
88
|
return credentials;
|
|
@@ -91,5 +93,15 @@ function brokerCredentialProvider({ appName }) {
|
|
|
91
93
|
return inflight;
|
|
92
94
|
};
|
|
93
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* AWS SDK credential provider that fetches short-lived credentials from the
|
|
98
|
+
* bigbrain-zth STS broker, scoped to `appName` — or, when `previewId` is
|
|
99
|
+
* given, to a single `previews/<appName>/<previewId>/*` slot instead of the
|
|
100
|
+
* full deploy path. Credentials are cached in memory until 5 minutes before
|
|
101
|
+
* expiry; concurrent calls share a single in-flight fetch.
|
|
102
|
+
*/
|
|
103
|
+
function brokerCredentialProvider({ appName, previewId, }) {
|
|
104
|
+
return createCachedCredentialProvider(() => requestSts(STS_BROKER_PATH, { appName, previewId }, previewId ? 'preview' : 'deploy'));
|
|
105
|
+
}
|
|
94
106
|
|
|
95
107
|
export { DEPLOY_VERSION_PATH, STS_BROKER_PATH, brokerCredentialProvider, fetchDeployVersion };
|
|
@@ -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":"
|
|
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"}
|