@hs-x/cli 0.4.2-next.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/cli/index.d.ts.map +1 -1
  2. package/dist/cli/index.js +4 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/command-catalog.d.ts +2 -2
  5. package/dist/command-catalog.d.ts.map +1 -1
  6. package/dist/command-catalog.js +1 -0
  7. package/dist/command-catalog.js.map +1 -1
  8. package/dist/commands/deploy.d.ts +7 -0
  9. package/dist/commands/deploy.d.ts.map +1 -1
  10. package/dist/commands/deploy.js +39 -5
  11. package/dist/commands/deploy.js.map +1 -1
  12. package/dist/commands/dev.d.ts.map +1 -1
  13. package/dist/commands/dev.js +96 -21
  14. package/dist/commands/dev.js.map +1 -1
  15. package/dist/commands/help-command.d.ts.map +1 -1
  16. package/dist/commands/help-command.js +164 -0
  17. package/dist/commands/help-command.js.map +1 -1
  18. package/dist/commands/react-health.js +6 -10
  19. package/dist/commands/react-health.js.map +1 -1
  20. package/dist/commands/react.js +2 -2
  21. package/dist/commands/sync.d.ts +129 -0
  22. package/dist/commands/sync.d.ts.map +1 -0
  23. package/dist/commands/sync.js +771 -0
  24. package/dist/commands/sync.js.map +1 -0
  25. package/dist/constants.d.ts +1 -1
  26. package/dist/constants.d.ts.map +1 -1
  27. package/dist/constants.js +1 -1
  28. package/dist/constants.js.map +1 -1
  29. package/dist/dev/compat-shim.d.ts +36 -0
  30. package/dist/dev/compat-shim.d.ts.map +1 -1
  31. package/dist/dev/compat-shim.js +36 -1
  32. package/dist/dev/compat-shim.js.map +1 -1
  33. package/dist/dev/component-selection.d.ts +18 -1
  34. package/dist/dev/component-selection.d.ts.map +1 -1
  35. package/dist/dev/component-selection.js +53 -5
  36. package/dist/dev/component-selection.js.map +1 -1
  37. package/dist/dev/hubspot-logs-poller.d.ts +6 -0
  38. package/dist/dev/hubspot-logs-poller.d.ts.map +1 -1
  39. package/dist/dev/hubspot-logs-poller.js +36 -10
  40. package/dist/dev/hubspot-logs-poller.js.map +1 -1
  41. package/dist/dev/hubspot-token-repair.d.ts +36 -0
  42. package/dist/dev/hubspot-token-repair.d.ts.map +1 -0
  43. package/dist/dev/hubspot-token-repair.js +88 -0
  44. package/dist/dev/hubspot-token-repair.js.map +1 -0
  45. package/dist/dev/session-manager.d.ts +19 -0
  46. package/dist/dev/session-manager.d.ts.map +1 -1
  47. package/dist/dev/session-manager.js +41 -8
  48. package/dist/dev/session-manager.js.map +1 -1
  49. package/dist/errors-registry.d.ts.map +1 -1
  50. package/dist/errors-registry.js +8 -0
  51. package/dist/errors-registry.js.map +1 -1
  52. package/dist/prompt.d.ts +2 -0
  53. package/dist/prompt.d.ts.map +1 -1
  54. package/dist/prompt.js +6 -0
  55. package/dist/prompt.js.map +1 -1
  56. package/package.json +8 -8
@@ -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;AA0C1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAyvBzC,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,CAuErB;AA+lFD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiJlB,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;AA2C1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAuzBzC,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,CAuErB;AAmnFD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiJlB,CAAC"}
@@ -16,7 +16,7 @@ import { formatConfigUrl, loadCliConfig } from '../config.js';
16
16
  import { CLI_VERSION } from '../constants.js';
17
17
  import { controlPlaneAuthHeaders } from '../control-plane-fetch.js';
18
18
  import { selectHubSpotDirectDevAccounts } from '../dev/account-selection.js';
19
- import { listHubSpotDevComponentCandidates, resolveHubSpotDevComponentSelectors, } from '../dev/component-selection.js';
19
+ import { listHubSpotDevComponentCandidates, resolveHubSpotDevComponentSelectors, resolveHubSpotDevSessionApp, } from '../dev/component-selection.js';
20
20
  import { createManagedDevEventWriter, createManagedDevSession, markManagedDevSessionReady, markManagedDevSessionState, prepareManagedDevSessionRestart, publicManagedDevSession, readManagedDevEvents, readManagedDevSession, reconcileManagedDevSession, setManagedDevSessionPid, stopManagedDevSessionProcess, waitForManagedDevSessionOwner, } from '../dev/managed-session.js';
21
21
  import { findListeningProcess, isHubSpotDevPortConflict, stopProcessCommand, terminateProcessGracefully, } from '../dev/port-process.js';
22
22
  import { executeCliHttp } from '../effect-http.js';
@@ -44,6 +44,22 @@ function isRecord(value) {
44
44
  function isErrnoException(value) {
45
45
  return value instanceof Error && typeof value.code === 'string';
46
46
  }
47
+ /**
48
+ * One heartbeat failure is noise (HubSpot hiccups; the next tick retries).
49
+ * Three in a row is ~90s of a session HubSpot no longer acknowledges, which is
50
+ * when cards stop loading — say so once, then again every ten ticks so a long
51
+ * outage keeps getting a line without flooding the stream.
52
+ */
53
+ function describeHeartbeatFailure(failure, portalId) {
54
+ const streak = failure.consecutiveFailures;
55
+ if (streak < 3 || (streak > 3 && (streak - 3) % 10 !== 0))
56
+ return undefined;
57
+ const detail = failure.status !== undefined ? `HTTP ${failure.status}` : failure.message;
58
+ const hint = failure.status === 401 || failure.status === 403
59
+ ? `HubSpot rejected the credential for testing account ${portalId} — run \`hs account auth\` for that account, then restart \`hs-x dev\`.`
60
+ : `HubSpot may have dropped session ${failure.sessionId}; restart \`hs-x dev\` if cards stop loading.`;
61
+ return `HubSpot dev-session heartbeat failed ${streak} times in a row (${detail}). ${hint}`;
62
+ }
47
63
  function describeDevSessionFailure(cause) {
48
64
  const error = isRecord(cause) ? cause : undefined;
49
65
  const message = cause instanceof Error ? cause.message : String(cause);
@@ -385,17 +401,29 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
385
401
  });
386
402
  const requestedComponents = resolveFlags(argv, '--component');
387
403
  const resolvedComponents = resolveHubSpotDevComponentSelectors(uiExtensionNodes?.candidates ?? [], requestedComponents);
388
- if (!resolvedComponents.ok) {
404
+ const requestedAppCheck = resolvedComponents.ok && requestedComponents.length > 0
405
+ ? resolveHubSpotDevSessionApp(uiExtensionNodes?.candidates ?? [], resolvedComponents.uids)
406
+ : undefined;
407
+ const componentsProblem = !resolvedComponents.ok
408
+ ? { code: 'HSX_E_DEV_SESSION_NO_NODES', message: resolvedComponents.message }
409
+ : requestedAppCheck && !requestedAppCheck.ok
410
+ ? { code: 'HSX_E_DEV_SESSION_MULTI_APP', message: requestedAppCheck.message }
411
+ : undefined;
412
+ if (componentsProblem || !resolvedComponents.ok) {
413
+ const problem = componentsProblem ?? {
414
+ code: 'HSX_E_DEV_SESSION_NO_NODES',
415
+ message: 'No HubSpot dev components resolved.',
416
+ };
389
417
  if (json) {
390
418
  write(`${JSON.stringify({
391
419
  schema_version: 1,
392
420
  command: 'dev',
393
421
  ok: false,
394
- error: { code: 'HSX_E_DEV_SESSION_NO_NODES', message: resolvedComponents.message },
422
+ error: { code: problem.code, message: problem.message },
395
423
  }, null, 2)}\n`);
396
424
  }
397
425
  else {
398
- reporter.error('HSX_E_DEV_SESSION_NO_NODES', resolvedComponents.message);
426
+ reporter.error(problem.code, problem.message);
399
427
  reporter.done(undefined, 1);
400
428
  }
401
429
  return { exitCode: 1 };
@@ -408,11 +436,16 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
408
436
  uiExtensionNodes.candidates.length > 1 &&
409
437
  !json &&
410
438
  isInteractive()) {
439
+ const pickerCandidates = uiExtensionNodes.candidates;
411
440
  const picked = await promptMultiSelect({
412
441
  message: `Which HubSpot components should run in dev mode? (testing account ${testingAccount.portalId})`,
413
442
  initial: [],
414
443
  minSelected: 1,
415
- options: uiExtensionNodes.candidates.map((candidate) => ({
444
+ validate: (selection) => {
445
+ const check = resolveHubSpotDevSessionApp(pickerCandidates, selection);
446
+ return check.ok ? undefined : check.message;
447
+ },
448
+ options: pickerCandidates.map((candidate) => ({
416
449
  value: candidate.uid,
417
450
  label: candidate.label,
418
451
  description: candidate.description,
@@ -462,6 +495,23 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
462
495
  projectDir: root,
463
496
  ...(selectedNodeUids ? { selectedNodeUids } : {}),
464
497
  ...(profile ? { profile } : {}),
498
+ onHeartbeatError: (failure) => {
499
+ const warning = describeHeartbeatFailure(failure, testingAccount.portalId);
500
+ if (!warning)
501
+ return;
502
+ if (directDevLogStream) {
503
+ directDevLogStream.bus.emit({
504
+ lane: 'hubspot',
505
+ level: 'warn',
506
+ ts: failure.at.getTime(),
507
+ msg: warning,
508
+ source: 'dev-session',
509
+ });
510
+ }
511
+ else if (!json) {
512
+ reporter.warn('HSX_W_DEV_SESSION_HEARTBEAT', warning);
513
+ }
514
+ },
465
515
  ...(directDevLogStream
466
516
  ? {
467
517
  onFrontendLog: (entry) => {
@@ -615,6 +665,13 @@ async function hubSpotDirectDevCommand({ argv, root, json, }) {
615
665
  try {
616
666
  await session.cleanup();
617
667
  }
668
+ catch (error) {
669
+ // Shutdown is best-effort: a HubSpot-side failure (an expired token
670
+ // on the session delete, say) is worth one line, not a crash trace.
671
+ if (!json) {
672
+ reporter.warn('HSX_W_DEV_SESSION_CLEANUP', `HubSpot dev session was not cleaned up: ${describeDevSessionFailure(error)} HubSpot expires idle sessions on its own.`);
673
+ }
674
+ }
618
675
  finally {
619
676
  try {
620
677
  await directDevLogStream?.cleanup();
@@ -753,17 +810,24 @@ async function devCommandCore({ argv, root, json, }) {
753
810
  }
754
811
  const workers = await discoverWorkerManifests(root);
755
812
  await generateProjectArtifacts({ root, workers });
756
- let portalId = resolveFlag(argv, '--portal');
813
+ const noHubSpot = argv.includes('--no-hubspot');
814
+ // `--no-hubspot` is an explicit local-only posture. It must win over both
815
+ // an accidental --portal flag and any account discovered from local CLI
816
+ // state, otherwise dev tries to register a provider override before the
817
+ // existing HubSpot dev-session guard gets a chance to skip that work.
818
+ let portalId = noHubSpot ? undefined : resolveFlag(argv, '--portal');
757
819
  const targetOrigin = resolveFlag(argv, '--target-origin');
758
820
  const telemetryOrigin = resolveFlag(argv, '--telemetry-origin');
759
821
  const forceDevOverride = argv.includes('--force');
760
- const resolvedDevAcct = await resolveAccountIdOrPrompt({
761
- argv,
762
- json,
763
- purpose: 'this dev session',
764
- root,
765
- });
766
- const accountId = resolvedDevAcct.accountId;
822
+ const resolvedDevAcct = noHubSpot
823
+ ? undefined
824
+ : await resolveAccountIdOrPrompt({
825
+ argv,
826
+ json,
827
+ purpose: 'this dev session',
828
+ root,
829
+ });
830
+ const accountId = resolvedDevAcct?.accountId;
767
831
  const devCliConfig = loadCliConfig(argv);
768
832
  // Portal id: HubSpot account ids in our store look like `portal-46993937`;
769
833
  // strip the prefix when the user didn't pass --portal explicitly.
@@ -806,16 +870,18 @@ async function devCommandCore({ argv, root, json, }) {
806
870
  reporter.done(undefined, 10);
807
871
  return { exitCode: 10 };
808
872
  }
809
- if (!portalId) {
873
+ if (!noHubSpot && !portalId) {
810
874
  portalId = await resolveDefaultPortalId({ root, accountId });
811
875
  }
812
876
  // Probe the HubSpot project IR up front so the picker can show UI-extension
813
877
  // nodes alongside HS-X capabilities. No-op if hsproject.json isn't present.
814
- const uiExtensionNodes = await probeHubSpotUiNodes({
815
- root,
816
- portalIdFlag: portalId,
817
- accountId,
818
- });
878
+ const uiExtensionNodes = noHubSpot
879
+ ? undefined
880
+ : await probeHubSpotUiNodes({
881
+ root,
882
+ portalIdFlag: portalId,
883
+ accountId,
884
+ });
819
885
  let capabilityIds = selectedCapabilities.length > 0 ? selectedCapabilities : allCapabilityIds;
820
886
  let selectedUiNodeUids = uiExtensionNodes && uiExtensionNodes.uids.length > 0 ? uiExtensionNodes.uids : undefined;
821
887
  // Unified picker: ask once over HS-X capabilities + HubSpot UI nodes.
@@ -845,6 +911,10 @@ async function devCommandCore({ argv, root, json, }) {
845
911
  message: `Which components should run in dev mode? (portal ${portalId})`,
846
912
  initial,
847
913
  minSelected: 1,
914
+ validate: (selection) => {
915
+ const check = resolveHubSpotDevSessionApp(uiExtensionNodes?.candidates ?? [], selection.filter((v) => v.startsWith(UI_PREFIX)).map((v) => v.slice(UI_PREFIX.length)));
916
+ return check.ok ? undefined : check.message;
917
+ },
848
918
  options,
849
919
  });
850
920
  if (picked === undefined)
@@ -861,7 +931,7 @@ async function devCommandCore({ argv, root, json, }) {
861
931
  // We register a dev override whenever the user has a HubSpot portal in
862
932
  // play. `hs-x connect` gives us the portal id implicitly; only edge cases
863
933
  // (no connected account, no --portal flag) skip registration.
864
- const shouldRegisterOverride = Boolean(portalId);
934
+ const shouldRegisterOverride = !noHubSpot && Boolean(portalId);
865
935
  if (shouldRegisterOverride) {
866
936
  const portalIdForOverride = portalId;
867
937
  if (!portalIdForOverride) {
@@ -1317,7 +1387,7 @@ async function devCommandCore({ argv, root, json, }) {
1317
1387
  ? [{ status: 'ok', key: 'telemetry', value: telemetryOrigin }]
1318
1388
  : []),
1319
1389
  ]);
1320
- if (!portalId) {
1390
+ if (!noHubSpot && !portalId) {
1321
1391
  reporter.info('No HubSpot portal found — running local-only. Pass `--portal <id>` (or set a default with `hs auth` / `hs-x login`) to mirror a live portal.');
1322
1392
  }
1323
1393
  // The pinned status line carries the key map (incl. quit); repeating it in
@@ -2145,6 +2215,11 @@ async function maybeStartHubSpotDevSession(input) {
2145
2215
  projectDir: input.root,
2146
2216
  selectedNodeUids: input.selectedNodeUids,
2147
2217
  ...(profile ? { profile } : {}),
2218
+ onHeartbeatError: (failure) => {
2219
+ const warning = describeHeartbeatFailure(failure, portalId);
2220
+ if (warning && !input.json)
2221
+ input.reporter.warn('HSX_W_DEV_SESSION_HEARTBEAT', warning);
2222
+ },
2148
2223
  });
2149
2224
  input.reporter
2150
2225
  .step('HubSpot UI-extension session')