@hs-x/cli 0.4.12 → 0.4.14
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/README.md +21 -7
- package/dist/commands/completion.d.ts.map +1 -1
- package/dist/commands/completion.js +3 -0
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/deploy.d.ts +10 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +37 -13
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +346 -32
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/help-command.d.ts.map +1 -1
- package/dist/commands/help-command.js +22 -6
- package/dist/commands/help-command.js.map +1 -1
- package/dist/commands/project-lifecycle.d.ts +10 -0
- package/dist/commands/project-lifecycle.d.ts.map +1 -1
- package/dist/commands/project-lifecycle.js +120 -33
- package/dist/commands/project-lifecycle.js.map +1 -1
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +2 -0
- package/dist/commands/project.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/dev/account-selection.d.ts +55 -0
- package/dist/dev/account-selection.d.ts.map +1 -1
- package/dist/dev/account-selection.js +120 -0
- package/dist/dev/account-selection.js.map +1 -1
- package/dist/dev/browser-log-bridge.d.ts +13 -8
- package/dist/dev/browser-log-bridge.d.ts.map +1 -1
- package/dist/dev/browser-log-bridge.js +37 -17
- package/dist/dev/browser-log-bridge.js.map +1 -1
- package/dist/dev/clipboard.d.ts +9 -0
- package/dist/dev/clipboard.d.ts.map +1 -0
- package/dist/dev/clipboard.js +42 -0
- package/dist/dev/clipboard.js.map +1 -0
- package/dist/dev/compat-shim.d.ts +19 -4
- package/dist/dev/compat-shim.d.ts.map +1 -1
- package/dist/dev/compat-shim.js +31 -4
- package/dist/dev/compat-shim.js.map +1 -1
- package/dist/dev/component-selection.d.ts +12 -0
- package/dist/dev/component-selection.d.ts.map +1 -1
- package/dist/dev/component-selection.js +29 -0
- package/dist/dev/component-selection.js.map +1 -1
- package/dist/dev/frontend-log-relay.d.ts +12 -0
- package/dist/dev/frontend-log-relay.d.ts.map +1 -1
- package/dist/dev/frontend-log-relay.js +31 -20
- package/dist/dev/frontend-log-relay.js.map +1 -1
- package/dist/dev/hubspot-log-lane.d.ts +27 -0
- package/dist/dev/hubspot-log-lane.d.ts.map +1 -0
- package/dist/dev/hubspot-log-lane.js +73 -0
- package/dist/dev/hubspot-log-lane.js.map +1 -0
- package/dist/dev/hubspot-logs-poller.d.ts +7 -1
- package/dist/dev/hubspot-logs-poller.d.ts.map +1 -1
- package/dist/dev/hubspot-logs-poller.js +28 -1
- package/dist/dev/hubspot-logs-poller.js.map +1 -1
- package/dist/dev/keyboard.d.ts +21 -3
- package/dist/dev/keyboard.d.ts.map +1 -1
- package/dist/dev/keyboard.js +106 -6
- package/dist/dev/keyboard.js.map +1 -1
- package/dist/dev/local-proxy.d.ts +90 -0
- package/dist/dev/local-proxy.d.ts.map +1 -0
- package/dist/dev/local-proxy.js +186 -0
- package/dist/dev/local-proxy.js.map +1 -0
- package/dist/dev/log-server.d.ts +9 -0
- package/dist/dev/log-server.d.ts.map +1 -1
- package/dist/dev/log-server.js +46 -3
- package/dist/dev/log-server.js.map +1 -1
- package/dist/dev/production-app-check.d.ts +23 -0
- package/dist/dev/production-app-check.d.ts.map +1 -0
- package/dist/dev/production-app-check.js +28 -0
- package/dist/dev/production-app-check.js.map +1 -0
- package/dist/dev/session-manager.d.ts +51 -1
- package/dist/dev/session-manager.d.ts.map +1 -1
- package/dist/dev/session-manager.js +155 -12
- package/dist/dev/session-manager.js.map +1 -1
- package/dist/dev/startup-progress.d.ts +7 -0
- package/dist/dev/startup-progress.d.ts.map +1 -0
- package/dist/dev/startup-progress.js +27 -0
- package/dist/dev/startup-progress.js.map +1 -0
- package/dist/dev/stream-event-format.d.ts +10 -1
- package/dist/dev/stream-event-format.d.ts.map +1 -1
- package/dist/dev/stream-event-format.js +157 -5
- package/dist/dev/stream-event-format.js.map +1 -1
- package/dist/dev/stream-renderer.d.ts +34 -1
- package/dist/dev/stream-renderer.d.ts.map +1 -1
- package/dist/dev/stream-renderer.js +371 -60
- package/dist/dev/stream-renderer.js.map +1 -1
- package/dist/dev/trace-markdown.d.ts +25 -0
- package/dist/dev/trace-markdown.d.ts.map +1 -0
- package/dist/dev/trace-markdown.js +159 -0
- package/dist/dev/trace-markdown.js.map +1 -0
- package/dist/hubspot-app-logs.d.ts +9 -0
- package/dist/hubspot-app-logs.d.ts.map +1 -1
- package/dist/hubspot-app-logs.js +6 -4
- package/dist/hubspot-app-logs.js.map +1 -1
- package/dist/hubspot-developer-client.js +3 -0
- package/dist/hubspot-developer-client.js.map +1 -1
- package/dist/hubspot-project.d.ts +2 -0
- package/dist/hubspot-project.d.ts.map +1 -1
- package/dist/hubspot-project.js +13 -1
- package/dist/hubspot-project.js.map +1 -1
- package/dist/prompt.d.ts +15 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +66 -15
- package/dist/prompt.js.map +1 -1
- package/dist/reporter/human.d.ts.map +1 -1
- package/dist/reporter/human.js +6 -1
- package/dist/reporter/human.js.map +1 -1
- package/dist/reporter/style.d.ts +10 -0
- package/dist/reporter/style.d.ts.map +1 -1
- package/dist/reporter/style.js +39 -0
- package/dist/reporter/style.js.map +1 -1
- package/dist/services/hubspot-dev-client.js +3 -0
- package/dist/services/hubspot-dev-client.js.map +1 -1
- package/package.json +9 -8
package/dist/commands/dev.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare const devCmd: Command.Command<"dev", Cwd, CliError, {
|
|
|
28
28
|
readonly testingAccount: Option.Option<string>;
|
|
29
29
|
readonly profile: Option.Option<string>;
|
|
30
30
|
readonly preserveState: boolean;
|
|
31
|
+
readonly localProxy: "auto" | "always" | "never";
|
|
31
32
|
readonly targetOrigin: Option.Option<string>;
|
|
32
33
|
readonly telemetryOrigin: Option.Option<string>;
|
|
33
34
|
readonly accountId: Option.Option<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAI/C,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAI/C,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,iBAAiB,CAAC;AA8D1D,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AA09BzC,wBAAsB,UAAU,CAAC,EAC/B,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B,GAAG,OAAO,CAAC,SAAS,CAAC,CA0ErB;AA2vFD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqJlB,CAAC"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -15,13 +15,16 @@ import { cwdOption, jsonOption, runHandler, runQuarantined } from '../cli/kit.js
|
|
|
15
15
|
import { formatConfigUrl, loadCliConfig } from '../config.js';
|
|
16
16
|
import { CLI_VERSION } from '../constants.js';
|
|
17
17
|
import { controlPlaneAuthHeaders } from '../control-plane-fetch.js';
|
|
18
|
-
import { selectHubSpotDirectDevAccounts } from '../dev/account-selection.js';
|
|
19
|
-
import { listHubSpotDevComponentCandidates, resolveHubSpotDevComponentSelectors, resolveHubSpotDevSessionApp, } from '../dev/component-selection.js';
|
|
18
|
+
import { describeHubSpotDevTestingChoice, describeHubSpotDevTestingPlanReason, planHubSpotDevTestingAccount, selectHubSpotDirectDevAccounts, } from '../dev/account-selection.js';
|
|
19
|
+
import { findHubSpotDevApplication, isHubSpotMarketplaceApp, listHubSpotDevComponentCandidates, resolveHubSpotDevComponentSelectors, resolveHubSpotDevSessionApp, } from '../dev/component-selection.js';
|
|
20
|
+
import { HUBSPOT_LOGS_POLL_INTERVAL_MS, startHubSpotLogLane, } from '../dev/hubspot-log-lane.js';
|
|
21
|
+
import { resolveHubSpotLocalProxyMode } from '../dev/local-proxy.js';
|
|
20
22
|
import { createManagedDevEventWriter, createManagedDevSession, markManagedDevSessionReady, markManagedDevSessionState, prepareManagedDevSessionRestart, publicManagedDevSession, readManagedDevEvents, readManagedDevSession, reconcileManagedDevSession, setManagedDevSessionPid, stopManagedDevSessionProcess, waitForManagedDevSessionOwner, } from '../dev/managed-session.js';
|
|
21
23
|
import { findListeningProcess, isHubSpotDevPortConflict, stopProcessCommand, terminateProcessGracefully, } from '../dev/port-process.js';
|
|
24
|
+
import { createDevStartupProgress } from '../dev/startup-progress.js';
|
|
22
25
|
import { executeCliHttp } from '../effect-http.js';
|
|
23
26
|
import { discoverHubSpotAccounts, exchangePak } from '../hubspot-auth.js';
|
|
24
|
-
import { detectLocalProjectMode, validateHubSpotProject } from '../hubspot-project.js';
|
|
27
|
+
import { detectLocalProjectMode, findContainingProjectRoot, validateHubSpotProject, } from '../hubspot-project.js';
|
|
25
28
|
import { LocalProjectPickerCancelled, resolveLocalProject } from '../local-project-picker.js';
|
|
26
29
|
import { requestMutationConsent } from '../mutation-consent.js';
|
|
27
30
|
import { isMachineOutput } from '../output-context.js';
|
|
@@ -281,6 +284,7 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
281
284
|
const reporter = createReporter({ command: 'dev', argv, entry: !json });
|
|
282
285
|
const profile = resolveFlag(argv, '--profile');
|
|
283
286
|
const preserveState = argv.includes('--preserve-state');
|
|
287
|
+
const yes = argv.some((argument) => argument === '--yes' || argument === '-y');
|
|
284
288
|
const validation = await validateHubSpotProject({ root, ...(profile ? { profile } : {}) });
|
|
285
289
|
if (!validation.ok) {
|
|
286
290
|
const result = {
|
|
@@ -363,15 +367,62 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
363
367
|
}
|
|
364
368
|
return { exitCode: 1 };
|
|
365
369
|
}
|
|
366
|
-
|
|
370
|
+
// Mirror `hs project dev`: recommend a developer test account or sandbox,
|
|
371
|
+
// and never land on the production project account without saying so.
|
|
372
|
+
const testingPlan = planHubSpotDevTestingAccount({
|
|
373
|
+
accounts,
|
|
374
|
+
projectAccount,
|
|
375
|
+
// selectHubSpotDirectDevAccounts defaults the testing account to the
|
|
376
|
+
// project account; only an explicit flag counts as an answer here.
|
|
377
|
+
selectedTestingAccount: requestedTestingAccount ? selectedTestingAccount : undefined,
|
|
378
|
+
interactive: !json && !yes && isInteractive(),
|
|
379
|
+
});
|
|
380
|
+
let testingAccount;
|
|
381
|
+
let testingChoice;
|
|
382
|
+
if (testingPlan.kind === 'prompt') {
|
|
383
|
+
const picked = await promptSelect({
|
|
384
|
+
message: 'Choose the account to test on',
|
|
385
|
+
options: testingPlan.choices.map((choice) => ({
|
|
386
|
+
value: String(choice.account.portalId),
|
|
387
|
+
...describeHubSpotDevTestingChoice(choice),
|
|
388
|
+
})),
|
|
389
|
+
default: String(testingPlan.choices[0]?.account.portalId),
|
|
390
|
+
});
|
|
391
|
+
if (picked === undefined)
|
|
392
|
+
return cancelledResult(argv, 'dev');
|
|
393
|
+
const choice = testingPlan.choices.find((item) => String(item.account.portalId) === picked);
|
|
394
|
+
if (!choice)
|
|
395
|
+
return cancelledResult(argv, 'dev');
|
|
396
|
+
testingAccount = choice.account;
|
|
397
|
+
testingChoice = choice.kind;
|
|
398
|
+
reporter.info(`Testing on ${testingAccount.name} (${testingAccount.portalId}) — ${describeTestingChoiceForRow(testingChoice)}`);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
testingAccount = testingPlan.account;
|
|
402
|
+
testingChoice = testingPlan.choice;
|
|
403
|
+
if (!json) {
|
|
404
|
+
const explanation = describeHubSpotDevTestingPlanReason(testingPlan);
|
|
405
|
+
if (explanation.level === 'warn') {
|
|
406
|
+
reporter.warn('HSX_W_DEV_TESTING_ON_PRODUCTION_ACCOUNT', explanation.message);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
reporter.info(explanation.message);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
367
413
|
if (testingAccount.personalAccessKey) {
|
|
414
|
+
const credentialStep = json
|
|
415
|
+
? undefined
|
|
416
|
+
: reporter.step(`Checking ${testingAccount.name} (${testingAccount.portalId}) credentials`);
|
|
368
417
|
try {
|
|
369
418
|
const exchanged = await exchangePak(testingAccount.personalAccessKey);
|
|
370
419
|
if (exchanged.portalId !== testingAccount.portalId) {
|
|
371
420
|
throw new Error(`the configured PAK belongs to account ${exchanged.portalId}, not ${testingAccount.portalId}`);
|
|
372
421
|
}
|
|
422
|
+
credentialStep?.ok();
|
|
373
423
|
}
|
|
374
424
|
catch (cause) {
|
|
425
|
+
credentialStep?.fail();
|
|
375
426
|
const message = `HubSpot testing account ${testingAccount.name} (${testingAccount.portalId}) needs fresh authentication: ${cause instanceof Error ? cause.message : String(cause)} Run \`hs account auth\` for that account, then retry.`;
|
|
376
427
|
if (json) {
|
|
377
428
|
write(`${JSON.stringify({
|
|
@@ -394,12 +445,19 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
394
445
|
const existingDevServer = await recoverHubSpotDevPort({ argv, json, reporter });
|
|
395
446
|
if (existingDevServer.kind === 'exit')
|
|
396
447
|
return existingDevServer.result;
|
|
448
|
+
const probeStep = json ? undefined : reporter.step('Reading HubSpot project components');
|
|
397
449
|
const uiExtensionNodes = await probeHubSpotUiNodes({
|
|
398
450
|
root,
|
|
399
451
|
portalIdFlag: String(projectAccount.portalId),
|
|
400
452
|
accountId: undefined,
|
|
401
453
|
...(profile ? { profile } : {}),
|
|
402
454
|
});
|
|
455
|
+
if (uiExtensionNodes) {
|
|
456
|
+
probeStep?.ok(`${uiExtensionNodes.candidates.length} component${uiExtensionNodes.candidates.length === 1 ? '' : 's'}${uiExtensionNodes.application?.name ? ` · ${uiExtensionNodes.application.name}` : ''}`);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
probeStep?.warn('no UI-extension components found');
|
|
460
|
+
}
|
|
403
461
|
const requestedComponents = resolveFlags(argv, '--component');
|
|
404
462
|
const resolvedComponents = resolveHubSpotDevComponentSelectors(uiExtensionNodes?.candidates ?? [], requestedComponents);
|
|
405
463
|
const requestedAppCheck = resolvedComponents.ok && requestedComponents.length > 0
|
|
@@ -446,16 +504,61 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
446
504
|
const check = resolveHubSpotDevSessionApp(pickerCandidates, selection);
|
|
447
505
|
return check.ok ? undefined : check.message;
|
|
448
506
|
},
|
|
507
|
+
columns: ['Name', 'Type', 'Objects', 'Location', 'UID'],
|
|
449
508
|
options: pickerCandidates.map((candidate) => ({
|
|
450
509
|
value: candidate.uid,
|
|
451
510
|
label: candidate.label,
|
|
452
511
|
description: candidate.description,
|
|
512
|
+
...(candidate.cells ? { cells: candidate.cells } : {}),
|
|
453
513
|
})),
|
|
454
514
|
});
|
|
455
515
|
if (picked === undefined)
|
|
456
516
|
return cancelledResult(argv, 'dev');
|
|
457
517
|
selectedNodeUids = picked;
|
|
458
518
|
}
|
|
519
|
+
// `hs project dev` warns before developing a marketplace app that real
|
|
520
|
+
// portals have installed. Same check, same numbers, same default of "no".
|
|
521
|
+
let productionInstallCount;
|
|
522
|
+
const application = uiExtensionNodes?.application;
|
|
523
|
+
if (isHubSpotMarketplaceApp(application) && application && projectAccount.personalAccessKey) {
|
|
524
|
+
const appLabel = application.name ?? application.uid;
|
|
525
|
+
const installStep = json
|
|
526
|
+
? undefined
|
|
527
|
+
: reporter.step(`Checking ${appLabel} production installs`);
|
|
528
|
+
const check = await checkProductionInstallsForDev({
|
|
529
|
+
root,
|
|
530
|
+
projectAccount,
|
|
531
|
+
application,
|
|
532
|
+
});
|
|
533
|
+
if (!check.ok) {
|
|
534
|
+
installStep?.warn('could not check');
|
|
535
|
+
if (!json)
|
|
536
|
+
reporter.warn('HSX_W_DEV_PRODUCTION_APP_CHECK_FAILED', check.reason);
|
|
537
|
+
}
|
|
538
|
+
else if (check.installCount === 0) {
|
|
539
|
+
installStep?.ok('none');
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
installStep?.warn(`${check.installCount} ${check.installCount === 1 ? 'account' : 'accounts'}`);
|
|
543
|
+
productionInstallCount = check.installCount;
|
|
544
|
+
const { describeHubSpotProductionInstallWarning, HUBSPOT_PRODUCTION_APP_CONFIRMATION } = await import('../dev/production-app-check.js');
|
|
545
|
+
const warning = describeHubSpotProductionInstallWarning(application.name ?? application.uid, check.installCount);
|
|
546
|
+
if (!json)
|
|
547
|
+
reporter.warn('HSX_W_DEV_PRODUCTION_APP_INSTALLS', warning);
|
|
548
|
+
if (!json && !yes && isInteractive()) {
|
|
549
|
+
const proceed = await promptConfirm({
|
|
550
|
+
message: HUBSPOT_PRODUCTION_APP_CONFIRMATION,
|
|
551
|
+
default: false,
|
|
552
|
+
// The checks above take seconds; an Enter mashed meanwhile must not
|
|
553
|
+
// answer "no" on the user's behalf.
|
|
554
|
+
discardTypeAhead: true,
|
|
555
|
+
});
|
|
556
|
+
if (!proceed)
|
|
557
|
+
return cancelledResult(argv, 'dev');
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const startupProgress = json ? undefined : createDevStartupProgress(reporter);
|
|
459
562
|
let directDevLogStream;
|
|
460
563
|
if (!json) {
|
|
461
564
|
const hsxLogPortValue = resolveFlag(argv, '--hsx-log-port') ?? '9099';
|
|
@@ -481,11 +584,13 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
481
584
|
return { exitCode: 2 };
|
|
482
585
|
}
|
|
483
586
|
const managedSessionId = resolveFlag(argv, '--managed-session');
|
|
587
|
+
startupProgress?.phase('Starting local log relay');
|
|
484
588
|
directDevLogStream = await startHsxDevLogStream({
|
|
485
589
|
port: hsxLogPort,
|
|
486
590
|
root,
|
|
487
591
|
reporter,
|
|
488
592
|
filter: onlyParsed.filter,
|
|
593
|
+
hubspotAccount: projectAccount,
|
|
489
594
|
...(managedSessionId ? { managedSessionId } : {}),
|
|
490
595
|
});
|
|
491
596
|
}
|
|
@@ -493,10 +598,21 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
493
598
|
const startDirectSession = () => startDevSession({
|
|
494
599
|
projectAccountId: projectAccount.portalId,
|
|
495
600
|
testingAccountId: testingAccount.portalId,
|
|
601
|
+
...(startupProgress ? { onStartupProgress: startupProgress.phase } : {}),
|
|
496
602
|
projectDir: root,
|
|
497
603
|
...(selectedNodeUids ? { selectedNodeUids } : {}),
|
|
498
604
|
...(profile ? { profile } : {}),
|
|
499
605
|
preserveState,
|
|
606
|
+
localProxyMode: resolveLocalProxyMode(argv),
|
|
607
|
+
...(directDevLogStream
|
|
608
|
+
? {
|
|
609
|
+
relayUrl: directDevLogStream.url,
|
|
610
|
+
onRelayReady: (handle) => {
|
|
611
|
+
if (directDevLogStream)
|
|
612
|
+
directDevLogStream.relay.current = handle;
|
|
613
|
+
},
|
|
614
|
+
}
|
|
615
|
+
: {}),
|
|
500
616
|
onHeartbeatError: (failure) => {
|
|
501
617
|
const warning = describeHeartbeatFailure(failure, testingAccount.portalId);
|
|
502
618
|
if (!warning)
|
|
@@ -548,6 +664,7 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
548
664
|
if (isHubSpotDevPortConflict(initialCause)) {
|
|
549
665
|
const recovery = await recoverHubSpotDevPort({ argv, json, reporter });
|
|
550
666
|
if (recovery.kind === 'exit') {
|
|
667
|
+
startupProgress?.finish(true);
|
|
551
668
|
try {
|
|
552
669
|
await directDevLogStream?.cleanup();
|
|
553
670
|
}
|
|
@@ -567,6 +684,7 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
567
684
|
}
|
|
568
685
|
}
|
|
569
686
|
}
|
|
687
|
+
startupProgress?.finish(!session);
|
|
570
688
|
if (!session) {
|
|
571
689
|
if (directDevLogStream) {
|
|
572
690
|
try {
|
|
@@ -609,11 +727,15 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
609
727
|
portalId: testingAccount.portalId,
|
|
610
728
|
projectAccountId: projectAccount.portalId,
|
|
611
729
|
testingAccountId: testingAccount.portalId,
|
|
730
|
+
testingAccountKind: testingChoice,
|
|
731
|
+
...(productionInstallCount !== undefined ? { productionInstallCount } : {}),
|
|
612
732
|
sessionId: session.sessionId,
|
|
613
733
|
components: session.nodeUids,
|
|
614
734
|
controlPlaneUsed: false,
|
|
615
735
|
cloudflareUsed: false,
|
|
616
|
-
preserveState,
|
|
736
|
+
preserveState: preserveState && session.frontendLogs.active,
|
|
737
|
+
frontendLogs: session.frontendLogs,
|
|
738
|
+
localProxy: session.localProxy,
|
|
617
739
|
};
|
|
618
740
|
if (json)
|
|
619
741
|
write(`${JSON.stringify(started, null, 2)}\n`);
|
|
@@ -622,22 +744,57 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
622
744
|
reporter.rows([
|
|
623
745
|
{ key: 'mode', value: 'HubSpot direct' },
|
|
624
746
|
{ key: 'project-account', value: `${projectAccount.name} (${projectAccount.portalId})` },
|
|
625
|
-
{
|
|
747
|
+
{
|
|
748
|
+
key: 'testing-account',
|
|
749
|
+
value: `${testingAccount.name} (${testingAccount.portalId})`,
|
|
750
|
+
detail: describeTestingChoiceForRow(testingChoice),
|
|
751
|
+
...(testingChoice === 'production'
|
|
752
|
+
? { status: 'warn' }
|
|
753
|
+
: { status: 'ok' }),
|
|
754
|
+
},
|
|
755
|
+
...(productionInstallCount !== undefined
|
|
756
|
+
? [
|
|
757
|
+
{
|
|
758
|
+
key: 'production installs',
|
|
759
|
+
value: `${productionInstallCount} ${productionInstallCount === 1 ? 'account' : 'accounts'}`,
|
|
760
|
+
detail: 'local changes can affect these installs',
|
|
761
|
+
status: 'warn',
|
|
762
|
+
},
|
|
763
|
+
]
|
|
764
|
+
: []),
|
|
626
765
|
{ key: 'ui-extensions', value: `session ${session.sessionId}`, status: 'ok' },
|
|
627
766
|
{ key: 'components', value: String(session.nodeUids.length) },
|
|
628
|
-
...(preserveState
|
|
767
|
+
...(preserveState && session.frontendLogs.active
|
|
629
768
|
? [{ key: 'reload state', value: 'JSON-safe React useState values', status: 'ok' }]
|
|
630
769
|
: []),
|
|
631
770
|
...(directDevLogStream
|
|
632
771
|
? [
|
|
633
772
|
{
|
|
634
773
|
key: 'logs',
|
|
635
|
-
value:
|
|
636
|
-
|
|
774
|
+
value: [
|
|
775
|
+
...(session.frontendLogs.active ? ['frontend'] : []),
|
|
776
|
+
'backend',
|
|
777
|
+
...(directDevLogStream.hubspotLane.active ? ['HubSpot events'] : []),
|
|
778
|
+
].join(' + '),
|
|
779
|
+
detail: session.frontendLogs.transport === 'fetch'
|
|
780
|
+
? `${directDevLogStream.url} · card logs via HubSpot local proxy`
|
|
781
|
+
: directDevLogStream.url,
|
|
637
782
|
status: 'ok',
|
|
638
783
|
},
|
|
784
|
+
hubspotLogsLaneRow(directDevLogStream.hubspotLane),
|
|
639
785
|
]
|
|
640
786
|
: []),
|
|
787
|
+
...(session.frontendLogs.unavailableReason
|
|
788
|
+
? [
|
|
789
|
+
{
|
|
790
|
+
key: 'frontend logs',
|
|
791
|
+
value: 'off',
|
|
792
|
+
detail: session.frontendLogs.unavailableReason,
|
|
793
|
+
status: 'warn',
|
|
794
|
+
},
|
|
795
|
+
]
|
|
796
|
+
: []),
|
|
797
|
+
...localProxyRows(session.localProxy),
|
|
641
798
|
{ key: 'Cloudflare', value: 'not used' },
|
|
642
799
|
{ key: 'HS-X control plane', value: 'not used' },
|
|
643
800
|
]);
|
|
@@ -707,6 +864,9 @@ const KNOWN_DEV_SUBCOMMANDS = new Set([...MANAGED_DEV_SUBCOMMANDS, 'invoke', 'cl
|
|
|
707
864
|
const MANAGED_DEV_LANES = new Set(['frontend', 'backend', 'request', 'hubspot']);
|
|
708
865
|
const MANAGED_DEV_LEVELS = new Set(['debug', 'info', 'warn', 'error']);
|
|
709
866
|
export async function devCommand({ argv, root, json, reporter, }) {
|
|
867
|
+
// Resolve once before direct/enhanced mode detection and managed-session
|
|
868
|
+
// creation so starting in src/app uses the same project, bindings, and cwd.
|
|
869
|
+
root = (await findContainingProjectRoot(root)) ?? root;
|
|
710
870
|
const managedSessionId = resolveFlag(argv, '--managed-session');
|
|
711
871
|
if (managedSessionId) {
|
|
712
872
|
await waitForManagedDevSessionOwner(managedSessionId);
|
|
@@ -1023,6 +1183,9 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1023
1183
|
const requestStarted = Date.now();
|
|
1024
1184
|
const method = request.method ?? 'GET';
|
|
1025
1185
|
const path = request.url ?? '/';
|
|
1186
|
+
const capabilityId = capabilityIdFromDevPath(path);
|
|
1187
|
+
const traceHeader = request.headers['x-hsx-trace-id'];
|
|
1188
|
+
const traceId = (Array.isArray(traceHeader) ? traceHeader[0] : traceHeader)?.trim();
|
|
1026
1189
|
response.on('finish', () => {
|
|
1027
1190
|
renderRequestLog({
|
|
1028
1191
|
method,
|
|
@@ -1030,6 +1193,8 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1030
1193
|
status: response.statusCode,
|
|
1031
1194
|
durationMs: Date.now() - requestStarted,
|
|
1032
1195
|
source: 'local',
|
|
1196
|
+
...(capabilityId ? { capabilityId } : {}),
|
|
1197
|
+
...(traceId ? { traceId: traceId.slice(0, 128) } : {}),
|
|
1033
1198
|
});
|
|
1034
1199
|
});
|
|
1035
1200
|
if (path === '/_hsx/health') {
|
|
@@ -1244,6 +1409,11 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1244
1409
|
runtimeOrigin: migratedCardTunnel.url,
|
|
1245
1410
|
})
|
|
1246
1411
|
: undefined;
|
|
1412
|
+
// The sidecar (below) is what the card's log relay posts to. Its URL is
|
|
1413
|
+
// fixed by --hsx-log-port, so the HubSpot session can be told about it
|
|
1414
|
+
// before it starts; the relay handler lands in this shared slot.
|
|
1415
|
+
const hsxLogPort = Number(resolveFlag(argv, '--hsx-log-port') ?? '9099');
|
|
1416
|
+
const hsxRelay = {};
|
|
1247
1417
|
// Optional: start a HubSpot UI-extension dev session if this directory
|
|
1248
1418
|
// contains an hsproject.json AND the user selected at least one UI node.
|
|
1249
1419
|
const hubSpotDevSession = selectedUiNodeUids && selectedUiNodeUids.length > 0
|
|
@@ -1254,12 +1424,15 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1254
1424
|
accountId,
|
|
1255
1425
|
reporter,
|
|
1256
1426
|
selectedNodeUids: selectedUiNodeUids,
|
|
1427
|
+
relayUrl: `http://127.0.0.1:${hsxLogPort}`,
|
|
1428
|
+
onRelayReady: (handle) => {
|
|
1429
|
+
hsxRelay.current = handle;
|
|
1430
|
+
},
|
|
1257
1431
|
})
|
|
1258
1432
|
: undefined;
|
|
1259
1433
|
// HS-X dev sidecar — receives frontend logs (and later: backend/request
|
|
1260
1434
|
// events) from inside the card iframe and surfaces them in the terminal.
|
|
1261
1435
|
// Stable default port 9099 so card code can hard-default the dev-log URL.
|
|
1262
|
-
const hsxLogPort = Number(resolveFlag(argv, '--hsx-log-port') ?? '9099');
|
|
1263
1436
|
// `--only` selectors pre-filter the stream (same grammar as the interactive
|
|
1264
1437
|
// keyboard, see dev/stream-filter.ts). Invalid selectors are a usage error.
|
|
1265
1438
|
const { parseOnlySelectors } = await import('../dev/stream-filter.js');
|
|
@@ -1279,16 +1452,29 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1279
1452
|
root,
|
|
1280
1453
|
reporter,
|
|
1281
1454
|
filter: onlyParsed.filter,
|
|
1455
|
+
relay: hsxRelay,
|
|
1282
1456
|
...(managedLogSessionId ? { managedSessionId: managedLogSessionId } : {}),
|
|
1283
1457
|
});
|
|
1284
1458
|
if (devLogStream) {
|
|
1285
1459
|
const paint = makeDevLogPaint(process.env, process.stderr);
|
|
1286
1460
|
requestSink = (entry) => {
|
|
1461
|
+
const effectiveAccountId = entry.accountId ?? accountId;
|
|
1287
1462
|
devLogStream.bus.emit({
|
|
1288
1463
|
lane: 'request',
|
|
1289
1464
|
level: entry.status >= 500 ? 'error' : entry.status >= 400 ? 'warn' : 'info',
|
|
1290
1465
|
ts: Date.now(),
|
|
1291
1466
|
msg: formatDevLogEntry(entry, paint, accountId),
|
|
1467
|
+
fields: {
|
|
1468
|
+
method: entry.method,
|
|
1469
|
+
path: entry.path,
|
|
1470
|
+
status: entry.status,
|
|
1471
|
+
durationMs: entry.durationMs,
|
|
1472
|
+
origin: entry.source ?? 'local',
|
|
1473
|
+
...(effectiveAccountId ? { accountId: effectiveAccountId } : {}),
|
|
1474
|
+
...(entry.capabilityId ? { capabilityId: entry.capabilityId } : {}),
|
|
1475
|
+
...(entry.traceId ? { traceId: entry.traceId } : {}),
|
|
1476
|
+
...(entry.detail ? { detail: entry.detail } : {}),
|
|
1477
|
+
},
|
|
1292
1478
|
});
|
|
1293
1479
|
};
|
|
1294
1480
|
}
|
|
@@ -1376,7 +1562,10 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1376
1562
|
]
|
|
1377
1563
|
: []),
|
|
1378
1564
|
...(devLogStream
|
|
1379
|
-
? [
|
|
1565
|
+
? [
|
|
1566
|
+
{ status: 'ok', key: 'logs', value: `streaming on ${devLogStream.url}` },
|
|
1567
|
+
hubspotLogsLaneRow(devLogStream.hubspotLane),
|
|
1568
|
+
]
|
|
1380
1569
|
: []),
|
|
1381
1570
|
...(devTunnel ? [{ status: 'ok', key: 'tunnel', value: devTunnel.url }] : []),
|
|
1382
1571
|
...(migratedCardDevArtifacts
|
|
@@ -1501,6 +1690,17 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1501
1690
|
});
|
|
1502
1691
|
return { exitCode: 0 };
|
|
1503
1692
|
}
|
|
1693
|
+
function capabilityIdFromDevPath(path) {
|
|
1694
|
+
const match = /^\/_hsx\/(?:cards|invoke)\/([^/?]+)/.exec(path);
|
|
1695
|
+
if (!match?.[1])
|
|
1696
|
+
return undefined;
|
|
1697
|
+
try {
|
|
1698
|
+
return decodeURIComponent(match[1]);
|
|
1699
|
+
}
|
|
1700
|
+
catch {
|
|
1701
|
+
return match[1];
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1504
1704
|
async function managedDevStartCommand(input) {
|
|
1505
1705
|
const reporter = input.reporter ??
|
|
1506
1706
|
createReporter({ command: 'dev start', argv: input.argv, entry: !input.json });
|
|
@@ -2100,7 +2300,8 @@ async function startHsxDevLogStream(input) {
|
|
|
2100
2300
|
]);
|
|
2101
2301
|
const bus = createDevEventBus();
|
|
2102
2302
|
const authToken = randomBytes(32).toString('base64url');
|
|
2103
|
-
const
|
|
2303
|
+
const relay = input.relay ?? {};
|
|
2304
|
+
const server = await startDevLogServer({ bus, port: input.port, authToken, relay });
|
|
2104
2305
|
const renderer = startDevStreamRenderer({
|
|
2105
2306
|
bus,
|
|
2106
2307
|
deferStatus: true,
|
|
@@ -2112,31 +2313,22 @@ async function startHsxDevLogStream(input) {
|
|
|
2112
2313
|
const unsubscribeManagedEvents = managedEventWriter
|
|
2113
2314
|
? bus.subscribe((event) => managedEventWriter.log(event))
|
|
2114
2315
|
: undefined;
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
try {
|
|
2121
|
-
const [{ resolveHubSpotLogsSource }, { startHubSpotLogsPoller }] = await Promise.all([
|
|
2122
|
-
import('../hubspot-app-logs.js'),
|
|
2123
|
-
import('../dev/hubspot-logs-poller.js'),
|
|
2124
|
-
]);
|
|
2125
|
-
const resolution = await resolveHubSpotLogsSource({ cwd: input.root });
|
|
2126
|
-
if (resolution.ok) {
|
|
2127
|
-
hubspotPoller = startHubSpotLogsPoller({ bus, source: resolution.source });
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
catch {
|
|
2131
|
-
// The HubSpot lane is additive; never fail the dev stream over it.
|
|
2132
|
-
}
|
|
2316
|
+
const hubspotLane = startHubSpotLogLane({
|
|
2317
|
+
root: input.root,
|
|
2318
|
+
bus,
|
|
2319
|
+
account: input.hubspotAccount,
|
|
2320
|
+
});
|
|
2133
2321
|
return {
|
|
2134
2322
|
url: server.url,
|
|
2135
2323
|
authToken,
|
|
2136
2324
|
renderer,
|
|
2137
2325
|
bus,
|
|
2326
|
+
get hubspotLane() {
|
|
2327
|
+
return hubspotLane.state;
|
|
2328
|
+
},
|
|
2329
|
+
relay,
|
|
2138
2330
|
async cleanup() {
|
|
2139
|
-
|
|
2331
|
+
hubspotLane.stop();
|
|
2140
2332
|
unsubscribeManagedEvents?.();
|
|
2141
2333
|
renderer.stop();
|
|
2142
2334
|
await Promise.all([server.close(), managedEventWriter?.close()]);
|
|
@@ -2191,7 +2383,13 @@ async function probeHubSpotUiNodes(input) {
|
|
|
2191
2383
|
accountId: portalId,
|
|
2192
2384
|
}, input.profile ? { profile: input.profile } : {});
|
|
2193
2385
|
const candidates = listHubSpotDevComponentCandidates(ir.intermediateNodesIndexedByUid);
|
|
2194
|
-
|
|
2386
|
+
const application = findHubSpotDevApplication(ir.intermediateNodesIndexedByUid);
|
|
2387
|
+
return {
|
|
2388
|
+
uids: candidates.map((candidate) => candidate.uid),
|
|
2389
|
+
candidates,
|
|
2390
|
+
portalId,
|
|
2391
|
+
...(application ? { application } : {}),
|
|
2392
|
+
};
|
|
2195
2393
|
}
|
|
2196
2394
|
catch {
|
|
2197
2395
|
// IR translate failed — caller falls back to capability-only behavior.
|
|
@@ -2216,6 +2414,9 @@ async function maybeStartHubSpotDevSession(input) {
|
|
|
2216
2414
|
selectedNodeUids: input.selectedNodeUids,
|
|
2217
2415
|
...(profile ? { profile } : {}),
|
|
2218
2416
|
preserveState: input.argv.includes('--preserve-state'),
|
|
2417
|
+
localProxyMode: resolveLocalProxyMode(input.argv),
|
|
2418
|
+
...(input.relayUrl ? { relayUrl: input.relayUrl } : {}),
|
|
2419
|
+
...(input.onRelayReady ? { onRelayReady: input.onRelayReady } : {}),
|
|
2219
2420
|
onHeartbeatError: (failure) => {
|
|
2220
2421
|
const warning = describeHeartbeatFailure(failure, portalId);
|
|
2221
2422
|
if (warning && !input.json)
|
|
@@ -2225,6 +2426,16 @@ async function maybeStartHubSpotDevSession(input) {
|
|
|
2225
2426
|
input.reporter
|
|
2226
2427
|
.step('HubSpot UI-extension session')
|
|
2227
2428
|
.ok(`id ${session.sessionId} · ${session.nodeUids.length} component${session.nodeUids.length === 1 ? '' : 's'}`);
|
|
2429
|
+
if (!input.json) {
|
|
2430
|
+
if (session.frontendLogs.unavailableReason) {
|
|
2431
|
+
input.reporter.warn('HSX_W_DEV_FRONTEND_LOGS_UNAVAILABLE', session.frontendLogs.unavailableReason);
|
|
2432
|
+
}
|
|
2433
|
+
for (const probe of session.localProxy.mappings) {
|
|
2434
|
+
if (probe.applied && probe.reachable)
|
|
2435
|
+
continue;
|
|
2436
|
+
input.reporter.warn('HSX_W_DEV_LOCAL_PROXY_UNREACHABLE', `local.json proxies ${probe.from} → ${probe.to}: ${describeLocalProxyIssue(session.localProxy, probe)}`);
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2228
2439
|
return session;
|
|
2229
2440
|
}
|
|
2230
2441
|
catch (error) {
|
|
@@ -2953,6 +3164,61 @@ function formatDevExpiry(now, expiresAt) {
|
|
|
2953
3164
|
function isFlagValue(value) {
|
|
2954
3165
|
return value !== undefined && value.length > 0 && !value.startsWith('-');
|
|
2955
3166
|
}
|
|
3167
|
+
/**
|
|
3168
|
+
* The HubSpot-side lane is the only place renders and proxy executions show
|
|
3169
|
+
* up, so its state must be on the checklist: a silent lane looks identical to
|
|
3170
|
+
* a broken one.
|
|
3171
|
+
*/
|
|
3172
|
+
function hubspotLogsLaneRow(lane) {
|
|
3173
|
+
if (lane.active) {
|
|
3174
|
+
return {
|
|
3175
|
+
key: 'HubSpot app logs',
|
|
3176
|
+
value: `app ${lane.appId} · renders + proxy executions`,
|
|
3177
|
+
detail: `read from account ${lane.developerAccountId} every ${HUBSPOT_LOGS_POLL_INTERVAL_MS / 1000}s; entries appear shortly after the card acts`,
|
|
3178
|
+
status: 'ok',
|
|
3179
|
+
};
|
|
3180
|
+
}
|
|
3181
|
+
if (lane.pending)
|
|
3182
|
+
return { key: 'HubSpot app logs', value: 'connecting', detail: lane.reason };
|
|
3183
|
+
return { key: 'HubSpot app logs', value: 'off', detail: lane.reason, status: 'warn' };
|
|
3184
|
+
}
|
|
3185
|
+
function describeTestingChoiceForRow(choice) {
|
|
3186
|
+
switch (choice) {
|
|
3187
|
+
case 'developer-test':
|
|
3188
|
+
return 'developer test account';
|
|
3189
|
+
case 'sandbox':
|
|
3190
|
+
return 'sandbox';
|
|
3191
|
+
default:
|
|
3192
|
+
return 'production';
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Resolve the deployed app and count its non-test installs with the project
|
|
3197
|
+
* account's own credentials — the same lookup `hs project dev` performs.
|
|
3198
|
+
*/
|
|
3199
|
+
async function checkProductionInstallsForDev(input) {
|
|
3200
|
+
try {
|
|
3201
|
+
const projectConfig = JSON.parse(await readFile(join(input.root, 'hsproject.json'), 'utf8'));
|
|
3202
|
+
if (!projectConfig.name) {
|
|
3203
|
+
return { ok: false, reason: 'hsproject.json has no project name to look the app up by.' };
|
|
3204
|
+
}
|
|
3205
|
+
const [{ createHubSpotDeveloperClient }, { checkHubSpotProductionInstalls }] = await Promise.all([import('@hs-x/hubspot-cli'), import('../dev/production-app-check.js')]);
|
|
3206
|
+
const client = createHubSpotDeveloperClient({
|
|
3207
|
+
developerAccountId: input.projectAccount.portalId,
|
|
3208
|
+
...(input.projectAccount.personalAccessKey
|
|
3209
|
+
? { personalAccessKey: input.projectAccount.personalAccessKey }
|
|
3210
|
+
: {}),
|
|
3211
|
+
});
|
|
3212
|
+
return await checkHubSpotProductionInstalls({
|
|
3213
|
+
client,
|
|
3214
|
+
projectName: projectConfig.name,
|
|
3215
|
+
appUid: input.application.uid,
|
|
3216
|
+
});
|
|
3217
|
+
}
|
|
3218
|
+
catch (cause) {
|
|
3219
|
+
return { ok: false, reason: cause instanceof Error ? cause.message : String(cause) };
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
2956
3222
|
function resolveFlag(argv, flag) {
|
|
2957
3223
|
const index = argv.indexOf(flag);
|
|
2958
3224
|
if (index !== -1) {
|
|
@@ -3021,6 +3287,7 @@ const projectAccountOption = Options.text('project-account').pipe(Options.option
|
|
|
3021
3287
|
const testingAccountOption = Options.text('testing-account').pipe(Options.optional);
|
|
3022
3288
|
const profileOption = Options.text('profile').pipe(Options.withAlias('p'), Options.optional);
|
|
3023
3289
|
const preserveStateOption = Options.boolean('preserve-state').pipe(Options.withDefault(false));
|
|
3290
|
+
const localProxyOption = Options.choice('local-proxy', ['auto', 'always', 'never']).pipe(Options.withDefault('auto'));
|
|
3024
3291
|
const targetOriginOption = Options.text('target-origin').pipe(Options.optional);
|
|
3025
3292
|
const telemetryOriginOption = Options.text('telemetry-origin').pipe(Options.optional);
|
|
3026
3293
|
const accountIdOption = Options.text('account-id').pipe(Options.optional);
|
|
@@ -3069,6 +3336,7 @@ export const devCmd = Command.make('dev', {
|
|
|
3069
3336
|
testingAccount: testingAccountOption,
|
|
3070
3337
|
profile: profileOption,
|
|
3071
3338
|
preserveState: preserveStateOption,
|
|
3339
|
+
localProxy: localProxyOption,
|
|
3072
3340
|
targetOrigin: targetOriginOption,
|
|
3073
3341
|
telemetryOrigin: telemetryOriginOption,
|
|
3074
3342
|
accountId: accountIdOption,
|
|
@@ -3148,6 +3416,8 @@ export const devCmd = Command.make('dev', {
|
|
|
3148
3416
|
argv.push('--profile', opts.profile.value);
|
|
3149
3417
|
if (opts.preserveState)
|
|
3150
3418
|
argv.push('--preserve-state');
|
|
3419
|
+
if (opts.localProxy !== 'auto')
|
|
3420
|
+
argv.push('--local-proxy', opts.localProxy);
|
|
3151
3421
|
if (Option.isSome(opts.targetOrigin)) {
|
|
3152
3422
|
argv.push('--target-origin', opts.targetOrigin.value);
|
|
3153
3423
|
}
|
|
@@ -3320,4 +3590,48 @@ async function devInvokeRemote(args) {
|
|
|
3320
3590
|
reporter.done(ok ? 'Invoked' : 'Failed', ok ? 0 : 1);
|
|
3321
3591
|
return { exitCode: ok ? 0 : 1 };
|
|
3322
3592
|
}
|
|
3593
|
+
/**
|
|
3594
|
+
* `<app root>/local.json` proxy mappings switch HubSpot's dev server into local
|
|
3595
|
+
* proxy mode for `hubspot.fetch`; a mapping whose target is down fails every
|
|
3596
|
+
* fetch to that origin with an empty error, so show each one up front.
|
|
3597
|
+
*/
|
|
3598
|
+
function resolveLocalProxyMode(argv) {
|
|
3599
|
+
return resolveHubSpotLocalProxyMode(resolveFlag(argv, '--local-proxy'));
|
|
3600
|
+
}
|
|
3601
|
+
/**
|
|
3602
|
+
* One short row per local.json mapping, plus one when proxy mode is on with no
|
|
3603
|
+
* mappings (`--local-proxy always`). Details stay one clause long; the mode
|
|
3604
|
+
* semantics live in `hs-x dev --help` and the guide.
|
|
3605
|
+
*/
|
|
3606
|
+
function localProxyRows(localProxy) {
|
|
3607
|
+
const rows = localProxy.mappings.map((probe) => {
|
|
3608
|
+
const healthy = probe.applied && probe.reachable;
|
|
3609
|
+
return {
|
|
3610
|
+
key: 'local proxy',
|
|
3611
|
+
value: probe.applied ? `${probe.from} → ${probe.to}` : `off · ${probe.from}`,
|
|
3612
|
+
status: healthy ? 'ok' : 'warn',
|
|
3613
|
+
...(healthy ? {} : { detail: describeLocalProxyIssue(localProxy, probe) }),
|
|
3614
|
+
};
|
|
3615
|
+
});
|
|
3616
|
+
if (localProxy.active && localProxy.mappings.length === 0) {
|
|
3617
|
+
rows.push({
|
|
3618
|
+
key: 'local proxy',
|
|
3619
|
+
value: 'on · no local.json mappings',
|
|
3620
|
+
detail: localProxy.signed
|
|
3621
|
+
? 'hubspot.fetch leaves this machine signed with the app secret'
|
|
3622
|
+
: 'hubspot.fetch leaves this machine unsigned; set HUBSPOT_CLIENT_SECRET to sign',
|
|
3623
|
+
status: localProxy.signed ? 'ok' : 'warn',
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
return rows;
|
|
3627
|
+
}
|
|
3628
|
+
function describeLocalProxyIssue(localProxy, probe) {
|
|
3629
|
+
if (localProxy.mode === 'never') {
|
|
3630
|
+
return 'ignored (--local-proxy never); hubspot.fetch stays on HubSpot proxy';
|
|
3631
|
+
}
|
|
3632
|
+
if (!probe.applied) {
|
|
3633
|
+
return `${probe.to} not listening; hubspot.fetch stays on HubSpot proxy — start it and restart, or --local-proxy always`;
|
|
3634
|
+
}
|
|
3635
|
+
return `${probe.to} not listening — every hubspot.fetch to this origin fails until it starts (--local-proxy always)`;
|
|
3636
|
+
}
|
|
3323
3637
|
//# sourceMappingURL=dev.js.map
|