@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
package/src/util/broker/app.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { brokerBaseUrl, routingHeaders } from '../../constants';
|
|
2
2
|
import { readZthAuth } from '../auth/auth-store';
|
|
3
|
+
import { info } from '../logger';
|
|
4
|
+
import { resolveAppVisibility } from '../prompt-visibility';
|
|
5
|
+
import type { ManifestEntry } from '../manifest';
|
|
3
6
|
|
|
4
7
|
export interface AppInfo {
|
|
5
8
|
appName: string;
|
|
@@ -32,3 +35,68 @@ export async function fetchApp(appName: string): Promise<AppInfo | null> {
|
|
|
32
35
|
}
|
|
33
36
|
return res.json() as Promise<AppInfo>;
|
|
34
37
|
}
|
|
38
|
+
|
|
39
|
+
async function registerApp(
|
|
40
|
+
appName: string,
|
|
41
|
+
token: string,
|
|
42
|
+
version: number,
|
|
43
|
+
entry: ManifestEntry,
|
|
44
|
+
visibility: 'public' | 'private',
|
|
45
|
+
gitRemote: string
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/register`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
50
|
+
body: JSON.stringify({ appName, version, entry, visibility, gitRemote }),
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const text = await res.text();
|
|
54
|
+
throw new Error(`App registration failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Releases a new version onto an app that already exists. */
|
|
59
|
+
async function updateApp(
|
|
60
|
+
appName: string,
|
|
61
|
+
token: string,
|
|
62
|
+
version: number,
|
|
63
|
+
entry: ManifestEntry,
|
|
64
|
+
gitRemote: string
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/update`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
69
|
+
body: JSON.stringify({ appName, version, entry, gitRemote }),
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
throw new Error(`App update failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface RegisterOrUpdateOptions {
|
|
78
|
+
appName: string;
|
|
79
|
+
token: string;
|
|
80
|
+
nextVersion: number;
|
|
81
|
+
entry: ManifestEntry;
|
|
82
|
+
gitRemote: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* v1 creates the app row (+ owner grant, prompting for visibility); later
|
|
87
|
+
* versions release onto it via update instead — the two are separate
|
|
88
|
+
* endpoints, so this picks based on the version it's handed. Shared by
|
|
89
|
+
* deploy and preview so this branching logic lives in exactly one place.
|
|
90
|
+
*/
|
|
91
|
+
export async function registerOrUpdateApp(opts: RegisterOrUpdateOptions): Promise<void> {
|
|
92
|
+
const { appName, token, nextVersion, entry, gitRemote } = opts;
|
|
93
|
+
|
|
94
|
+
if (nextVersion === 1) {
|
|
95
|
+
const visibility = await resolveAppVisibility(appName);
|
|
96
|
+
info(`registering "${appName}" → v${nextVersion} (${visibility})`);
|
|
97
|
+
await registerApp(appName, token, nextVersion, entry, visibility, gitRemote);
|
|
98
|
+
} else {
|
|
99
|
+
info(`updating "${appName}" → v${nextVersion}`);
|
|
100
|
+
await updateApp(appName, token, nextVersion, entry, gitRemote);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import { getOutputMode } from './logger';
|
|
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
|
+
export async function resolveAppVisibility(appName: string): Promise<'public' | 'private'> {
|
|
8
|
+
if (getOutputMode() === 'json' || !process.stdout.isTTY) {
|
|
9
|
+
return 'private';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13
|
+
const answer = await new Promise<string>(resolve => {
|
|
14
|
+
rl.question(`Make "${appName}" public to all monday.com employees? (y/N) `, resolve);
|
|
15
|
+
});
|
|
16
|
+
rl.close();
|
|
17
|
+
return answer.trim().toLowerCase() === 'y' ? 'public' : 'private';
|
|
18
|
+
}
|
package/dist/commands/dev.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAKD,wBAAsB,UAAU,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAyErE"}
|
package/dist/commands/dev.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
const fs$1 = require('node:fs');
|
|
5
|
-
const fs = require('fs-extra');
|
|
6
|
-
const chokidar = require('chokidar');
|
|
7
|
-
const execa = require('execa');
|
|
8
|
-
const util_logger = require('../util/logger.js');
|
|
9
|
-
const shadow_prepare = require('../shadow/prepare.js');
|
|
10
|
-
const shadow_consumerPkg = require('../shadow/consumer-pkg.js');
|
|
11
|
-
const shadow_sync = require('../shadow/sync.js');
|
|
12
|
-
const util_openBrowser = require('../util/open-browser.js');
|
|
13
|
-
|
|
14
|
-
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
15
|
-
|
|
16
|
-
const path__default = /*#__PURE__*/_interopDefault(path);
|
|
17
|
-
const fs__default = /*#__PURE__*/_interopDefault(fs$1);
|
|
18
|
-
|
|
19
|
-
const TRIDENT_READY_PATTERN = /Server started at/i;
|
|
20
|
-
// Should be run from the consumer app directory — uses process.cwd() to locate it.
|
|
21
|
-
async function devCommand(opts = {}) {
|
|
22
|
-
const ctx = await shadow_prepare.prepareShadow();
|
|
23
|
-
const { port } = ctx;
|
|
24
|
-
const url = `https://bigbrain.me/bigbrain-vibe/${ctx.appName}?zth_local=${port}`;
|
|
25
|
-
util_logger.info('starting z2h app');
|
|
26
|
-
// stdout is piped so we can detect trident's ready banner before printing
|
|
27
|
-
// our own ready line. stdin/stderr stay inherited so colored errors and
|
|
28
|
-
// interactive prompts still flow naturally.
|
|
29
|
-
const server = execa.execa('yarn', ['start'], {
|
|
30
|
-
cwd: ctx.paths.shadowDir,
|
|
31
|
-
stdio: ['inherit', 'pipe', 'inherit'],
|
|
32
|
-
// Trident requires NPM_TOKEN to be set or it refuses to start; 'FAKE_TOKEN' satisfies the check without real credentials.
|
|
33
|
-
// we always pass a fake token for same behavior for all users (no matter if they have a token or not)
|
|
34
|
-
env: { ...process.env, NPM_TOKEN: 'FAKE_TOKEN' },
|
|
35
|
-
});
|
|
36
|
-
const pidFile = path__default.default.join(ctx.paths.shadowDir, 'dev.pid');
|
|
37
|
-
if (server.pid) {
|
|
38
|
-
await fs.writeFile(pidFile, String(server.pid));
|
|
39
|
-
}
|
|
40
|
-
const cleanupPid = () => {
|
|
41
|
-
try {
|
|
42
|
-
fs__default.default.unlinkSync(pidFile);
|
|
43
|
-
}
|
|
44
|
-
catch (_) {
|
|
45
|
-
// file already gone — ignore
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
server.on('exit', cleanupPid);
|
|
49
|
-
process.on('exit', cleanupPid);
|
|
50
|
-
process.on('SIGINT', () => {
|
|
51
|
-
server.kill();
|
|
52
|
-
process.exit(0);
|
|
53
|
-
});
|
|
54
|
-
process.on('SIGTERM', () => {
|
|
55
|
-
server.kill();
|
|
56
|
-
process.exit(0);
|
|
57
|
-
});
|
|
58
|
-
let readyPrinted = false;
|
|
59
|
-
server.stdout?.on('data', (chunk) => {
|
|
60
|
-
process.stdout.write(chunk);
|
|
61
|
-
if (!readyPrinted && TRIDENT_READY_PATTERN.test(chunk.toString())) {
|
|
62
|
-
readyPrinted = true;
|
|
63
|
-
util_logger.output(`\n[z2h-cli] ready — open:\n ${url}\n`, {
|
|
64
|
-
ok: true,
|
|
65
|
-
appName: ctx.appName,
|
|
66
|
-
port,
|
|
67
|
-
url,
|
|
68
|
-
});
|
|
69
|
-
if (opts.open) {
|
|
70
|
-
void util_openBrowser.openInBrowser(url);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
// Only watch consumer package.json. Source changes flow through Vite's
|
|
75
|
-
// native import graph — the wrapper imports consumer src via relative
|
|
76
|
-
// path, so Vite's HMR picks up edits without any rsync.
|
|
77
|
-
const pkgWatcher = chokidar.watch(ctx.paths.consumerPkg, { ignoreInitial: true });
|
|
78
|
-
pkgWatcher.on('change', async () => {
|
|
79
|
-
util_logger.warn('consumer package.json changed — re-checking deps');
|
|
80
|
-
try {
|
|
81
|
-
const raw = (await fs.readJson(ctx.paths.consumerPkg));
|
|
82
|
-
const { pkg } = await shadow_consumerPkg.ensureConsumerPkg(ctx.paths.consumerPkg, raw);
|
|
83
|
-
await shadow_sync.syncShadow(ctx.paths, pkg);
|
|
84
|
-
}
|
|
85
|
-
catch (err) {
|
|
86
|
-
util_logger.errorLog(`re-prepare failed: ${err.message}`);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
await server;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
exports.devCommand = devCommand;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/commands/dev.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAKD,wBAAsB,UAAU,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAyErE"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import { writeFile, readJson } from 'fs-extra';
|
|
4
|
-
import { watch } from 'chokidar';
|
|
5
|
-
import { execa } from 'execa';
|
|
6
|
-
import { info, output, warn, errorLog } from '../util/logger.mjs';
|
|
7
|
-
import { prepareShadow } from '../shadow/prepare.mjs';
|
|
8
|
-
import { ensureConsumerPkg } from '../shadow/consumer-pkg.mjs';
|
|
9
|
-
import { syncShadow } from '../shadow/sync.mjs';
|
|
10
|
-
import { openInBrowser } from '../util/open-browser.mjs';
|
|
11
|
-
|
|
12
|
-
const TRIDENT_READY_PATTERN = /Server started at/i;
|
|
13
|
-
// Should be run from the consumer app directory — uses process.cwd() to locate it.
|
|
14
|
-
async function devCommand(opts = {}) {
|
|
15
|
-
const ctx = await prepareShadow();
|
|
16
|
-
const { port } = ctx;
|
|
17
|
-
const url = `https://bigbrain.me/bigbrain-vibe/${ctx.appName}?zth_local=${port}`;
|
|
18
|
-
info('starting z2h app');
|
|
19
|
-
// stdout is piped so we can detect trident's ready banner before printing
|
|
20
|
-
// our own ready line. stdin/stderr stay inherited so colored errors and
|
|
21
|
-
// interactive prompts still flow naturally.
|
|
22
|
-
const server = execa('yarn', ['start'], {
|
|
23
|
-
cwd: ctx.paths.shadowDir,
|
|
24
|
-
stdio: ['inherit', 'pipe', 'inherit'],
|
|
25
|
-
// Trident requires NPM_TOKEN to be set or it refuses to start; 'FAKE_TOKEN' satisfies the check without real credentials.
|
|
26
|
-
// we always pass a fake token for same behavior for all users (no matter if they have a token or not)
|
|
27
|
-
env: { ...process.env, NPM_TOKEN: 'FAKE_TOKEN' },
|
|
28
|
-
});
|
|
29
|
-
const pidFile = path.join(ctx.paths.shadowDir, 'dev.pid');
|
|
30
|
-
if (server.pid) {
|
|
31
|
-
await writeFile(pidFile, String(server.pid));
|
|
32
|
-
}
|
|
33
|
-
const cleanupPid = () => {
|
|
34
|
-
try {
|
|
35
|
-
fs.unlinkSync(pidFile);
|
|
36
|
-
}
|
|
37
|
-
catch (_) {
|
|
38
|
-
// file already gone — ignore
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
server.on('exit', cleanupPid);
|
|
42
|
-
process.on('exit', cleanupPid);
|
|
43
|
-
process.on('SIGINT', () => {
|
|
44
|
-
server.kill();
|
|
45
|
-
process.exit(0);
|
|
46
|
-
});
|
|
47
|
-
process.on('SIGTERM', () => {
|
|
48
|
-
server.kill();
|
|
49
|
-
process.exit(0);
|
|
50
|
-
});
|
|
51
|
-
let readyPrinted = false;
|
|
52
|
-
server.stdout?.on('data', (chunk) => {
|
|
53
|
-
process.stdout.write(chunk);
|
|
54
|
-
if (!readyPrinted && TRIDENT_READY_PATTERN.test(chunk.toString())) {
|
|
55
|
-
readyPrinted = true;
|
|
56
|
-
output(`\n[z2h-cli] ready — open:\n ${url}\n`, {
|
|
57
|
-
ok: true,
|
|
58
|
-
appName: ctx.appName,
|
|
59
|
-
port,
|
|
60
|
-
url,
|
|
61
|
-
});
|
|
62
|
-
if (opts.open) {
|
|
63
|
-
void openInBrowser(url);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
// Only watch consumer package.json. Source changes flow through Vite's
|
|
68
|
-
// native import graph — the wrapper imports consumer src via relative
|
|
69
|
-
// path, so Vite's HMR picks up edits without any rsync.
|
|
70
|
-
const pkgWatcher = watch(ctx.paths.consumerPkg, { ignoreInitial: true });
|
|
71
|
-
pkgWatcher.on('change', async () => {
|
|
72
|
-
warn('consumer package.json changed — re-checking deps');
|
|
73
|
-
try {
|
|
74
|
-
const raw = (await readJson(ctx.paths.consumerPkg));
|
|
75
|
-
const { pkg } = await ensureConsumerPkg(ctx.paths.consumerPkg, raw);
|
|
76
|
-
await syncShadow(ctx.paths, pkg);
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
errorLog(`re-prepare failed: ${err.message}`);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
await server;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export { devCommand };
|
package/src/commands/dev.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import { readJson, writeFile } from 'fs-extra';
|
|
4
|
-
import { watch } from 'chokidar';
|
|
5
|
-
import { execa } from 'execa';
|
|
6
|
-
import { info, warn, errorLog, output } from '../util/logger';
|
|
7
|
-
import { prepareShadow } from '../shadow/prepare';
|
|
8
|
-
import { ensureConsumerPkg } from '../shadow/consumer-pkg';
|
|
9
|
-
import { syncShadow } from '../shadow/sync';
|
|
10
|
-
import { openInBrowser } from '../util/open-browser';
|
|
11
|
-
import type { ConsumerPkg } from '../types';
|
|
12
|
-
|
|
13
|
-
export interface DevOptions {
|
|
14
|
-
open?: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const TRIDENT_READY_PATTERN = /Server started at/i;
|
|
18
|
-
|
|
19
|
-
// Should be run from the consumer app directory — uses process.cwd() to locate it.
|
|
20
|
-
export async function devCommand(opts: DevOptions = {}): Promise<void> {
|
|
21
|
-
const ctx = await prepareShadow();
|
|
22
|
-
const { port } = ctx;
|
|
23
|
-
const url = `https://bigbrain.me/bigbrain-vibe/${ctx.appName}?zth_local=${port}`;
|
|
24
|
-
|
|
25
|
-
info('starting z2h app');
|
|
26
|
-
// stdout is piped so we can detect trident's ready banner before printing
|
|
27
|
-
// our own ready line. stdin/stderr stay inherited so colored errors and
|
|
28
|
-
// interactive prompts still flow naturally.
|
|
29
|
-
const server = execa('yarn', ['start'], {
|
|
30
|
-
cwd: ctx.paths.shadowDir,
|
|
31
|
-
stdio: ['inherit', 'pipe', 'inherit'],
|
|
32
|
-
// Trident requires NPM_TOKEN to be set or it refuses to start; 'FAKE_TOKEN' satisfies the check without real credentials.
|
|
33
|
-
// we always pass a fake token for same behavior for all users (no matter if they have a token or not)
|
|
34
|
-
env: { ...process.env, NPM_TOKEN: 'FAKE_TOKEN' },
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const pidFile = path.join(ctx.paths.shadowDir, 'dev.pid');
|
|
38
|
-
if (server.pid) {
|
|
39
|
-
await writeFile(pidFile, String(server.pid));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const cleanupPid = () => {
|
|
43
|
-
try {
|
|
44
|
-
fs.unlinkSync(pidFile);
|
|
45
|
-
} catch (_) {
|
|
46
|
-
// file already gone — ignore
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
server.on('exit', cleanupPid);
|
|
50
|
-
process.on('exit', cleanupPid);
|
|
51
|
-
process.on('SIGINT', () => {
|
|
52
|
-
server.kill();
|
|
53
|
-
process.exit(0);
|
|
54
|
-
});
|
|
55
|
-
process.on('SIGTERM', () => {
|
|
56
|
-
server.kill();
|
|
57
|
-
process.exit(0);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
let readyPrinted = false;
|
|
61
|
-
server.stdout?.on('data', (chunk: Buffer) => {
|
|
62
|
-
process.stdout.write(chunk);
|
|
63
|
-
if (!readyPrinted && TRIDENT_READY_PATTERN.test(chunk.toString())) {
|
|
64
|
-
readyPrinted = true;
|
|
65
|
-
output(`\n[z2h-cli] ready — open:\n ${url}\n`, {
|
|
66
|
-
ok: true,
|
|
67
|
-
appName: ctx.appName,
|
|
68
|
-
port,
|
|
69
|
-
url,
|
|
70
|
-
});
|
|
71
|
-
if (opts.open) {
|
|
72
|
-
void openInBrowser(url);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Only watch consumer package.json. Source changes flow through Vite's
|
|
78
|
-
// native import graph — the wrapper imports consumer src via relative
|
|
79
|
-
// path, so Vite's HMR picks up edits without any rsync.
|
|
80
|
-
const pkgWatcher = watch(ctx.paths.consumerPkg, { ignoreInitial: true });
|
|
81
|
-
pkgWatcher.on('change', async () => {
|
|
82
|
-
warn('consumer package.json changed — re-checking deps');
|
|
83
|
-
try {
|
|
84
|
-
const raw = (await readJson(ctx.paths.consumerPkg)) as ConsumerPkg;
|
|
85
|
-
const { pkg } = await ensureConsumerPkg(ctx.paths.consumerPkg, raw);
|
|
86
|
-
await syncShadow(ctx.paths, pkg);
|
|
87
|
-
} catch (err) {
|
|
88
|
-
errorLog(`re-prepare failed: ${(err as Error).message}`);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
await server;
|
|
93
|
-
}
|