handoff-app 2.0.0-7 → 2.0.0-9

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 (125) hide show
  1. package/README.md +130 -14
  2. package/UPGRADE.md +20 -0
  3. package/dist/app-builder/build.js +10 -2
  4. package/dist/app-builder/client-config.js +3 -1
  5. package/dist/app-builder/watchers.js +17 -7
  6. package/dist/cache/build-cache.js +5 -1
  7. package/dist/catalog/normalize.js +3 -0
  8. package/dist/catalog/previews.js +5 -4
  9. package/dist/catalog/types.d.ts +8 -0
  10. package/dist/cli/auth/device.d.ts +6 -1
  11. package/dist/cli/auth/device.js +5 -2
  12. package/dist/cli/auth/store.d.ts +15 -6
  13. package/dist/cli/auth/store.js +72 -19
  14. package/dist/commands/login/index.d.ts +5 -0
  15. package/dist/commands/login/index.js +18 -6
  16. package/dist/commands/logout/index.d.ts +5 -0
  17. package/dist/commands/logout/index.js +54 -15
  18. package/dist/commands/types.d.ts +2 -0
  19. package/dist/commands/utils.d.ts +21 -7
  20. package/dist/commands/utils.js +11 -10
  21. package/dist/config/catalog-include.d.ts +2 -2
  22. package/dist/config/defaults.d.ts +3 -3
  23. package/dist/config/defaults.js +54 -48
  24. package/dist/config/env.d.ts +9 -0
  25. package/dist/config/env.js +43 -0
  26. package/dist/config/errors.d.ts +4 -0
  27. package/dist/config/errors.js +11 -0
  28. package/dist/config/from-env.d.ts +24 -0
  29. package/dist/config/from-env.js +7 -0
  30. package/dist/config/helpers.d.ts +2 -0
  31. package/dist/config/helpers.js +3 -1
  32. package/dist/config/index.d.ts +2 -1
  33. package/dist/config/index.js +6 -1
  34. package/dist/config/loader.d.ts +29 -11
  35. package/dist/config/loader.js +121 -46
  36. package/dist/config/resolve-env.d.ts +5 -0
  37. package/dist/config/resolve-env.js +102 -0
  38. package/dist/config/runtime.d.ts +3 -3
  39. package/dist/config/runtime.js +8 -10
  40. package/dist/config/validator.d.ts +2 -2
  41. package/dist/config/validator.js +2 -4
  42. package/dist/handlebars.d.ts +10 -2
  43. package/dist/index.d.ts +33 -4
  44. package/dist/index.js +60 -16
  45. package/dist/pipeline/index.d.ts +1 -1
  46. package/dist/pipeline/index.js +1 -1
  47. package/dist/registry/asset-storage/adapters/vercel-blob.js +1 -1
  48. package/dist/registry/asset-storage/resolve.d.ts +2 -2
  49. package/dist/registry/asset-storage/resolve.js +4 -1
  50. package/dist/registry/checkout/index.js +4 -4
  51. package/dist/registry/connection.d.ts +28 -24
  52. package/dist/registry/connection.js +53 -67
  53. package/dist/registry/db/driver.d.ts +4 -11
  54. package/dist/registry/db/driver.js +4 -16
  55. package/dist/registry/db/index.d.ts +1 -1
  56. package/dist/registry/db/index.js +1 -2
  57. package/dist/registry/publish/index.js +4 -4
  58. package/dist/store/filesystem.d.ts +2 -2
  59. package/dist/transformers/plugins/ssr-render.js +40 -15
  60. package/dist/transformers/preview/component/css.d.ts +1 -0
  61. package/dist/transformers/preview/component/css.js +85 -47
  62. package/dist/transformers/preview/pattern/html.js +3 -1
  63. package/dist/transformers/preview/types.d.ts +13 -0
  64. package/dist/transformers/utils/build.d.ts +19 -0
  65. package/dist/transformers/utils/build.js +83 -1
  66. package/dist/transformers/utils/module.js +2 -1
  67. package/dist/types/config.d.ts +54 -29
  68. package/docs/api.md +31 -0
  69. package/docs/cli.md +2 -2
  70. package/package.json +1 -1
  71. package/src/app/components/Component/PageSliceResolver.tsx +1 -0
  72. package/src/app/components/Component/Preview.tsx +380 -189
  73. package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +4 -0
  74. package/src/app/pages/account/tokens.tsx +3 -1
  75. package/src/app-builder/build.ts +10 -2
  76. package/src/app-builder/client-config.ts +5 -3
  77. package/src/app-builder/watchers.ts +18 -8
  78. package/src/cache/build-cache.ts +5 -1
  79. package/src/catalog/README.md +3 -0
  80. package/src/catalog/normalize.ts +2 -0
  81. package/src/catalog/previews.ts +6 -3
  82. package/src/catalog/types.ts +8 -0
  83. package/src/cli/auth/device.ts +8 -3
  84. package/src/cli/auth/store.ts +75 -18
  85. package/src/commands/login/index.ts +20 -6
  86. package/src/commands/logout/index.ts +74 -20
  87. package/src/commands/types.ts +2 -0
  88. package/src/commands/utils.ts +14 -5
  89. package/src/config/README.md +61 -1
  90. package/src/config/catalog-include.ts +2 -2
  91. package/src/config/defaults.ts +24 -14
  92. package/src/config/env.ts +41 -0
  93. package/src/config/errors.ts +7 -0
  94. package/src/config/from-env.ts +26 -0
  95. package/src/config/helpers.ts +4 -2
  96. package/src/config/index.ts +4 -1
  97. package/src/config/loader.ts +158 -50
  98. package/src/config/resolve-env.ts +94 -0
  99. package/src/config/runtime.ts +12 -7
  100. package/src/config/validator.ts +4 -6
  101. package/src/handlebars.ts +8 -2
  102. package/src/index.d.ts +2 -2
  103. package/src/index.ts +85 -8
  104. package/src/pipeline/index.ts +1 -1
  105. package/src/registry/asset-storage/adapters/vercel-blob.ts +1 -1
  106. package/src/registry/asset-storage/resolve.ts +6 -3
  107. package/src/registry/checkout/index.ts +5 -5
  108. package/src/registry/connection.ts +72 -71
  109. package/src/registry/db/driver.ts +6 -17
  110. package/src/registry/db/index.ts +0 -1
  111. package/src/registry/publish/index.ts +5 -5
  112. package/src/store/filesystem.ts +2 -2
  113. package/src/transformers/plugins/ssr-render.ts +53 -16
  114. package/src/transformers/preview/component/css.ts +93 -50
  115. package/src/transformers/preview/pattern/html.ts +3 -1
  116. package/src/transformers/preview/types.ts +14 -0
  117. package/src/transformers/utils/build.ts +80 -0
  118. package/src/transformers/utils/module.ts +4 -2
  119. package/src/types/config.ts +51 -29
  120. package/templates/default/env.tpl +10 -0
  121. package/templates/default/gitignore.tpl +7 -0
  122. package/templates/default/handoff.config.js.tpl +21 -172
  123. package/templates/default/handoff.config.ts.tpl +21 -156
  124. package/templates/default/handoff.config.with-examples.js.tpl +20 -171
  125. package/templates/default/handoff.config.with-examples.ts.tpl +20 -155
package/dist/index.js CHANGED
@@ -35,18 +35,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.CoreTypes = exports.CoreTransformerUtils = exports.CoreTransformers = exports.defineAssetStorage = exports.defineConfig = void 0;
39
- require("dotenv/config");
38
+ exports.CoreTypes = exports.CoreTransformerUtils = exports.CoreTransformers = exports.defineAssetStorage = exports.fromEnv = exports.defineConfig = void 0;
39
+ require("./config/env");
40
40
  const fs_extra_1 = __importDefault(require("fs-extra"));
41
41
  const handoff_core_1 = require("handoff-core");
42
42
  const path_1 = __importDefault(require("path"));
43
43
  const app_builder_1 = __importStar(require("./app-builder"));
44
44
  const eject_1 = require("./cli/eject");
45
45
  const make_1 = require("./cli/make");
46
+ const store_1 = require("./cli/auth/store");
46
47
  const config_1 = require("./config");
47
48
  const pipeline_1 = __importStar(require("./pipeline"));
48
49
  const content_kinds_1 = require("./registry/content-kinds");
49
- const store_1 = require("./store");
50
+ const store_2 = require("./store");
50
51
  const builder_1 = __importStar(require("./transformers/preview/component/builder"));
51
52
  const logger_1 = require("./utils/logger");
52
53
  const path_2 = require("./utils/path");
@@ -114,7 +115,7 @@ class Handoff {
114
115
  */
115
116
  get store() {
116
117
  if (!this._store) {
117
- this._store = (0, store_1.createFilesystemStore)(this);
118
+ this._store = (0, store_2.createFilesystemStore)(this);
118
119
  }
119
120
  return this._store;
120
121
  }
@@ -134,24 +135,52 @@ class Handoff {
134
135
  this.construct(options);
135
136
  }
136
137
  construct(options) {
137
- var _a, _b, _c, _d;
138
- this.config = null;
139
- this.debug = (_a = options.debug) !== null && _a !== void 0 ? _a : false;
140
- this.force = (_b = options.force) !== null && _b !== void 0 ? _b : false;
141
- this.dryRun = (_c = options.dryRun) !== null && _c !== void 0 ? _c : false;
142
- this.skipBuild = (_d = options.skipBuild) !== null && _d !== void 0 ? _d : false;
138
+ var _a, _b, _c, _d, _e;
139
+ // Ahead of `Logger.init` so a profile env file can set `HANDOFF_LOG_LEVEL` and `HANDOFF_LOG_SCOPES`.
140
+ (0, config_1.loadProfileEnv)((_a = (0, config_1.resolveProfileSelection)(options.profile)) === null || _a === void 0 ? void 0 : _a.name);
141
+ this.debug = (_b = options.debug) !== null && _b !== void 0 ? _b : false;
142
+ this.force = (_c = options.force) !== null && _c !== void 0 ? _c : false;
143
+ this.dryRun = (_d = options.dryRun) !== null && _d !== void 0 ? _d : false;
144
+ this.skipBuild = (_e = options.skipBuild) !== null && _e !== void 0 ? _e : false;
143
145
  logger_1.Logger.init({ debug: this.debug });
144
146
  this.init(options.config);
145
147
  }
148
+ /**
149
+ * What a selected profile may resolve to besides a config file, and how to explain a name that
150
+ * resolves to nothing. A command that does not accept a login still names one it found, so the
151
+ * difference between the two kinds of profile is visible rather than puzzling.
152
+ */
153
+ profilesWithoutConfig() {
154
+ var _a;
155
+ const selected = (_a = (0, config_1.resolveProfileSelection)(this._options.profile)) === null || _a === void 0 ? void 0 : _a.name;
156
+ if (!selected) {
157
+ return {};
158
+ }
159
+ const accepts = this._options.profileWithoutConfig;
160
+ const names = (0, store_1.loginProfileNames)(this.workingPath);
161
+ if (!accepts) {
162
+ return names.includes(selected)
163
+ ? {
164
+ profileNotFoundHint: 'A saved registry login uses that name, but only login, logout, publish, and checkout accept a profile without a config file.',
165
+ }
166
+ : {};
167
+ }
168
+ return {
169
+ knownProfiles: accepts === 'any' ? [...names, selected] : names,
170
+ profileNotFoundHint: 'No saved registry login uses that name either. Run `handoff-app login --profile <name> --url <registry-url>` to create one.',
171
+ };
172
+ }
146
173
  init(configOverride) {
147
174
  var _a, _b, _c;
148
- const configResult = (0, config_1.initConfigWithMetadata)(configOverride !== null && configOverride !== void 0 ? configOverride : {}, {
149
- workingPath: this.workingPath,
150
- configPath: this._options.configPath,
151
- });
175
+ // Resolved before anything is assigned: a reload that throws - a deleted profile file, a
176
+ // broken config - then leaves the last good state in place for the watchers holding this
177
+ // instance.
178
+ const configResult = (0, config_1.initConfigWithMetadata)(configOverride !== null && configOverride !== void 0 ? configOverride : {}, Object.assign({ workingPath: this.workingPath, configPath: this._options.configPath, profile: this._options.profile }, this.profilesWithoutConfig()));
152
179
  const config = configResult.config;
153
180
  this.config = config;
154
181
  this._mainConfigFilePath = configResult.configPath;
182
+ this._profileConfigFilePath = configResult.profileConfigPath;
183
+ this._profile = configResult.profile;
155
184
  this.exportsDirectory = (_a = config.exportsOutputDirectory) !== null && _a !== void 0 ? _a : this.exportsDirectory;
156
185
  this.sitesDirectory = (_b = config.sitesOutputDirectory) !== null && _b !== void 0 ? _b : this.exportsDirectory;
157
186
  [this.runtimeConfig, this._configFilePaths, this._configFileIndex] = (0, config_1.initRuntimeConfig)(this);
@@ -569,8 +598,8 @@ class Handoff {
569
598
  * @returns {string[]} Array of absolute paths to config files
570
599
  */
571
600
  getConfigFilePaths() {
572
- const combined = this._mainConfigFilePath ? [this._mainConfigFilePath, ...this._configFilePaths] : this._configFilePaths;
573
- return Array.from(new Set(combined));
601
+ const mainPaths = [this._mainConfigFilePath, this._profileConfigFilePath].filter(Boolean);
602
+ return Array.from(new Set([...mainPaths, ...this._configFilePaths]));
574
603
  }
575
604
  /**
576
605
  * Gets the selected main config file path if one exists.
@@ -578,6 +607,19 @@ class Handoff {
578
607
  getMainConfigFilePath() {
579
608
  return this._mainConfigFilePath;
580
609
  }
610
+ getProfile() {
611
+ return this._profile;
612
+ }
613
+ /**
614
+ * True for the base config file and for the profile file merged onto it. Both carry project-wide
615
+ * settings, so a change to either one invalidates every entity rather than a single declaration.
616
+ */
617
+ isMainConfigFile(filePath) {
618
+ const normalized = (0, path_2.normalizePathForCompare)(filePath);
619
+ return [this._mainConfigFilePath, this._profileConfigFilePath]
620
+ .filter(Boolean)
621
+ .some((mainPath) => (0, path_2.normalizePathForCompare)(mainPath) === normalized);
622
+ }
581
623
  getConfigFileEntry(configPath) {
582
624
  return this._configFileIndex.get((0, path_2.normalizePathForCompare)(configPath));
583
625
  }
@@ -606,6 +648,8 @@ class Handoff {
606
648
  }
607
649
  var config_2 = require("./config");
608
650
  Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return config_2.defineConfig; } });
651
+ var from_env_1 = require("./config/from-env");
652
+ Object.defineProperty(exports, "fromEnv", { enumerable: true, get: function () { return from_env_1.fromEnv; } });
609
653
  var define_1 = require("./registry/asset-storage/define");
610
654
  Object.defineProperty(exports, "defineAssetStorage", { enumerable: true, get: function () { return define_1.defineAssetStorage; } });
611
655
  // Export transformers and types from handoff-core
@@ -1,4 +1,4 @@
1
- import 'dotenv/config';
1
+ import '../config/env';
2
2
  import Handoff from '..';
3
3
  export { readPrevJSONFile, zip, zipAssets } from './archive';
4
4
  export { buildComponents } from './components';
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.buildPatterns = exports.buildComponents = exports.zipAssets = exports.zip = exports.readPrevJSONFile = void 0;
16
- require("dotenv/config");
16
+ require("../config/env");
17
17
  const app_builder_1 = __importDefault(require("../app-builder"));
18
18
  const logger_1 = require("../utils/logger");
19
19
  const figma_1 = require("./figma");
@@ -52,7 +52,7 @@ const createVercelBlobStorage = (params) => {
52
52
  const token = (_a = process.env[params.tokenEnv]) === null || _a === void 0 ? void 0 : _a.trim();
53
53
  if (!token) {
54
54
  throw new Error(`Vercel Blob storage is configured but the token env var "${params.tokenEnv}" is not set. ` +
55
- 'Set it to a Blob read/write token, or change runtime.registry.assetStorage.tokenEnv.');
55
+ 'Set it to a Blob read/write token, or change runtime.registry.assetStorage.token.');
56
56
  }
57
57
  return token;
58
58
  };
@@ -6,7 +6,7 @@
6
6
  * {@link import('../db/driver')}: only provider selection, module location, non-secret options, and
7
7
  * env-var *names* are ever persisted; secret *values* are read from `process.env` at request time.
8
8
  */
9
- import type { Config } from '../../types/config';
9
+ import type { ResolvedConfig } from '../../types/config';
10
10
  import type { AssetStorageProvider } from '../db/schema';
11
11
  /** Which adapter implementation is active. */
12
12
  export type AssetStorageAdapterKind = 'database' | 'vercel-blob' | 'custom';
@@ -42,4 +42,4 @@ export interface ResolvedAssetStorage {
42
42
  /** Resolve raw asset storage settings into a fully-defaulted shape. */
43
43
  export declare const resolveAssetStorageSettings: (settings: AssetStorageSettings | null | undefined) => ResolvedAssetStorage;
44
44
  /** Resolve the active asset storage settings from a loaded config (CLI/build side). */
45
- export declare const resolveAssetStorageFromConfig: (config: Config | null | undefined) => ResolvedAssetStorage;
45
+ export declare const resolveAssetStorageFromConfig: (config: ResolvedConfig | null | undefined) => ResolvedAssetStorage;
@@ -32,5 +32,8 @@ const resolveAssetStorageSettings = (settings) => {
32
32
  };
33
33
  exports.resolveAssetStorageSettings = resolveAssetStorageSettings;
34
34
  /** Resolve the active asset storage settings from a loaded config (CLI/build side). */
35
- const resolveAssetStorageFromConfig = (config) => { var _a, _b; return (0, exports.resolveAssetStorageSettings)((_b = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registry) === null || _b === void 0 ? void 0 : _b.assetStorage); };
35
+ const resolveAssetStorageFromConfig = (config) => {
36
+ var _a, _b, _c, _d, _e, _f;
37
+ return (0, exports.resolveAssetStorageSettings)(Object.assign(Object.assign({}, (_b = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registry) === null || _b === void 0 ? void 0 : _b.assetStorage), { tokenEnv: (_f = (_e = (_d = (_c = config === null || config === void 0 ? void 0 : config.runtime) === null || _c === void 0 ? void 0 : _c.registry) === null || _d === void 0 ? void 0 : _d.assetStorage) === null || _e === void 0 ? void 0 : _e.token) === null || _f === void 0 ? void 0 : _f.$env }));
38
+ };
36
39
  exports.resolveAssetStorageFromConfig = resolveAssetStorageFromConfig;
@@ -106,14 +106,14 @@ const resolveConnectionOrThrow = (handoff) => __awaiter(void 0, void 0, void 0,
106
106
  throw new CheckoutError(`checkout is only available from a connected workspace (runtime.mode: "workspace"); this project is "${mode}". ` +
107
107
  'A registry host stores and serves entities; it has no local workspace to check them out into.');
108
108
  }
109
- const connection = yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath);
109
+ const connection = yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath, handoff.getProfile());
110
110
  if (!connection.url) {
111
- throw new CheckoutError(`No registry is configured. Run \`handoff-app login --url <registry-url>\`, set runtime.registryConnection.url, ` +
111
+ throw new CheckoutError(`No registry is configured. Run \`${(0, connection_1.loginCommandHint)(connection)}\`, set runtime.registryConnection.url, ` +
112
112
  `or set the "${connection.urlEnv}" environment variable to the base URL of the registry to checkout from.`);
113
113
  }
114
114
  if (!connection.accessToken) {
115
- throw new CheckoutError(`No registry access token is configured. Run \`handoff-app login --url ${connection.url}\`, or set the ` +
116
- `"${connection.accessTokenEnv}" environment variable to a user-issued token for CI.`);
115
+ throw new CheckoutError(`No registry access token is configured. Run \`${(0, connection_1.loginCommandHint)(connection)}\`, or set the ` +
116
+ `"${connection.accessTokenEnv}" environment variable to a user-issued token for CI.${(0, connection_1.unusedLoginNote)(connection)}`);
117
117
  }
118
118
  return connection;
119
119
  });
@@ -1,20 +1,17 @@
1
1
  /**
2
- * Connected-workspace registry connection resolution.
3
- *
4
- * A connected workspace is `runtime.mode: 'workspace'` plus a `runtime.registryConnection` block
5
- * pointing at a remote registry — it is not a third runtime mode. The connection is resolved from
6
- * config + environment: the registry URL may be given inline or via an env-var *name*, and the
7
- * access token is always read from an env-var name (never persisted as a value). This is the single
8
- * source the publish (and later checkout) client reads its target + credentials from.
9
- *
10
- * The older `HANDOFF_CLOUD_*` names still work as deprecated fallbacks; see
11
- * {@link DEPRECATED_ENV_ALIASES}.
2
+ * Registry connection values resolve at config load. Saved device login credentials can fill
3
+ * missing values for CLI operations; neither source is persisted in the app's client config.
12
4
  */
13
- import type { Config } from '../types/config';
5
+ import type { ResolvedConfig } from '../types/config';
14
6
  /** Default env-var name holding the remote registry base URL. */
15
7
  export declare const DEFAULT_REGISTRY_URL_ENV = "HANDOFF_REGISTRY_URL";
16
8
  /** Default env-var name holding the connected-workspace access token. */
17
9
  export declare const DEFAULT_REGISTRY_ACCESS_TOKEN_ENV = "HANDOFF_REGISTRY_ACCESS_TOKEN";
10
+ /** A saved login and the profile it is stored under, which is not always the selected profile. */
11
+ interface SavedLogin {
12
+ profile: string;
13
+ url: string;
14
+ }
18
15
  /** Fully resolved connected-workspace registry connection. */
19
16
  export interface ResolvedRegistryConnection {
20
17
  /** Resolved registry base URL (inline config value wins, else the named env var), or `''`. */
@@ -25,25 +22,32 @@ export interface ResolvedRegistryConnection {
25
22
  urlEnv: string;
26
23
  /** Name of the env var the access token is read from. */
27
24
  accessTokenEnv: string;
25
+ /** Profile the saved login was looked up under. */
26
+ loginProfile: string;
27
+ /** Set when a saved login was skipped because it was issued for another registry. */
28
+ unmatchedLogin?: SavedLogin;
28
29
  }
29
- /**
30
- * Resolve the connected-workspace registry connection from config + environment. Never throws —
31
- * Callers inspect the resolved URL and access token to surface specific configuration errors.
32
- *
33
- * `urlEnv` and `accessTokenEnv` always report the canonical name, even when an alias supplied the
34
- * value, so errors built from them point at the variable to adopt.
35
- */
36
- export declare const resolveRegistryConnection: (config: Config | null | undefined) => ResolvedRegistryConnection;
30
+ /** Read values resolved at config load, retaining variable names for CLI diagnostics. */
31
+ export declare const resolveRegistryConnection: (config: ResolvedConfig | null | undefined) => ResolvedRegistryConnection;
37
32
  /**
38
33
  * Resolve the same connection for an authenticated CLI operation, letting a saved device login fill
39
34
  * in what the environment does not provide. A valid login supplies both the registry URL and the
40
- * access token, so `handoff-app login` alone is enough to publish and checkout. Environment values
41
- * keep precedence for deterministic CI: `HANDOFF_REGISTRY_URL` (or inline config) wins over the
42
- * saved URL, and `HANDOFF_REGISTRY_ACCESS_TOKEN` wins over the saved token. The saved token only
43
- * applies to the URL it was issued for.
35
+ * access token, so `handoff-app login` alone is enough to publish and checkout. The login is the one
36
+ * saved under the selected profile, which is how one workspace addresses several registries.
37
+ * Environment values keep precedence for deterministic CI: `HANDOFF_REGISTRY_URL` (or inline config)
38
+ * wins over the saved URL, and `HANDOFF_REGISTRY_ACCESS_TOKEN` wins over the saved token. The saved
39
+ * token only applies to the URL it was issued for.
44
40
  *
45
41
  * A configured `HANDOFF_SYNC_SECRET` is the last resort. Being deployment-wide rather than personal,
46
42
  * it fills in only where there is no explicit token and no login (CI, in practice), instead of quietly
47
43
  * speaking for a developer who is signed in.
48
44
  */
49
- export declare const resolveAuthenticatedRegistryConnection: (config: Config | null | undefined, workingPath?: string) => Promise<ResolvedRegistryConnection>;
45
+ export declare const resolveAuthenticatedRegistryConnection: (config: ResolvedConfig | null | undefined, workingPath?: string, profile?: string) => Promise<ResolvedRegistryConnection>;
46
+ /** The `handoff-app login` call that would authorize this connection, naming the selected profile. */
47
+ export declare const loginCommandHint: (connection: ResolvedRegistryConnection) => string;
48
+ /**
49
+ * Names a saved login that was skipped because it belongs to another registry. Without it, a run
50
+ * that resolves its URL from the environment reports a missing token while a login is in place.
51
+ */
52
+ export declare const unusedLoginNote: (connection: ResolvedRegistryConnection) => string;
53
+ export {};
@@ -1,15 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
- * Connected-workspace registry connection resolution.
4
- *
5
- * A connected workspace is `runtime.mode: 'workspace'` plus a `runtime.registryConnection` block
6
- * pointing at a remote registry — it is not a third runtime mode. The connection is resolved from
7
- * config + environment: the registry URL may be given inline or via an env-var *name*, and the
8
- * access token is always read from an env-var name (never persisted as a value). This is the single
9
- * source the publish (and later checkout) client reads its target + credentials from.
10
- *
11
- * The older `HANDOFF_CLOUD_*` names still work as deprecated fallbacks; see
12
- * {@link DEPRECATED_ENV_ALIASES}.
3
+ * Registry connection values resolve at config load. Saved device login credentials can fill
4
+ * missing values for CLI operations; neither source is persisted in the app's client config.
13
5
  */
14
6
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
7
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -21,89 +13,83 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
21
13
  });
22
14
  };
23
15
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.resolveAuthenticatedRegistryConnection = exports.resolveRegistryConnection = exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV = exports.DEFAULT_REGISTRY_URL_ENV = void 0;
16
+ exports.unusedLoginNote = exports.loginCommandHint = exports.resolveAuthenticatedRegistryConnection = exports.resolveRegistryConnection = exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV = exports.DEFAULT_REGISTRY_URL_ENV = void 0;
17
+ const resolve_env_1 = require("../config/resolve-env");
25
18
  const store_1 = require("../cli/auth/store");
26
19
  // Imported straight from the module, not the `auth` barrel, to keep the database schema off the CLI
27
20
  // startup path; this file is resolved for every command.
28
21
  const sync_secret_1 = require("./auth/sync-secret");
29
- const logger_1 = require("../utils/logger");
30
22
  /** Default env-var name holding the remote registry base URL. */
31
23
  exports.DEFAULT_REGISTRY_URL_ENV = 'HANDOFF_REGISTRY_URL';
32
24
  /** Default env-var name holding the connected-workspace access token. */
33
25
  exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV = 'HANDOFF_REGISTRY_ACCESS_TOKEN';
34
- /**
35
- * Old env-var names we still read, keyed by the name that replaced them. Keeps workspaces set up
36
- * from the older docs working instead of silently reporting no registry.
37
- */
38
- const DEPRECATED_ENV_ALIASES = {
39
- [exports.DEFAULT_REGISTRY_URL_ENV]: 'HANDOFF_CLOUD_URL',
40
- [exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV]: 'HANDOFF_CLOUD_TOKEN',
41
- };
42
- /** Aliases we've already warned about, so each one warns once per run and not once per lookup. */
43
- const warnedAliases = new Set();
44
- /**
45
- * Value of the named env var, falling back to its deprecated alias. Only the default names have
46
- * aliases: a custom `urlEnv`/`accessTokenEnv` is a deliberate choice, so a stray `HANDOFF_CLOUD_*`
47
- * must not stand in for it. The warning goes through {@link Logger.warn}, so
48
- * `HANDOFF_LOG_LEVEL=error` silences it in CI.
49
- */
50
- const readConnectionEnv = (name) => {
51
- var _a, _b;
52
- const configured = (_a = process.env[name]) === null || _a === void 0 ? void 0 : _a.trim();
53
- if (configured)
54
- return configured;
55
- const alias = DEPRECATED_ENV_ALIASES[name];
56
- const inherited = alias ? (_b = process.env[alias]) === null || _b === void 0 ? void 0 : _b.trim() : '';
57
- if (!inherited)
58
- return '';
59
- if (!warnedAliases.has(alias)) {
60
- warnedAliases.add(alias);
61
- logger_1.Logger.warn(`${alias} is deprecated and will be removed in a future release; set ${name} instead.`);
62
- }
63
- return inherited;
64
- };
65
- /**
66
- * Resolve the connected-workspace registry connection from config + environment. Never throws —
67
- * Callers inspect the resolved URL and access token to surface specific configuration errors.
68
- *
69
- * `urlEnv` and `accessTokenEnv` always report the canonical name, even when an alias supplied the
70
- * value, so errors built from them point at the variable to adopt.
71
- */
26
+ /** Read values resolved at config load, retaining variable names for CLI diagnostics. */
72
27
  const resolveRegistryConnection = (config) => {
73
- var _a, _b, _c, _d;
28
+ var _a, _b, _c;
74
29
  const connection = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registryConnection;
75
- const urlEnv = ((_b = connection === null || connection === void 0 ? void 0 : connection.urlEnv) === null || _b === void 0 ? void 0 : _b.trim()) || exports.DEFAULT_REGISTRY_URL_ENV;
76
- const accessTokenEnv = ((_c = connection === null || connection === void 0 ? void 0 : connection.accessTokenEnv) === null || _c === void 0 ? void 0 : _c.trim()) || exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV;
77
- const inlineUrl = (_d = connection === null || connection === void 0 ? void 0 : connection.url) === null || _d === void 0 ? void 0 : _d.trim();
78
- const url = inlineUrl || readConnectionEnv(urlEnv);
79
- const accessToken = readConnectionEnv(accessTokenEnv);
30
+ const urlEnv = (0, resolve_env_1.configEnvName)(config, 'runtime.registryConnection.url') || exports.DEFAULT_REGISTRY_URL_ENV;
31
+ const accessTokenEnv = (0, resolve_env_1.configEnvName)(config, 'runtime.registryConnection.accessToken') || exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV;
32
+ const url = ((_b = connection === null || connection === void 0 ? void 0 : connection.url) === null || _b === void 0 ? void 0 : _b.trim()) || '';
33
+ const accessToken = ((_c = connection === null || connection === void 0 ? void 0 : connection.accessToken) === null || _c === void 0 ? void 0 : _c.trim()) || '';
80
34
  return {
81
35
  url,
82
36
  accessToken,
83
37
  urlEnv,
84
38
  accessTokenEnv,
39
+ loginProfile: store_1.DEFAULT_LOGIN_PROFILE,
85
40
  };
86
41
  };
87
42
  exports.resolveRegistryConnection = resolveRegistryConnection;
43
+ /**
44
+ * The valid login to use for a profile. A profile without its own login falls back to the default
45
+ * login, so a workspace that names profiles for other reasons keeps working after one plain
46
+ * `handoff-app login`. The fallback is safe because a token is only ever used for the registry URL
47
+ * it was issued for.
48
+ */
49
+ const readProfileLogin = (workingPath, profile) => __awaiter(void 0, void 0, void 0, function* () {
50
+ const own = yield (0, store_1.readCliAuth)(workingPath, profile);
51
+ if ((0, store_1.cliAuthTokenIsValid)(own))
52
+ return { auth: own, profile };
53
+ const fallback = profile === store_1.DEFAULT_LOGIN_PROFILE ? null : yield (0, store_1.readCliAuth)(workingPath);
54
+ return (0, store_1.cliAuthTokenIsValid)(fallback) ? { auth: fallback, profile: store_1.DEFAULT_LOGIN_PROFILE } : null;
55
+ });
88
56
  /**
89
57
  * Resolve the same connection for an authenticated CLI operation, letting a saved device login fill
90
58
  * in what the environment does not provide. A valid login supplies both the registry URL and the
91
- * access token, so `handoff-app login` alone is enough to publish and checkout. Environment values
92
- * keep precedence for deterministic CI: `HANDOFF_REGISTRY_URL` (or inline config) wins over the
93
- * saved URL, and `HANDOFF_REGISTRY_ACCESS_TOKEN` wins over the saved token. The saved token only
94
- * applies to the URL it was issued for.
59
+ * access token, so `handoff-app login` alone is enough to publish and checkout. The login is the one
60
+ * saved under the selected profile, which is how one workspace addresses several registries.
61
+ * Environment values keep precedence for deterministic CI: `HANDOFF_REGISTRY_URL` (or inline config)
62
+ * wins over the saved URL, and `HANDOFF_REGISTRY_ACCESS_TOKEN` wins over the saved token. The saved
63
+ * token only applies to the URL it was issued for.
95
64
  *
96
65
  * A configured `HANDOFF_SYNC_SECRET` is the last resort. Being deployment-wide rather than personal,
97
66
  * it fills in only where there is no explicit token and no login (CI, in practice), instead of quietly
98
67
  * speaking for a developer who is signed in.
99
68
  */
100
- const resolveAuthenticatedRegistryConnection = (config_1, ...args_1) => __awaiter(void 0, [config_1, ...args_1], void 0, function* (config, workingPath = process.cwd()) {
69
+ const resolveAuthenticatedRegistryConnection = (config_1, ...args_1) => __awaiter(void 0, [config_1, ...args_1], void 0, function* (config, workingPath = process.cwd(), profile) {
101
70
  const connection = (0, exports.resolveRegistryConnection)(config);
102
- const auth = yield (0, store_1.readCliAuth)(workingPath);
103
- const login = (0, store_1.cliAuthTokenIsValid)(auth) ? auth : null;
104
- const url = connection.url || (login === null || login === void 0 ? void 0 : login.remoteUrl) || '';
105
- const savedToken = login && (0, store_1.cliAuthMatchesRegistry)(login, url) ? login.accessToken : '';
106
- const accessToken = connection.accessToken || savedToken || (0, sync_secret_1.resolveRegistrySyncSecret)();
107
- return Object.assign(Object.assign({}, connection), { url, accessToken });
71
+ const loginProfile = (profile === null || profile === void 0 ? void 0 : profile.trim()) || store_1.DEFAULT_LOGIN_PROFILE;
72
+ const login = yield readProfileLogin(workingPath, loginProfile);
73
+ const url = connection.url || (login === null || login === void 0 ? void 0 : login.auth.remoteUrl) || '';
74
+ const matches = Boolean(login) && (0, store_1.cliAuthMatchesRegistry)(login.auth, url);
75
+ const accessToken = connection.accessToken || (matches ? login.auth.accessToken : '') || (0, sync_secret_1.resolveRegistrySyncSecret)();
76
+ return Object.assign(Object.assign({}, connection), { url,
77
+ accessToken,
78
+ loginProfile, unmatchedLogin: login && !matches ? { profile: login.profile, url: login.auth.remoteUrl } : undefined });
108
79
  });
109
80
  exports.resolveAuthenticatedRegistryConnection = resolveAuthenticatedRegistryConnection;
81
+ /** The `handoff-app login` call that would authorize this connection, naming the selected profile. */
82
+ const loginCommandHint = (connection) => [
83
+ 'handoff-app login',
84
+ connection.loginProfile === store_1.DEFAULT_LOGIN_PROFILE ? '' : ` --profile ${connection.loginProfile}`,
85
+ ` --url ${connection.url || '<registry-url>'}`,
86
+ ].join('');
87
+ exports.loginCommandHint = loginCommandHint;
88
+ /**
89
+ * Names a saved login that was skipped because it belongs to another registry. Without it, a run
90
+ * that resolves its URL from the environment reports a missing token while a login is in place.
91
+ */
92
+ const unusedLoginNote = (connection) => connection.unmatchedLogin
93
+ ? ` The login saved for profile "${connection.unmatchedLogin.profile}" is for ${connection.unmatchedLogin.url}, not ${connection.url}.`
94
+ : '';
95
+ exports.unusedLoginNote = unusedLoginNote;
@@ -9,7 +9,7 @@
9
9
  * credentials are referenced by environment-variable name only and resolved to a value at runtime;
10
10
  * the value is never stored in config.
11
11
  */
12
- import type { Config } from '../../types/config';
12
+ import type { ResolvedConfig } from '../../types/config';
13
13
  /** Built-in registry database connection driver. Both ship with the package and target Postgres. */
14
14
  export type RegistryDatabaseDriver = 'pg' | 'neon';
15
15
  /** Default env-var name holding the database connection string. */
@@ -28,19 +28,12 @@ export interface ResolvedRegistryDatabase {
28
28
  connectionString: string;
29
29
  }
30
30
  /** Resolve the env-var name holding the database URL (defaults to `DATABASE_URL`). */
31
- export declare const resolveDatabaseUrlEnv: (config: Config | null | undefined) => string;
32
- /**
33
- * Resolve the legacy fixed-token env-var name for configuration compatibility.
34
- *
35
- * @deprecated The returned env var is intentionally ignored by registry authorization. Use a
36
- * user-issued access token or `handoff-app login`.
37
- */
38
- export declare const resolveApiTokenEnv: (config: Config | null | undefined) => string;
31
+ export declare const resolveDatabaseUrlEnv: (config: ResolvedConfig | null | undefined) => string;
39
32
  /** Resolve the configured registry database driver (defaults to `pg`). */
40
- export declare const resolveRegistryDriver: (config: Config | null | undefined) => RegistryDatabaseDriver;
33
+ export declare const resolveRegistryDriver: (config: ResolvedConfig | null | undefined) => RegistryDatabaseDriver;
41
34
  /**
42
35
  * Resolve the registry database connection from config + environment. Throws an actionable error
43
36
  * when the configured env var is unset so the CLI can surface a clear "missing database URL"
44
37
  * message instead of failing deep inside the driver.
45
38
  */
46
- export declare const resolveRegistryDatabase: (config: Config | null | undefined) => ResolvedRegistryDatabase;
39
+ export declare const resolveRegistryDatabase: (config: ResolvedConfig | null | undefined) => ResolvedRegistryDatabase;
@@ -11,7 +11,7 @@
11
11
  * the value is never stored in config.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.resolveRegistryDatabase = exports.resolveRegistryDriver = exports.resolveApiTokenEnv = exports.resolveDatabaseUrlEnv = exports.DEFAULT_REGISTRY_DRIVER = exports.DEFAULT_REGISTRY_API_TOKEN_ENV = exports.DEFAULT_DATABASE_URL_ENV = void 0;
14
+ exports.resolveRegistryDatabase = exports.resolveRegistryDriver = exports.resolveDatabaseUrlEnv = exports.DEFAULT_REGISTRY_DRIVER = exports.DEFAULT_REGISTRY_API_TOKEN_ENV = exports.DEFAULT_DATABASE_URL_ENV = void 0;
15
15
  /** Default env-var name holding the database connection string. */
16
16
  exports.DEFAULT_DATABASE_URL_ENV = 'DATABASE_URL';
17
17
  /** @deprecated Fixed registry server secrets are no longer authorized. */
@@ -20,23 +20,11 @@ exports.DEFAULT_REGISTRY_API_TOKEN_ENV = 'HANDOFF_REGISTRY_API_TOKEN';
20
20
  exports.DEFAULT_REGISTRY_DRIVER = 'pg';
21
21
  /** Resolve the env-var name holding the database URL (defaults to `DATABASE_URL`). */
22
22
  const resolveDatabaseUrlEnv = (config) => {
23
- var _a, _b, _c;
24
- const configured = (_c = (_b = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registry) === null || _b === void 0 ? void 0 : _b.databaseUrlEnv) === null || _c === void 0 ? void 0 : _c.trim();
23
+ var _a, _b, _c, _d;
24
+ const configured = (_d = (_c = (_b = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registry) === null || _b === void 0 ? void 0 : _b.databaseUrl) === null || _c === void 0 ? void 0 : _c.$env) === null || _d === void 0 ? void 0 : _d.trim();
25
25
  return configured || exports.DEFAULT_DATABASE_URL_ENV;
26
26
  };
27
27
  exports.resolveDatabaseUrlEnv = resolveDatabaseUrlEnv;
28
- /**
29
- * Resolve the legacy fixed-token env-var name for configuration compatibility.
30
- *
31
- * @deprecated The returned env var is intentionally ignored by registry authorization. Use a
32
- * user-issued access token or `handoff-app login`.
33
- */
34
- const resolveApiTokenEnv = (config) => {
35
- var _a, _b, _c;
36
- const configured = (_c = (_b = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registry) === null || _b === void 0 ? void 0 : _b.apiTokenEnv) === null || _c === void 0 ? void 0 : _c.trim();
37
- return configured || exports.DEFAULT_REGISTRY_API_TOKEN_ENV;
38
- };
39
- exports.resolveApiTokenEnv = resolveApiTokenEnv;
40
28
  /** Resolve the configured registry database driver (defaults to `pg`). */
41
29
  const resolveRegistryDriver = (config) => {
42
30
  var _a, _b, _c, _d;
@@ -55,7 +43,7 @@ const resolveRegistryDatabase = (config) => {
55
43
  const connectionString = (_a = process.env[databaseUrlEnv]) === null || _a === void 0 ? void 0 : _a.trim();
56
44
  if (!connectionString) {
57
45
  throw new Error(`Registry database URL is not configured. Set the "${databaseUrlEnv}" environment variable to a ` +
58
- `PostgreSQL connection string (or change "runtime.registry.databaseUrlEnv" to point at the env var you use).`);
46
+ `PostgreSQL connection string (or change "runtime.registry.databaseUrl" to point at the env var you use).`);
59
47
  }
60
48
  return { driver, databaseUrlEnv, connectionString };
61
49
  };
@@ -1,5 +1,5 @@
1
1
  export { buildMetadata, componentFiles, components, docsArtifacts, patternFiles, patterns, registryAccessTokens, registryAuthActionTokens, registryAuthRateLimits, registryDeviceAuthorizations, registryInstallations, registrySchema, registryUsers, type BuildMetadataEntityKind, type DocsArtifactEntityKind, type RegistryAccessScope, type RegistryAuthActionPurpose, type RegistryDeviceAuthorizationStatus, type RegistryRateLimitBucket, type RegistryReviewMetadata, type RegistryUserRole, type RegistryUserStatus, } from './schema';
2
2
  export type { ArtifactBuildStatus as RegistryBuildStatus } from '../../artifacts/types';
3
- export { DEFAULT_DATABASE_URL_ENV, DEFAULT_REGISTRY_DRIVER, DEFAULT_REGISTRY_API_TOKEN_ENV, resolveApiTokenEnv, resolveDatabaseUrlEnv, resolveRegistryDriver, resolveRegistryDatabase, type RegistryDatabaseDriver, type ResolvedRegistryDatabase, } from './driver';
3
+ export { DEFAULT_DATABASE_URL_ENV, DEFAULT_REGISTRY_DRIVER, DEFAULT_REGISTRY_API_TOKEN_ENV, resolveDatabaseUrlEnv, resolveRegistryDriver, resolveRegistryDatabase, type RegistryDatabaseDriver, type ResolvedRegistryDatabase, } from './driver';
4
4
  export { createRegistryDbConnection, type CreateRegistryDbConnectionParams, type RegistryDatabase, type RegistryDbConnection, } from './client';
5
5
  export { getMigrationsFolder, runRegistryMigrations } from './migrate';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runRegistryMigrations = exports.getMigrationsFolder = exports.createRegistryDbConnection = exports.resolveRegistryDatabase = exports.resolveRegistryDriver = exports.resolveDatabaseUrlEnv = exports.resolveApiTokenEnv = exports.DEFAULT_REGISTRY_API_TOKEN_ENV = exports.DEFAULT_REGISTRY_DRIVER = exports.DEFAULT_DATABASE_URL_ENV = exports.registryUsers = exports.registrySchema = exports.registryInstallations = exports.registryDeviceAuthorizations = exports.registryAuthRateLimits = exports.registryAuthActionTokens = exports.registryAccessTokens = exports.patterns = exports.patternFiles = exports.docsArtifacts = exports.components = exports.componentFiles = exports.buildMetadata = void 0;
3
+ exports.runRegistryMigrations = exports.getMigrationsFolder = exports.createRegistryDbConnection = exports.resolveRegistryDatabase = exports.resolveRegistryDriver = exports.resolveDatabaseUrlEnv = exports.DEFAULT_REGISTRY_API_TOKEN_ENV = exports.DEFAULT_REGISTRY_DRIVER = exports.DEFAULT_DATABASE_URL_ENV = exports.registryUsers = exports.registrySchema = exports.registryInstallations = exports.registryDeviceAuthorizations = exports.registryAuthRateLimits = exports.registryAuthActionTokens = exports.registryAccessTokens = exports.patterns = exports.patternFiles = exports.docsArtifacts = exports.components = exports.componentFiles = exports.buildMetadata = void 0;
4
4
  var schema_1 = require("./schema");
5
5
  Object.defineProperty(exports, "buildMetadata", { enumerable: true, get: function () { return schema_1.buildMetadata; } });
6
6
  Object.defineProperty(exports, "componentFiles", { enumerable: true, get: function () { return schema_1.componentFiles; } });
@@ -19,7 +19,6 @@ var driver_1 = require("./driver");
19
19
  Object.defineProperty(exports, "DEFAULT_DATABASE_URL_ENV", { enumerable: true, get: function () { return driver_1.DEFAULT_DATABASE_URL_ENV; } });
20
20
  Object.defineProperty(exports, "DEFAULT_REGISTRY_DRIVER", { enumerable: true, get: function () { return driver_1.DEFAULT_REGISTRY_DRIVER; } });
21
21
  Object.defineProperty(exports, "DEFAULT_REGISTRY_API_TOKEN_ENV", { enumerable: true, get: function () { return driver_1.DEFAULT_REGISTRY_API_TOKEN_ENV; } });
22
- Object.defineProperty(exports, "resolveApiTokenEnv", { enumerable: true, get: function () { return driver_1.resolveApiTokenEnv; } });
23
22
  Object.defineProperty(exports, "resolveDatabaseUrlEnv", { enumerable: true, get: function () { return driver_1.resolveDatabaseUrlEnv; } });
24
23
  Object.defineProperty(exports, "resolveRegistryDriver", { enumerable: true, get: function () { return driver_1.resolveRegistryDriver; } });
25
24
  Object.defineProperty(exports, "resolveRegistryDatabase", { enumerable: true, get: function () { return driver_1.resolveRegistryDatabase; } });
@@ -79,14 +79,14 @@ const assertUniqueCatalogIds = (handoff) => {
79
79
  */
80
80
  const resolveConnectionOrThrow = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
81
81
  (0, exports.assertPublishableWorkspace)(handoff);
82
- const connection = yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath);
82
+ const connection = yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath, handoff.getProfile());
83
83
  if (!connection.url) {
84
- throw new PublishError(`No registry is configured. Run \`handoff-app login --url <registry-url>\`, set runtime.registryConnection.url, ` +
84
+ throw new PublishError(`No registry is configured. Run \`${(0, connection_1.loginCommandHint)(connection)}\`, set runtime.registryConnection.url, ` +
85
85
  `or set the "${connection.urlEnv}" environment variable to the base URL of the registry to publish to.`);
86
86
  }
87
87
  if (!connection.accessToken) {
88
- throw new PublishError(`No registry access token is configured. Run \`handoff-app login --url ${connection.url}\`, or set the ` +
89
- `"${connection.accessTokenEnv}" environment variable to a user-issued token for CI.`);
88
+ throw new PublishError(`No registry access token is configured. Run \`${(0, connection_1.loginCommandHint)(connection)}\`, or set the ` +
89
+ `"${connection.accessTokenEnv}" environment variable to a user-issued token for CI.${(0, connection_1.unusedLoginNote)(connection)}`);
90
90
  }
91
91
  return connection;
92
92
  });
@@ -9,7 +9,7 @@
9
9
  * without changing how anything is discovered or built.
10
10
  */
11
11
  import type { ComponentListObject, PageListObject, PatternListObject } from '../transformers/preview/types';
12
- import type { Config, RuntimeConfig } from '../types/config';
12
+ import type { ResolvedConfig, RuntimeConfig } from '../types/config';
13
13
  import type { ComponentStore, PageStore, PatternStore, SourceReference, TextFileResource, TokenArtifactResource, TokenSetRecord, TokenStore } from './types';
14
14
  /**
15
15
  * Minimal context needed to back the filesystem store. A `Handoff` instance satisfies this (it
@@ -18,7 +18,7 @@ import type { ComponentStore, PageStore, PatternStore, SourceReference, TextFile
18
18
  */
19
19
  export interface FilesystemStoreContext {
20
20
  runtimeConfig?: RuntimeConfig | null;
21
- config?: Config | null;
21
+ config?: ResolvedConfig | null;
22
22
  /** Workspace root (`Handoff.workingPath`), holding `public/` and `fonts/`. */
23
23
  workingPath?: string;
24
24
  /** Absolute path to the generated `tokens.json` (`Handoff.getTokensFilePath()`). */