@hs-x/cli 0.4.12 → 0.4.13
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 +368 -16
- 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 +11 -0
- package/dist/dev/component-selection.d.ts.map +1 -1
- package/dist/dev/component-selection.js +23 -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-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 +96 -4
- 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 +49 -1
- package/dist/dev/session-manager.d.ts.map +1 -1
- package/dist/dev/session-manager.js +152 -12
- package/dist/dev/session-manager.js.map +1 -1
- 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 +6 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +20 -0
- 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 +8 -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;AAwD1D,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;AAm9BzC,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;AA+yFD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqJlB,CAAC"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -15,13 +15,14 @@ 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 { resolveHubSpotLocalProxyMode } from '../dev/local-proxy.js';
|
|
20
21
|
import { createManagedDevEventWriter, createManagedDevSession, markManagedDevSessionReady, markManagedDevSessionState, prepareManagedDevSessionRestart, publicManagedDevSession, readManagedDevEvents, readManagedDevSession, reconcileManagedDevSession, setManagedDevSessionPid, stopManagedDevSessionProcess, waitForManagedDevSessionOwner, } from '../dev/managed-session.js';
|
|
21
22
|
import { findListeningProcess, isHubSpotDevPortConflict, stopProcessCommand, terminateProcessGracefully, } from '../dev/port-process.js';
|
|
22
23
|
import { executeCliHttp } from '../effect-http.js';
|
|
23
24
|
import { discoverHubSpotAccounts, exchangePak } from '../hubspot-auth.js';
|
|
24
|
-
import { detectLocalProjectMode, validateHubSpotProject } from '../hubspot-project.js';
|
|
25
|
+
import { detectLocalProjectMode, findContainingProjectRoot, validateHubSpotProject, } from '../hubspot-project.js';
|
|
25
26
|
import { LocalProjectPickerCancelled, resolveLocalProject } from '../local-project-picker.js';
|
|
26
27
|
import { requestMutationConsent } from '../mutation-consent.js';
|
|
27
28
|
import { isMachineOutput } from '../output-context.js';
|
|
@@ -281,6 +282,7 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
281
282
|
const reporter = createReporter({ command: 'dev', argv, entry: !json });
|
|
282
283
|
const profile = resolveFlag(argv, '--profile');
|
|
283
284
|
const preserveState = argv.includes('--preserve-state');
|
|
285
|
+
const yes = argv.some((argument) => argument === '--yes' || argument === '-y');
|
|
284
286
|
const validation = await validateHubSpotProject({ root, ...(profile ? { profile } : {}) });
|
|
285
287
|
if (!validation.ok) {
|
|
286
288
|
const result = {
|
|
@@ -363,15 +365,62 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
363
365
|
}
|
|
364
366
|
return { exitCode: 1 };
|
|
365
367
|
}
|
|
366
|
-
|
|
368
|
+
// Mirror `hs project dev`: recommend a developer test account or sandbox,
|
|
369
|
+
// and never land on the production project account without saying so.
|
|
370
|
+
const testingPlan = planHubSpotDevTestingAccount({
|
|
371
|
+
accounts,
|
|
372
|
+
projectAccount,
|
|
373
|
+
// selectHubSpotDirectDevAccounts defaults the testing account to the
|
|
374
|
+
// project account; only an explicit flag counts as an answer here.
|
|
375
|
+
selectedTestingAccount: requestedTestingAccount ? selectedTestingAccount : undefined,
|
|
376
|
+
interactive: !json && !yes && isInteractive(),
|
|
377
|
+
});
|
|
378
|
+
let testingAccount;
|
|
379
|
+
let testingChoice;
|
|
380
|
+
if (testingPlan.kind === 'prompt') {
|
|
381
|
+
const picked = await promptSelect({
|
|
382
|
+
message: 'Choose the account to test on',
|
|
383
|
+
options: testingPlan.choices.map((choice) => ({
|
|
384
|
+
value: String(choice.account.portalId),
|
|
385
|
+
...describeHubSpotDevTestingChoice(choice),
|
|
386
|
+
})),
|
|
387
|
+
default: String(testingPlan.choices[0]?.account.portalId),
|
|
388
|
+
});
|
|
389
|
+
if (picked === undefined)
|
|
390
|
+
return cancelledResult(argv, 'dev');
|
|
391
|
+
const choice = testingPlan.choices.find((item) => String(item.account.portalId) === picked);
|
|
392
|
+
if (!choice)
|
|
393
|
+
return cancelledResult(argv, 'dev');
|
|
394
|
+
testingAccount = choice.account;
|
|
395
|
+
testingChoice = choice.kind;
|
|
396
|
+
reporter.info(`Testing on ${testingAccount.name} (${testingAccount.portalId}) — ${describeTestingChoiceForRow(testingChoice)}`);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
testingAccount = testingPlan.account;
|
|
400
|
+
testingChoice = testingPlan.choice;
|
|
401
|
+
if (!json) {
|
|
402
|
+
const explanation = describeHubSpotDevTestingPlanReason(testingPlan);
|
|
403
|
+
if (explanation.level === 'warn') {
|
|
404
|
+
reporter.warn('HSX_W_DEV_TESTING_ON_PRODUCTION_ACCOUNT', explanation.message);
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
reporter.info(explanation.message);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
367
411
|
if (testingAccount.personalAccessKey) {
|
|
412
|
+
const credentialStep = json
|
|
413
|
+
? undefined
|
|
414
|
+
: reporter.step(`Checking ${testingAccount.name} (${testingAccount.portalId}) credentials`);
|
|
368
415
|
try {
|
|
369
416
|
const exchanged = await exchangePak(testingAccount.personalAccessKey);
|
|
370
417
|
if (exchanged.portalId !== testingAccount.portalId) {
|
|
371
418
|
throw new Error(`the configured PAK belongs to account ${exchanged.portalId}, not ${testingAccount.portalId}`);
|
|
372
419
|
}
|
|
420
|
+
credentialStep?.ok();
|
|
373
421
|
}
|
|
374
422
|
catch (cause) {
|
|
423
|
+
credentialStep?.fail();
|
|
375
424
|
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
425
|
if (json) {
|
|
377
426
|
write(`${JSON.stringify({
|
|
@@ -394,12 +443,19 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
394
443
|
const existingDevServer = await recoverHubSpotDevPort({ argv, json, reporter });
|
|
395
444
|
if (existingDevServer.kind === 'exit')
|
|
396
445
|
return existingDevServer.result;
|
|
446
|
+
const probeStep = json ? undefined : reporter.step('Reading HubSpot project components');
|
|
397
447
|
const uiExtensionNodes = await probeHubSpotUiNodes({
|
|
398
448
|
root,
|
|
399
449
|
portalIdFlag: String(projectAccount.portalId),
|
|
400
450
|
accountId: undefined,
|
|
401
451
|
...(profile ? { profile } : {}),
|
|
402
452
|
});
|
|
453
|
+
if (uiExtensionNodes) {
|
|
454
|
+
probeStep?.ok(`${uiExtensionNodes.candidates.length} component${uiExtensionNodes.candidates.length === 1 ? '' : 's'}${uiExtensionNodes.application?.name ? ` · ${uiExtensionNodes.application.name}` : ''}`);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
probeStep?.warn('no UI-extension components found');
|
|
458
|
+
}
|
|
403
459
|
const requestedComponents = resolveFlags(argv, '--component');
|
|
404
460
|
const resolvedComponents = resolveHubSpotDevComponentSelectors(uiExtensionNodes?.candidates ?? [], requestedComponents);
|
|
405
461
|
const requestedAppCheck = resolvedComponents.ok && requestedComponents.length > 0
|
|
@@ -456,6 +512,48 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
456
512
|
return cancelledResult(argv, 'dev');
|
|
457
513
|
selectedNodeUids = picked;
|
|
458
514
|
}
|
|
515
|
+
// `hs project dev` warns before developing a marketplace app that real
|
|
516
|
+
// portals have installed. Same check, same numbers, same default of "no".
|
|
517
|
+
let productionInstallCount;
|
|
518
|
+
const application = uiExtensionNodes?.application;
|
|
519
|
+
if (isHubSpotMarketplaceApp(application) && application && projectAccount.personalAccessKey) {
|
|
520
|
+
const appLabel = application.name ?? application.uid;
|
|
521
|
+
const installStep = json
|
|
522
|
+
? undefined
|
|
523
|
+
: reporter.step(`Checking ${appLabel} production installs`);
|
|
524
|
+
const check = await checkProductionInstallsForDev({
|
|
525
|
+
root,
|
|
526
|
+
projectAccount,
|
|
527
|
+
application,
|
|
528
|
+
});
|
|
529
|
+
if (!check.ok) {
|
|
530
|
+
installStep?.warn('could not check');
|
|
531
|
+
if (!json)
|
|
532
|
+
reporter.warn('HSX_W_DEV_PRODUCTION_APP_CHECK_FAILED', check.reason);
|
|
533
|
+
}
|
|
534
|
+
else if (check.installCount === 0) {
|
|
535
|
+
installStep?.ok('none');
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
installStep?.warn(`${check.installCount} ${check.installCount === 1 ? 'account' : 'accounts'}`);
|
|
539
|
+
productionInstallCount = check.installCount;
|
|
540
|
+
const { describeHubSpotProductionInstallWarning, HUBSPOT_PRODUCTION_APP_CONFIRMATION } = await import('../dev/production-app-check.js');
|
|
541
|
+
const warning = describeHubSpotProductionInstallWarning(application.name ?? application.uid, check.installCount);
|
|
542
|
+
if (!json)
|
|
543
|
+
reporter.warn('HSX_W_DEV_PRODUCTION_APP_INSTALLS', warning);
|
|
544
|
+
if (!json && !yes && isInteractive()) {
|
|
545
|
+
const proceed = await promptConfirm({
|
|
546
|
+
message: HUBSPOT_PRODUCTION_APP_CONFIRMATION,
|
|
547
|
+
default: false,
|
|
548
|
+
// The checks above take seconds; an Enter mashed meanwhile must not
|
|
549
|
+
// answer "no" on the user's behalf.
|
|
550
|
+
discardTypeAhead: true,
|
|
551
|
+
});
|
|
552
|
+
if (!proceed)
|
|
553
|
+
return cancelledResult(argv, 'dev');
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
459
557
|
let directDevLogStream;
|
|
460
558
|
if (!json) {
|
|
461
559
|
const hsxLogPortValue = resolveFlag(argv, '--hsx-log-port') ?? '9099';
|
|
@@ -486,6 +584,7 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
486
584
|
root,
|
|
487
585
|
reporter,
|
|
488
586
|
filter: onlyParsed.filter,
|
|
587
|
+
hubspotAccount: projectAccount,
|
|
489
588
|
...(managedSessionId ? { managedSessionId } : {}),
|
|
490
589
|
});
|
|
491
590
|
}
|
|
@@ -497,6 +596,16 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
497
596
|
...(selectedNodeUids ? { selectedNodeUids } : {}),
|
|
498
597
|
...(profile ? { profile } : {}),
|
|
499
598
|
preserveState,
|
|
599
|
+
localProxyMode: resolveLocalProxyMode(argv),
|
|
600
|
+
...(directDevLogStream
|
|
601
|
+
? {
|
|
602
|
+
relayUrl: directDevLogStream.url,
|
|
603
|
+
onRelayReady: (handle) => {
|
|
604
|
+
if (directDevLogStream)
|
|
605
|
+
directDevLogStream.relay.current = handle;
|
|
606
|
+
},
|
|
607
|
+
}
|
|
608
|
+
: {}),
|
|
500
609
|
onHeartbeatError: (failure) => {
|
|
501
610
|
const warning = describeHeartbeatFailure(failure, testingAccount.portalId);
|
|
502
611
|
if (!warning)
|
|
@@ -609,11 +718,15 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
609
718
|
portalId: testingAccount.portalId,
|
|
610
719
|
projectAccountId: projectAccount.portalId,
|
|
611
720
|
testingAccountId: testingAccount.portalId,
|
|
721
|
+
testingAccountKind: testingChoice,
|
|
722
|
+
...(productionInstallCount !== undefined ? { productionInstallCount } : {}),
|
|
612
723
|
sessionId: session.sessionId,
|
|
613
724
|
components: session.nodeUids,
|
|
614
725
|
controlPlaneUsed: false,
|
|
615
726
|
cloudflareUsed: false,
|
|
616
|
-
preserveState,
|
|
727
|
+
preserveState: preserveState && session.frontendLogs.active,
|
|
728
|
+
frontendLogs: session.frontendLogs,
|
|
729
|
+
localProxy: session.localProxy,
|
|
617
730
|
};
|
|
618
731
|
if (json)
|
|
619
732
|
write(`${JSON.stringify(started, null, 2)}\n`);
|
|
@@ -622,22 +735,57 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
|
|
|
622
735
|
reporter.rows([
|
|
623
736
|
{ key: 'mode', value: 'HubSpot direct' },
|
|
624
737
|
{ key: 'project-account', value: `${projectAccount.name} (${projectAccount.portalId})` },
|
|
625
|
-
{
|
|
738
|
+
{
|
|
739
|
+
key: 'testing-account',
|
|
740
|
+
value: `${testingAccount.name} (${testingAccount.portalId})`,
|
|
741
|
+
detail: describeTestingChoiceForRow(testingChoice),
|
|
742
|
+
...(testingChoice === 'production'
|
|
743
|
+
? { status: 'warn' }
|
|
744
|
+
: { status: 'ok' }),
|
|
745
|
+
},
|
|
746
|
+
...(productionInstallCount !== undefined
|
|
747
|
+
? [
|
|
748
|
+
{
|
|
749
|
+
key: 'production installs',
|
|
750
|
+
value: `${productionInstallCount} ${productionInstallCount === 1 ? 'account' : 'accounts'}`,
|
|
751
|
+
detail: 'local changes can affect these installs',
|
|
752
|
+
status: 'warn',
|
|
753
|
+
},
|
|
754
|
+
]
|
|
755
|
+
: []),
|
|
626
756
|
{ key: 'ui-extensions', value: `session ${session.sessionId}`, status: 'ok' },
|
|
627
757
|
{ key: 'components', value: String(session.nodeUids.length) },
|
|
628
|
-
...(preserveState
|
|
758
|
+
...(preserveState && session.frontendLogs.active
|
|
629
759
|
? [{ key: 'reload state', value: 'JSON-safe React useState values', status: 'ok' }]
|
|
630
760
|
: []),
|
|
631
761
|
...(directDevLogStream
|
|
632
762
|
? [
|
|
633
763
|
{
|
|
634
764
|
key: 'logs',
|
|
635
|
-
value:
|
|
636
|
-
|
|
765
|
+
value: [
|
|
766
|
+
...(session.frontendLogs.active ? ['frontend'] : []),
|
|
767
|
+
'backend',
|
|
768
|
+
...(directDevLogStream.hubspotLane.active ? ['HubSpot events'] : []),
|
|
769
|
+
].join(' + '),
|
|
770
|
+
detail: session.frontendLogs.transport === 'fetch'
|
|
771
|
+
? `${directDevLogStream.url} · card logs via HubSpot local proxy`
|
|
772
|
+
: directDevLogStream.url,
|
|
637
773
|
status: 'ok',
|
|
638
774
|
},
|
|
775
|
+
hubspotLogsLaneRow(directDevLogStream.hubspotLane),
|
|
776
|
+
]
|
|
777
|
+
: []),
|
|
778
|
+
...(session.frontendLogs.unavailableReason
|
|
779
|
+
? [
|
|
780
|
+
{
|
|
781
|
+
key: 'frontend logs',
|
|
782
|
+
value: 'off',
|
|
783
|
+
detail: session.frontendLogs.unavailableReason,
|
|
784
|
+
status: 'warn',
|
|
785
|
+
},
|
|
639
786
|
]
|
|
640
787
|
: []),
|
|
788
|
+
...localProxyRows(session.localProxy),
|
|
641
789
|
{ key: 'Cloudflare', value: 'not used' },
|
|
642
790
|
{ key: 'HS-X control plane', value: 'not used' },
|
|
643
791
|
]);
|
|
@@ -707,6 +855,9 @@ const KNOWN_DEV_SUBCOMMANDS = new Set([...MANAGED_DEV_SUBCOMMANDS, 'invoke', 'cl
|
|
|
707
855
|
const MANAGED_DEV_LANES = new Set(['frontend', 'backend', 'request', 'hubspot']);
|
|
708
856
|
const MANAGED_DEV_LEVELS = new Set(['debug', 'info', 'warn', 'error']);
|
|
709
857
|
export async function devCommand({ argv, root, json, reporter, }) {
|
|
858
|
+
// Resolve once before direct/enhanced mode detection and managed-session
|
|
859
|
+
// creation so starting in src/app uses the same project, bindings, and cwd.
|
|
860
|
+
root = (await findContainingProjectRoot(root)) ?? root;
|
|
710
861
|
const managedSessionId = resolveFlag(argv, '--managed-session');
|
|
711
862
|
if (managedSessionId) {
|
|
712
863
|
await waitForManagedDevSessionOwner(managedSessionId);
|
|
@@ -1023,6 +1174,9 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1023
1174
|
const requestStarted = Date.now();
|
|
1024
1175
|
const method = request.method ?? 'GET';
|
|
1025
1176
|
const path = request.url ?? '/';
|
|
1177
|
+
const capabilityId = capabilityIdFromDevPath(path);
|
|
1178
|
+
const traceHeader = request.headers['x-hsx-trace-id'];
|
|
1179
|
+
const traceId = (Array.isArray(traceHeader) ? traceHeader[0] : traceHeader)?.trim();
|
|
1026
1180
|
response.on('finish', () => {
|
|
1027
1181
|
renderRequestLog({
|
|
1028
1182
|
method,
|
|
@@ -1030,6 +1184,8 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1030
1184
|
status: response.statusCode,
|
|
1031
1185
|
durationMs: Date.now() - requestStarted,
|
|
1032
1186
|
source: 'local',
|
|
1187
|
+
...(capabilityId ? { capabilityId } : {}),
|
|
1188
|
+
...(traceId ? { traceId: traceId.slice(0, 128) } : {}),
|
|
1033
1189
|
});
|
|
1034
1190
|
});
|
|
1035
1191
|
if (path === '/_hsx/health') {
|
|
@@ -1244,6 +1400,11 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1244
1400
|
runtimeOrigin: migratedCardTunnel.url,
|
|
1245
1401
|
})
|
|
1246
1402
|
: undefined;
|
|
1403
|
+
// The sidecar (below) is what the card's log relay posts to. Its URL is
|
|
1404
|
+
// fixed by --hsx-log-port, so the HubSpot session can be told about it
|
|
1405
|
+
// before it starts; the relay handler lands in this shared slot.
|
|
1406
|
+
const hsxLogPort = Number(resolveFlag(argv, '--hsx-log-port') ?? '9099');
|
|
1407
|
+
const hsxRelay = {};
|
|
1247
1408
|
// Optional: start a HubSpot UI-extension dev session if this directory
|
|
1248
1409
|
// contains an hsproject.json AND the user selected at least one UI node.
|
|
1249
1410
|
const hubSpotDevSession = selectedUiNodeUids && selectedUiNodeUids.length > 0
|
|
@@ -1254,12 +1415,15 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1254
1415
|
accountId,
|
|
1255
1416
|
reporter,
|
|
1256
1417
|
selectedNodeUids: selectedUiNodeUids,
|
|
1418
|
+
relayUrl: `http://127.0.0.1:${hsxLogPort}`,
|
|
1419
|
+
onRelayReady: (handle) => {
|
|
1420
|
+
hsxRelay.current = handle;
|
|
1421
|
+
},
|
|
1257
1422
|
})
|
|
1258
1423
|
: undefined;
|
|
1259
1424
|
// HS-X dev sidecar — receives frontend logs (and later: backend/request
|
|
1260
1425
|
// events) from inside the card iframe and surfaces them in the terminal.
|
|
1261
1426
|
// 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
1427
|
// `--only` selectors pre-filter the stream (same grammar as the interactive
|
|
1264
1428
|
// keyboard, see dev/stream-filter.ts). Invalid selectors are a usage error.
|
|
1265
1429
|
const { parseOnlySelectors } = await import('../dev/stream-filter.js');
|
|
@@ -1279,16 +1443,29 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1279
1443
|
root,
|
|
1280
1444
|
reporter,
|
|
1281
1445
|
filter: onlyParsed.filter,
|
|
1446
|
+
relay: hsxRelay,
|
|
1282
1447
|
...(managedLogSessionId ? { managedSessionId: managedLogSessionId } : {}),
|
|
1283
1448
|
});
|
|
1284
1449
|
if (devLogStream) {
|
|
1285
1450
|
const paint = makeDevLogPaint(process.env, process.stderr);
|
|
1286
1451
|
requestSink = (entry) => {
|
|
1452
|
+
const effectiveAccountId = entry.accountId ?? accountId;
|
|
1287
1453
|
devLogStream.bus.emit({
|
|
1288
1454
|
lane: 'request',
|
|
1289
1455
|
level: entry.status >= 500 ? 'error' : entry.status >= 400 ? 'warn' : 'info',
|
|
1290
1456
|
ts: Date.now(),
|
|
1291
1457
|
msg: formatDevLogEntry(entry, paint, accountId),
|
|
1458
|
+
fields: {
|
|
1459
|
+
method: entry.method,
|
|
1460
|
+
path: entry.path,
|
|
1461
|
+
status: entry.status,
|
|
1462
|
+
durationMs: entry.durationMs,
|
|
1463
|
+
origin: entry.source ?? 'local',
|
|
1464
|
+
...(effectiveAccountId ? { accountId: effectiveAccountId } : {}),
|
|
1465
|
+
...(entry.capabilityId ? { capabilityId: entry.capabilityId } : {}),
|
|
1466
|
+
...(entry.traceId ? { traceId: entry.traceId } : {}),
|
|
1467
|
+
...(entry.detail ? { detail: entry.detail } : {}),
|
|
1468
|
+
},
|
|
1292
1469
|
});
|
|
1293
1470
|
};
|
|
1294
1471
|
}
|
|
@@ -1376,7 +1553,10 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1376
1553
|
]
|
|
1377
1554
|
: []),
|
|
1378
1555
|
...(devLogStream
|
|
1379
|
-
? [
|
|
1556
|
+
? [
|
|
1557
|
+
{ status: 'ok', key: 'logs', value: `streaming on ${devLogStream.url}` },
|
|
1558
|
+
hubspotLogsLaneRow(devLogStream.hubspotLane),
|
|
1559
|
+
]
|
|
1380
1560
|
: []),
|
|
1381
1561
|
...(devTunnel ? [{ status: 'ok', key: 'tunnel', value: devTunnel.url }] : []),
|
|
1382
1562
|
...(migratedCardDevArtifacts
|
|
@@ -1501,6 +1681,17 @@ async function devCommandCore({ argv, root, json, }) {
|
|
|
1501
1681
|
});
|
|
1502
1682
|
return { exitCode: 0 };
|
|
1503
1683
|
}
|
|
1684
|
+
function capabilityIdFromDevPath(path) {
|
|
1685
|
+
const match = /^\/_hsx\/(?:cards|invoke)\/([^/?]+)/.exec(path);
|
|
1686
|
+
if (!match?.[1])
|
|
1687
|
+
return undefined;
|
|
1688
|
+
try {
|
|
1689
|
+
return decodeURIComponent(match[1]);
|
|
1690
|
+
}
|
|
1691
|
+
catch {
|
|
1692
|
+
return match[1];
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1504
1695
|
async function managedDevStartCommand(input) {
|
|
1505
1696
|
const reporter = input.reporter ??
|
|
1506
1697
|
createReporter({ command: 'dev start', argv: input.argv, entry: !input.json });
|
|
@@ -2091,6 +2282,9 @@ function managedDevFollowReadFilter(filter) {
|
|
|
2091
2282
|
function delayMs(ms) {
|
|
2092
2283
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2093
2284
|
}
|
|
2285
|
+
const HUBSPOT_LOGS_POLL_INTERVAL_MS = 30_000;
|
|
2286
|
+
/** First poll reaches this far back so the lane shows recent activity at once. */
|
|
2287
|
+
const HUBSPOT_LOGS_CATCH_UP_MS = 2 * 60_000;
|
|
2094
2288
|
async function startHsxDevLogStream(input) {
|
|
2095
2289
|
try {
|
|
2096
2290
|
const [{ createDevEventBus }, { startDevLogServer }, { startDevStreamRenderer }] = await Promise.all([
|
|
@@ -2100,7 +2294,8 @@ async function startHsxDevLogStream(input) {
|
|
|
2100
2294
|
]);
|
|
2101
2295
|
const bus = createDevEventBus();
|
|
2102
2296
|
const authToken = randomBytes(32).toString('base64url');
|
|
2103
|
-
const
|
|
2297
|
+
const relay = input.relay ?? {};
|
|
2298
|
+
const server = await startDevLogServer({ bus, port: input.port, authToken, relay });
|
|
2104
2299
|
const renderer = startDevStreamRenderer({
|
|
2105
2300
|
bus,
|
|
2106
2301
|
deferStatus: true,
|
|
@@ -2117,24 +2312,61 @@ async function startHsxDevLogStream(input) {
|
|
|
2117
2312
|
// Missing binding/credentials just means the lane stays off; `hs-x dev`
|
|
2118
2313
|
// must work without HubSpot credentials.
|
|
2119
2314
|
let hubspotPoller;
|
|
2315
|
+
let hubspotLane = {
|
|
2316
|
+
active: false,
|
|
2317
|
+
reason: 'HubSpot app logs could not be set up.',
|
|
2318
|
+
};
|
|
2120
2319
|
try {
|
|
2121
2320
|
const [{ resolveHubSpotLogsSource }, { startHubSpotLogsPoller }] = await Promise.all([
|
|
2122
2321
|
import('../hubspot-app-logs.js'),
|
|
2123
2322
|
import('../dev/hubspot-logs-poller.js'),
|
|
2124
2323
|
]);
|
|
2125
|
-
const
|
|
2324
|
+
const account = input.hubspotAccount;
|
|
2325
|
+
const resolution = await resolveHubSpotLogsSource({
|
|
2326
|
+
cwd: input.root,
|
|
2327
|
+
...(account?.personalAccessKey
|
|
2328
|
+
? {
|
|
2329
|
+
credentials: {
|
|
2330
|
+
developerAccountId: account.portalId,
|
|
2331
|
+
personalAccessKey: account.personalAccessKey,
|
|
2332
|
+
},
|
|
2333
|
+
}
|
|
2334
|
+
: {}),
|
|
2335
|
+
});
|
|
2126
2336
|
if (resolution.ok) {
|
|
2127
|
-
hubspotPoller = startHubSpotLogsPoller({
|
|
2337
|
+
hubspotPoller = startHubSpotLogsPoller({
|
|
2338
|
+
bus,
|
|
2339
|
+
source: resolution.source,
|
|
2340
|
+
intervalMs: HUBSPOT_LOGS_POLL_INTERVAL_MS,
|
|
2341
|
+
catchUpMs: HUBSPOT_LOGS_CATCH_UP_MS,
|
|
2342
|
+
});
|
|
2343
|
+
// Poll once now so the lane proves itself (or its first error) within
|
|
2344
|
+
// seconds of startup instead of one interval later.
|
|
2345
|
+
void hubspotPoller.poll();
|
|
2346
|
+
hubspotLane = {
|
|
2347
|
+
active: true,
|
|
2348
|
+
appId: resolution.source.appId,
|
|
2349
|
+
developerAccountId: resolution.source.developerAccountId,
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
else {
|
|
2353
|
+
hubspotLane = { active: false, reason: resolution.reason };
|
|
2128
2354
|
}
|
|
2129
2355
|
}
|
|
2130
|
-
catch {
|
|
2356
|
+
catch (cause) {
|
|
2131
2357
|
// The HubSpot lane is additive; never fail the dev stream over it.
|
|
2358
|
+
hubspotLane = {
|
|
2359
|
+
active: false,
|
|
2360
|
+
reason: cause instanceof Error ? cause.message : String(cause),
|
|
2361
|
+
};
|
|
2132
2362
|
}
|
|
2133
2363
|
return {
|
|
2134
2364
|
url: server.url,
|
|
2135
2365
|
authToken,
|
|
2136
2366
|
renderer,
|
|
2137
2367
|
bus,
|
|
2368
|
+
hubspotLane,
|
|
2369
|
+
relay,
|
|
2138
2370
|
async cleanup() {
|
|
2139
2371
|
hubspotPoller?.stop();
|
|
2140
2372
|
unsubscribeManagedEvents?.();
|
|
@@ -2191,7 +2423,13 @@ async function probeHubSpotUiNodes(input) {
|
|
|
2191
2423
|
accountId: portalId,
|
|
2192
2424
|
}, input.profile ? { profile: input.profile } : {});
|
|
2193
2425
|
const candidates = listHubSpotDevComponentCandidates(ir.intermediateNodesIndexedByUid);
|
|
2194
|
-
|
|
2426
|
+
const application = findHubSpotDevApplication(ir.intermediateNodesIndexedByUid);
|
|
2427
|
+
return {
|
|
2428
|
+
uids: candidates.map((candidate) => candidate.uid),
|
|
2429
|
+
candidates,
|
|
2430
|
+
portalId,
|
|
2431
|
+
...(application ? { application } : {}),
|
|
2432
|
+
};
|
|
2195
2433
|
}
|
|
2196
2434
|
catch {
|
|
2197
2435
|
// IR translate failed — caller falls back to capability-only behavior.
|
|
@@ -2216,6 +2454,9 @@ async function maybeStartHubSpotDevSession(input) {
|
|
|
2216
2454
|
selectedNodeUids: input.selectedNodeUids,
|
|
2217
2455
|
...(profile ? { profile } : {}),
|
|
2218
2456
|
preserveState: input.argv.includes('--preserve-state'),
|
|
2457
|
+
localProxyMode: resolveLocalProxyMode(input.argv),
|
|
2458
|
+
...(input.relayUrl ? { relayUrl: input.relayUrl } : {}),
|
|
2459
|
+
...(input.onRelayReady ? { onRelayReady: input.onRelayReady } : {}),
|
|
2219
2460
|
onHeartbeatError: (failure) => {
|
|
2220
2461
|
const warning = describeHeartbeatFailure(failure, portalId);
|
|
2221
2462
|
if (warning && !input.json)
|
|
@@ -2225,6 +2466,16 @@ async function maybeStartHubSpotDevSession(input) {
|
|
|
2225
2466
|
input.reporter
|
|
2226
2467
|
.step('HubSpot UI-extension session')
|
|
2227
2468
|
.ok(`id ${session.sessionId} · ${session.nodeUids.length} component${session.nodeUids.length === 1 ? '' : 's'}`);
|
|
2469
|
+
if (!input.json) {
|
|
2470
|
+
if (session.frontendLogs.unavailableReason) {
|
|
2471
|
+
input.reporter.warn('HSX_W_DEV_FRONTEND_LOGS_UNAVAILABLE', session.frontendLogs.unavailableReason);
|
|
2472
|
+
}
|
|
2473
|
+
for (const probe of session.localProxy.mappings) {
|
|
2474
|
+
if (probe.applied && probe.reachable)
|
|
2475
|
+
continue;
|
|
2476
|
+
input.reporter.warn('HSX_W_DEV_LOCAL_PROXY_UNREACHABLE', `local.json proxies ${probe.from} → ${probe.to}: ${describeLocalProxyIssue(session.localProxy, probe)}`);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2228
2479
|
return session;
|
|
2229
2480
|
}
|
|
2230
2481
|
catch (error) {
|
|
@@ -2953,6 +3204,59 @@ function formatDevExpiry(now, expiresAt) {
|
|
|
2953
3204
|
function isFlagValue(value) {
|
|
2954
3205
|
return value !== undefined && value.length > 0 && !value.startsWith('-');
|
|
2955
3206
|
}
|
|
3207
|
+
/**
|
|
3208
|
+
* The HubSpot-side lane is the only place renders and proxy executions show
|
|
3209
|
+
* up, so its state must be on the checklist: a silent lane looks identical to
|
|
3210
|
+
* a broken one.
|
|
3211
|
+
*/
|
|
3212
|
+
function hubspotLogsLaneRow(lane) {
|
|
3213
|
+
if (lane.active) {
|
|
3214
|
+
return {
|
|
3215
|
+
key: 'HubSpot app logs',
|
|
3216
|
+
value: `app ${lane.appId} · renders + proxy executions`,
|
|
3217
|
+
detail: `read from account ${lane.developerAccountId} every ${HUBSPOT_LOGS_POLL_INTERVAL_MS / 1000}s; entries appear shortly after the card acts`,
|
|
3218
|
+
status: 'ok',
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3221
|
+
return { key: 'HubSpot app logs', value: 'off', detail: lane.reason, status: 'warn' };
|
|
3222
|
+
}
|
|
3223
|
+
function describeTestingChoiceForRow(choice) {
|
|
3224
|
+
switch (choice) {
|
|
3225
|
+
case 'developer-test':
|
|
3226
|
+
return 'developer test account';
|
|
3227
|
+
case 'sandbox':
|
|
3228
|
+
return 'sandbox';
|
|
3229
|
+
default:
|
|
3230
|
+
return 'production';
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Resolve the deployed app and count its non-test installs with the project
|
|
3235
|
+
* account's own credentials — the same lookup `hs project dev` performs.
|
|
3236
|
+
*/
|
|
3237
|
+
async function checkProductionInstallsForDev(input) {
|
|
3238
|
+
try {
|
|
3239
|
+
const projectConfig = JSON.parse(await readFile(join(input.root, 'hsproject.json'), 'utf8'));
|
|
3240
|
+
if (!projectConfig.name) {
|
|
3241
|
+
return { ok: false, reason: 'hsproject.json has no project name to look the app up by.' };
|
|
3242
|
+
}
|
|
3243
|
+
const [{ createHubSpotDeveloperClient }, { checkHubSpotProductionInstalls }] = await Promise.all([import('@hs-x/hubspot-cli'), import('../dev/production-app-check.js')]);
|
|
3244
|
+
const client = createHubSpotDeveloperClient({
|
|
3245
|
+
developerAccountId: input.projectAccount.portalId,
|
|
3246
|
+
...(input.projectAccount.personalAccessKey
|
|
3247
|
+
? { personalAccessKey: input.projectAccount.personalAccessKey }
|
|
3248
|
+
: {}),
|
|
3249
|
+
});
|
|
3250
|
+
return await checkHubSpotProductionInstalls({
|
|
3251
|
+
client,
|
|
3252
|
+
projectName: projectConfig.name,
|
|
3253
|
+
appUid: input.application.uid,
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
catch (cause) {
|
|
3257
|
+
return { ok: false, reason: cause instanceof Error ? cause.message : String(cause) };
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
2956
3260
|
function resolveFlag(argv, flag) {
|
|
2957
3261
|
const index = argv.indexOf(flag);
|
|
2958
3262
|
if (index !== -1) {
|
|
@@ -3021,6 +3325,7 @@ const projectAccountOption = Options.text('project-account').pipe(Options.option
|
|
|
3021
3325
|
const testingAccountOption = Options.text('testing-account').pipe(Options.optional);
|
|
3022
3326
|
const profileOption = Options.text('profile').pipe(Options.withAlias('p'), Options.optional);
|
|
3023
3327
|
const preserveStateOption = Options.boolean('preserve-state').pipe(Options.withDefault(false));
|
|
3328
|
+
const localProxyOption = Options.choice('local-proxy', ['auto', 'always', 'never']).pipe(Options.withDefault('auto'));
|
|
3024
3329
|
const targetOriginOption = Options.text('target-origin').pipe(Options.optional);
|
|
3025
3330
|
const telemetryOriginOption = Options.text('telemetry-origin').pipe(Options.optional);
|
|
3026
3331
|
const accountIdOption = Options.text('account-id').pipe(Options.optional);
|
|
@@ -3069,6 +3374,7 @@ export const devCmd = Command.make('dev', {
|
|
|
3069
3374
|
testingAccount: testingAccountOption,
|
|
3070
3375
|
profile: profileOption,
|
|
3071
3376
|
preserveState: preserveStateOption,
|
|
3377
|
+
localProxy: localProxyOption,
|
|
3072
3378
|
targetOrigin: targetOriginOption,
|
|
3073
3379
|
telemetryOrigin: telemetryOriginOption,
|
|
3074
3380
|
accountId: accountIdOption,
|
|
@@ -3148,6 +3454,8 @@ export const devCmd = Command.make('dev', {
|
|
|
3148
3454
|
argv.push('--profile', opts.profile.value);
|
|
3149
3455
|
if (opts.preserveState)
|
|
3150
3456
|
argv.push('--preserve-state');
|
|
3457
|
+
if (opts.localProxy !== 'auto')
|
|
3458
|
+
argv.push('--local-proxy', opts.localProxy);
|
|
3151
3459
|
if (Option.isSome(opts.targetOrigin)) {
|
|
3152
3460
|
argv.push('--target-origin', opts.targetOrigin.value);
|
|
3153
3461
|
}
|
|
@@ -3320,4 +3628,48 @@ async function devInvokeRemote(args) {
|
|
|
3320
3628
|
reporter.done(ok ? 'Invoked' : 'Failed', ok ? 0 : 1);
|
|
3321
3629
|
return { exitCode: ok ? 0 : 1 };
|
|
3322
3630
|
}
|
|
3631
|
+
/**
|
|
3632
|
+
* `<app root>/local.json` proxy mappings switch HubSpot's dev server into local
|
|
3633
|
+
* proxy mode for `hubspot.fetch`; a mapping whose target is down fails every
|
|
3634
|
+
* fetch to that origin with an empty error, so show each one up front.
|
|
3635
|
+
*/
|
|
3636
|
+
function resolveLocalProxyMode(argv) {
|
|
3637
|
+
return resolveHubSpotLocalProxyMode(resolveFlag(argv, '--local-proxy'));
|
|
3638
|
+
}
|
|
3639
|
+
/**
|
|
3640
|
+
* One short row per local.json mapping, plus one when proxy mode is on with no
|
|
3641
|
+
* mappings (`--local-proxy always`). Details stay one clause long; the mode
|
|
3642
|
+
* semantics live in `hs-x dev --help` and the guide.
|
|
3643
|
+
*/
|
|
3644
|
+
function localProxyRows(localProxy) {
|
|
3645
|
+
const rows = localProxy.mappings.map((probe) => {
|
|
3646
|
+
const healthy = probe.applied && probe.reachable;
|
|
3647
|
+
return {
|
|
3648
|
+
key: 'local proxy',
|
|
3649
|
+
value: probe.applied ? `${probe.from} → ${probe.to}` : `off · ${probe.from}`,
|
|
3650
|
+
status: healthy ? 'ok' : 'warn',
|
|
3651
|
+
...(healthy ? {} : { detail: describeLocalProxyIssue(localProxy, probe) }),
|
|
3652
|
+
};
|
|
3653
|
+
});
|
|
3654
|
+
if (localProxy.active && localProxy.mappings.length === 0) {
|
|
3655
|
+
rows.push({
|
|
3656
|
+
key: 'local proxy',
|
|
3657
|
+
value: 'on · no local.json mappings',
|
|
3658
|
+
detail: localProxy.signed
|
|
3659
|
+
? 'hubspot.fetch leaves this machine signed with the app secret'
|
|
3660
|
+
: 'hubspot.fetch leaves this machine unsigned; set HUBSPOT_CLIENT_SECRET to sign',
|
|
3661
|
+
status: localProxy.signed ? 'ok' : 'warn',
|
|
3662
|
+
});
|
|
3663
|
+
}
|
|
3664
|
+
return rows;
|
|
3665
|
+
}
|
|
3666
|
+
function describeLocalProxyIssue(localProxy, probe) {
|
|
3667
|
+
if (localProxy.mode === 'never') {
|
|
3668
|
+
return 'ignored (--local-proxy never); hubspot.fetch stays on HubSpot proxy';
|
|
3669
|
+
}
|
|
3670
|
+
if (!probe.applied) {
|
|
3671
|
+
return `${probe.to} not listening; hubspot.fetch stays on HubSpot proxy — start it and restart, or --local-proxy always`;
|
|
3672
|
+
}
|
|
3673
|
+
return `${probe.to} not listening — every hubspot.fetch to this origin fails until it starts (--local-proxy always)`;
|
|
3674
|
+
}
|
|
3323
3675
|
//# sourceMappingURL=dev.js.map
|