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.
- package/README.md +130 -14
- package/UPGRADE.md +20 -0
- package/dist/app-builder/build.js +10 -2
- package/dist/app-builder/client-config.js +3 -1
- package/dist/app-builder/watchers.js +17 -7
- package/dist/cache/build-cache.js +5 -1
- package/dist/catalog/normalize.js +3 -0
- package/dist/catalog/previews.js +5 -4
- package/dist/catalog/types.d.ts +8 -0
- package/dist/cli/auth/device.d.ts +6 -1
- package/dist/cli/auth/device.js +5 -2
- package/dist/cli/auth/store.d.ts +15 -6
- package/dist/cli/auth/store.js +72 -19
- package/dist/commands/login/index.d.ts +5 -0
- package/dist/commands/login/index.js +18 -6
- package/dist/commands/logout/index.d.ts +5 -0
- package/dist/commands/logout/index.js +54 -15
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands/utils.d.ts +21 -7
- package/dist/commands/utils.js +11 -10
- package/dist/config/catalog-include.d.ts +2 -2
- package/dist/config/defaults.d.ts +3 -3
- package/dist/config/defaults.js +54 -48
- package/dist/config/env.d.ts +9 -0
- package/dist/config/env.js +43 -0
- package/dist/config/errors.d.ts +4 -0
- package/dist/config/errors.js +11 -0
- package/dist/config/from-env.d.ts +24 -0
- package/dist/config/from-env.js +7 -0
- package/dist/config/helpers.d.ts +2 -0
- package/dist/config/helpers.js +3 -1
- package/dist/config/index.d.ts +2 -1
- package/dist/config/index.js +6 -1
- package/dist/config/loader.d.ts +29 -11
- package/dist/config/loader.js +121 -46
- package/dist/config/resolve-env.d.ts +5 -0
- package/dist/config/resolve-env.js +102 -0
- package/dist/config/runtime.d.ts +3 -3
- package/dist/config/runtime.js +8 -10
- package/dist/config/validator.d.ts +2 -2
- package/dist/config/validator.js +2 -4
- package/dist/handlebars.d.ts +10 -2
- package/dist/index.d.ts +33 -4
- package/dist/index.js +60 -16
- package/dist/pipeline/index.d.ts +1 -1
- package/dist/pipeline/index.js +1 -1
- package/dist/registry/asset-storage/adapters/vercel-blob.js +1 -1
- package/dist/registry/asset-storage/resolve.d.ts +2 -2
- package/dist/registry/asset-storage/resolve.js +4 -1
- package/dist/registry/checkout/index.js +4 -4
- package/dist/registry/connection.d.ts +28 -24
- package/dist/registry/connection.js +53 -67
- package/dist/registry/db/driver.d.ts +4 -11
- package/dist/registry/db/driver.js +4 -16
- package/dist/registry/db/index.d.ts +1 -1
- package/dist/registry/db/index.js +1 -2
- package/dist/registry/publish/index.js +4 -4
- package/dist/store/filesystem.d.ts +2 -2
- package/dist/transformers/plugins/ssr-render.js +40 -15
- package/dist/transformers/preview/component/css.d.ts +1 -0
- package/dist/transformers/preview/component/css.js +85 -47
- package/dist/transformers/preview/pattern/html.js +3 -1
- package/dist/transformers/preview/types.d.ts +13 -0
- package/dist/transformers/utils/build.d.ts +19 -0
- package/dist/transformers/utils/build.js +83 -1
- package/dist/transformers/utils/module.js +2 -1
- package/dist/types/config.d.ts +54 -29
- package/docs/api.md +31 -0
- package/docs/cli.md +2 -2
- package/package.json +1 -1
- package/src/app/components/Component/PageSliceResolver.tsx +1 -0
- package/src/app/components/Component/Preview.tsx +380 -189
- package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +4 -0
- package/src/app/pages/account/tokens.tsx +3 -1
- package/src/app-builder/build.ts +10 -2
- package/src/app-builder/client-config.ts +5 -3
- package/src/app-builder/watchers.ts +18 -8
- package/src/cache/build-cache.ts +5 -1
- package/src/catalog/README.md +3 -0
- package/src/catalog/normalize.ts +2 -0
- package/src/catalog/previews.ts +6 -3
- package/src/catalog/types.ts +8 -0
- package/src/cli/auth/device.ts +8 -3
- package/src/cli/auth/store.ts +75 -18
- package/src/commands/login/index.ts +20 -6
- package/src/commands/logout/index.ts +74 -20
- package/src/commands/types.ts +2 -0
- package/src/commands/utils.ts +14 -5
- package/src/config/README.md +61 -1
- package/src/config/catalog-include.ts +2 -2
- package/src/config/defaults.ts +24 -14
- package/src/config/env.ts +41 -0
- package/src/config/errors.ts +7 -0
- package/src/config/from-env.ts +26 -0
- package/src/config/helpers.ts +4 -2
- package/src/config/index.ts +4 -1
- package/src/config/loader.ts +158 -50
- package/src/config/resolve-env.ts +94 -0
- package/src/config/runtime.ts +12 -7
- package/src/config/validator.ts +4 -6
- package/src/handlebars.ts +8 -2
- package/src/index.d.ts +2 -2
- package/src/index.ts +85 -8
- package/src/pipeline/index.ts +1 -1
- package/src/registry/asset-storage/adapters/vercel-blob.ts +1 -1
- package/src/registry/asset-storage/resolve.ts +6 -3
- package/src/registry/checkout/index.ts +5 -5
- package/src/registry/connection.ts +72 -71
- package/src/registry/db/driver.ts +6 -17
- package/src/registry/db/index.ts +0 -1
- package/src/registry/publish/index.ts +5 -5
- package/src/store/filesystem.ts +2 -2
- package/src/transformers/plugins/ssr-render.ts +53 -16
- package/src/transformers/preview/component/css.ts +93 -50
- package/src/transformers/preview/pattern/html.ts +3 -1
- package/src/transformers/preview/types.ts +14 -0
- package/src/transformers/utils/build.ts +80 -0
- package/src/transformers/utils/module.ts +4 -2
- package/src/types/config.ts +51 -29
- package/templates/default/env.tpl +10 -0
- package/templates/default/gitignore.tpl +7 -0
- package/templates/default/handoff.config.js.tpl +21 -172
- package/templates/default/handoff.config.ts.tpl +21 -156
- package/templates/default/handoff.config.with-examples.js.tpl +20 -171
- package/templates/default/handoff.config.with-examples.ts.tpl +20 -155
package/dist/config/loader.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { Config } from '../types/config';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
constructor(message: string);
|
|
5
|
-
}
|
|
1
|
+
import { Config, ResolvedConfig } from '../types/config';
|
|
2
|
+
export declare const CONFIG_FILE_PREFERENCE: readonly ["handoff.config.ts", "handoff.config.js", "handoff.config.cjs", "handoff.config.json"];
|
|
3
|
+
export { HandoffConfigError } from './errors';
|
|
6
4
|
type ConfigLoadResult = {
|
|
7
|
-
config:
|
|
5
|
+
config: ResolvedConfig;
|
|
8
6
|
configPath?: string;
|
|
7
|
+
profile?: string;
|
|
8
|
+
/** The profile file merged onto the base config. */
|
|
9
|
+
profileConfigPath?: string;
|
|
9
10
|
};
|
|
10
11
|
/** Where to look for the config: the working directory, plus an optional explicit file. */
|
|
11
12
|
export type ConfigLoadContext = {
|
|
@@ -13,7 +14,24 @@ export type ConfigLoadContext = {
|
|
|
13
14
|
workingPath?: string;
|
|
14
15
|
/** Explicit config file (the CLI's `-c, --config`), replacing discovery entirely. */
|
|
15
16
|
configPath?: string;
|
|
17
|
+
/** Profile to merge onto the base config (the CLI's `--profile`), overriding `HANDOFF_PROFILE`. */
|
|
18
|
+
profile?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Profile names that resolve without a `handoff.config.<profile>.*` file, leaving the base config
|
|
21
|
+
* unchanged. Registry commands pass the names of saved logins, because there a profile selects the
|
|
22
|
+
* registry rather than a config layer.
|
|
23
|
+
*/
|
|
24
|
+
knownProfiles?: string[];
|
|
25
|
+
/** Sentence appended when a selected profile matches neither a config file nor `knownProfiles`. */
|
|
26
|
+
profileNotFoundHint?: string;
|
|
16
27
|
};
|
|
28
|
+
/** Which selector picked the profile, so an error can name the one the user set. */
|
|
29
|
+
type ProfileSelection = {
|
|
30
|
+
name: string;
|
|
31
|
+
source: '--profile' | 'HANDOFF_PROFILE';
|
|
32
|
+
};
|
|
33
|
+
/** `--profile` wins over `HANDOFF_PROFILE`, and an empty value counts as unset. */
|
|
34
|
+
export declare const resolveProfileSelection: (requestedProfile?: string) => ProfileSelection | undefined;
|
|
17
35
|
/**
|
|
18
36
|
* Loads the handoff configuration for the given working directory and returns metadata.
|
|
19
37
|
*/
|
|
@@ -22,12 +40,12 @@ export declare const initConfigWithMetadata: (configOverride?: Partial<Config>,
|
|
|
22
40
|
* Loads the handoff configuration for the given working directory.
|
|
23
41
|
*
|
|
24
42
|
* Searches for config files in order: handoff.config.ts, handoff.config.js, handoff.config.cjs, handoff.config.json,
|
|
25
|
-
* unless `context.configPath` names one explicitly.
|
|
26
|
-
*
|
|
43
|
+
* unless `context.configPath` names one explicitly. A selected profile adds a `handoff.config.<profile>.*` sidecar,
|
|
44
|
+
* which only `context.knownProfiles` makes optional.
|
|
45
|
+
* Layers resolve as defaults, base config, profile, then the given overrides, merging plain objects recursively.
|
|
27
46
|
*
|
|
28
47
|
* @param configOverride - Optional partial config to override file-loaded values.
|
|
29
|
-
* @param context - Optional working directory
|
|
48
|
+
* @param context - Optional working directory, explicit config file, and profile to load.
|
|
30
49
|
* @returns The fully resolved Config object.
|
|
31
50
|
*/
|
|
32
|
-
export declare const initConfig: (configOverride?: Partial<Config>, context?: ConfigLoadContext) =>
|
|
33
|
-
export {};
|
|
51
|
+
export declare const initConfig: (configOverride?: Partial<Config>, context?: ConfigLoadContext) => ResolvedConfig;
|
package/dist/config/loader.js
CHANGED
|
@@ -3,31 +3,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.initConfig = exports.initConfigWithMetadata = exports.HandoffConfigError = void 0;
|
|
6
|
+
exports.initConfig = exports.initConfigWithMetadata = exports.resolveProfileSelection = exports.HandoffConfigError = exports.CONFIG_FILE_PREFERENCE = void 0;
|
|
7
7
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
8
|
+
const build_1 = require("../transformers/utils/build");
|
|
8
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
-
const
|
|
10
|
+
const mergeWith_1 = __importDefault(require("lodash/mergeWith"));
|
|
10
11
|
const module_1 = require("module");
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
12
13
|
const logger_1 = require("../utils/logger");
|
|
13
14
|
const path_2 = require("../utils/path");
|
|
14
15
|
const defaults_1 = require("./defaults");
|
|
15
|
-
const
|
|
16
|
+
const env_1 = require("./env");
|
|
17
|
+
const helpers_1 = require("./helpers");
|
|
18
|
+
const errors_1 = require("./errors");
|
|
19
|
+
const from_env_1 = require("./from-env");
|
|
20
|
+
const resolve_env_1 = require("./resolve-env");
|
|
21
|
+
exports.CONFIG_FILE_PREFERENCE = [
|
|
16
22
|
'handoff.config.ts',
|
|
17
23
|
'handoff.config.js',
|
|
18
24
|
'handoff.config.cjs',
|
|
19
25
|
'handoff.config.json',
|
|
20
26
|
];
|
|
21
27
|
/** Extensions the loader knows how to evaluate, derived from the discoverable file names. */
|
|
22
|
-
const SUPPORTED_CONFIG_EXTENSIONS = Array.from(new Set(CONFIG_FILE_PREFERENCE.map((fileName) => path_1.default.extname(fileName))));
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.name = 'HandoffConfigError';
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
exports.HandoffConfigError = HandoffConfigError;
|
|
28
|
+
const SUPPORTED_CONFIG_EXTENSIONS = Array.from(new Set(exports.CONFIG_FILE_PREFERENCE.map((fileName) => path_1.default.extname(fileName))));
|
|
29
|
+
/** A profile name becomes part of a file name, so it stays lowercase letters, numbers, and hyphens. */
|
|
30
|
+
const PROFILE_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
31
|
+
var errors_2 = require("./errors");
|
|
32
|
+
Object.defineProperty(exports, "HandoffConfigError", { enumerable: true, get: function () { return errors_2.HandoffConfigError; } });
|
|
31
33
|
const evaluateTypeScriptConfig = (filePath, handoffModulePath) => {
|
|
32
34
|
var _a, _b;
|
|
33
35
|
const buildResult = esbuild_1.default.buildSync({
|
|
@@ -39,6 +41,7 @@ const evaluateTypeScriptConfig = (filePath, handoffModulePath) => {
|
|
|
39
41
|
target: 'node16',
|
|
40
42
|
logLevel: 'silent',
|
|
41
43
|
external: ['handoff-app'],
|
|
44
|
+
loader: build_1.STYLE_IMPORT_LOADERS,
|
|
42
45
|
});
|
|
43
46
|
const code = (_b = (_a = buildResult.outputFiles) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text;
|
|
44
47
|
if (!code) {
|
|
@@ -75,6 +78,28 @@ const loadConfigFile = (configPath) => {
|
|
|
75
78
|
const importedConfig = require(configPath);
|
|
76
79
|
return (importedConfig.default || importedConfig);
|
|
77
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Normalize here and not only in `defineConfig`: a `.json` config, or a profile that skips the
|
|
83
|
+
* helper, would otherwise keep its camelCase keys. The rest of the codebase reads the runtime keys,
|
|
84
|
+
* so a later layer could not override an earlier one.
|
|
85
|
+
*/
|
|
86
|
+
const loadConfigLayer = (configPath) => (0, helpers_1.normalizeConfig)(loadConfigFile(configPath));
|
|
87
|
+
const pickExisting = (candidates) => {
|
|
88
|
+
const existing = candidates.filter((filePath) => fs_extra_1.default.existsSync(filePath));
|
|
89
|
+
if (!existing.length) {
|
|
90
|
+
return { selected: undefined, ignored: [] };
|
|
91
|
+
}
|
|
92
|
+
const [selected, ...ignored] = existing;
|
|
93
|
+
return { selected, ignored };
|
|
94
|
+
};
|
|
95
|
+
const warnIgnored = (selected, ignored) => {
|
|
96
|
+
if (!selected || ignored.length === 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
logger_1.Logger.warn(`Multiple config files found. Using "${path_1.default.basename(selected)}" and ignoring: ${ignored
|
|
100
|
+
.map((filePath) => `"${path_1.default.basename(filePath)}"`)
|
|
101
|
+
.join(', ')}.`);
|
|
102
|
+
};
|
|
78
103
|
/**
|
|
79
104
|
* Picks the config file to load. An explicitly requested file replaces discovery - it is resolved
|
|
80
105
|
* from `workingPath` and must exist in a format the loader can evaluate. Otherwise every known
|
|
@@ -84,70 +109,120 @@ const resolveConfigFilePath = (workingPath, requestedPath) => {
|
|
|
84
109
|
if (requestedPath) {
|
|
85
110
|
const selected = path_1.default.resolve(workingPath, requestedPath);
|
|
86
111
|
if (!fs_extra_1.default.existsSync(selected)) {
|
|
87
|
-
throw new HandoffConfigError(`Config file not found: "${requestedPath}" (resolved to ${selected})`);
|
|
112
|
+
throw new errors_1.HandoffConfigError(`Config file not found: "${requestedPath}" (resolved to ${selected})`);
|
|
88
113
|
}
|
|
89
114
|
if (!SUPPORTED_CONFIG_EXTENSIONS.includes(path_1.default.extname(selected))) {
|
|
90
|
-
throw new HandoffConfigError(`Unsupported config file "${path_1.default.basename(selected)}". Supported extensions: ${SUPPORTED_CONFIG_EXTENSIONS.join(', ')}`);
|
|
115
|
+
throw new errors_1.HandoffConfigError(`Unsupported config file "${path_1.default.basename(selected)}". Supported extensions: ${SUPPORTED_CONFIG_EXTENSIONS.join(', ')}`);
|
|
91
116
|
}
|
|
92
117
|
return { selected, ignored: [] };
|
|
93
118
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
119
|
+
return pickExisting(exports.CONFIG_FILE_PREFERENCE.map((fileName) => path_1.default.resolve(workingPath, fileName)));
|
|
120
|
+
};
|
|
121
|
+
/** `--profile` wins over `HANDOFF_PROFILE`, and an empty value counts as unset. */
|
|
122
|
+
const resolveProfileSelection = (requestedProfile) => {
|
|
123
|
+
var _a;
|
|
124
|
+
const fromOption = requestedProfile === null || requestedProfile === void 0 ? void 0 : requestedProfile.trim();
|
|
125
|
+
if (fromOption) {
|
|
126
|
+
return { name: fromOption, source: '--profile' };
|
|
99
127
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
128
|
+
const fromEnvironment = (_a = process.env.HANDOFF_PROFILE) === null || _a === void 0 ? void 0 : _a.trim();
|
|
129
|
+
if (fromEnvironment) {
|
|
130
|
+
return { name: fromEnvironment, source: 'HANDOFF_PROFILE' };
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
};
|
|
134
|
+
exports.resolveProfileSelection = resolveProfileSelection;
|
|
135
|
+
/**
|
|
136
|
+
* Picks the profile file for a selected profile. It sits beside the base config: the base file name
|
|
137
|
+
* without its extension, then the profile name, then any config extension. So `handoff.config.ts`
|
|
138
|
+
* pairs with `handoff.config.local.*`, and a config named through `-c` keeps its own name. A selected
|
|
139
|
+
* profile has to resolve - silently falling back to the base config would deploy the wrong settings.
|
|
140
|
+
* A name listed in `knownProfiles` resolves without a file, because it names something else that
|
|
141
|
+
* exists, such as a saved registry login.
|
|
142
|
+
*/
|
|
143
|
+
const resolveProfileFilePath = (workingPath, baseConfigPath, selection, context) => {
|
|
144
|
+
var _a;
|
|
145
|
+
if (!PROFILE_NAME_PATTERN.test(selection.name)) {
|
|
146
|
+
throw new errors_1.HandoffConfigError(`Invalid config profile "${selection.name}" (from ${selection.source}). ` +
|
|
147
|
+
`A profile name can contain lowercase letters, numbers, and hyphens.`);
|
|
148
|
+
}
|
|
149
|
+
const baseName = baseConfigPath
|
|
150
|
+
? path_1.default.join(path_1.default.dirname(baseConfigPath), path_1.default.basename(baseConfigPath, path_1.default.extname(baseConfigPath)))
|
|
151
|
+
: path_1.default.resolve(workingPath, 'handoff.config');
|
|
152
|
+
const candidates = SUPPORTED_CONFIG_EXTENSIONS.map((extension) => `${baseName}.${selection.name}${extension}`);
|
|
153
|
+
const { selected, ignored } = pickExisting(candidates);
|
|
154
|
+
if (!selected) {
|
|
155
|
+
if ((_a = context.knownProfiles) === null || _a === void 0 ? void 0 : _a.includes(selection.name)) {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
throw new errors_1.HandoffConfigError(`Config profile "${selection.name}" (from ${selection.source}) not found. ` +
|
|
159
|
+
`Create one of: ${candidates.map((candidate) => path_1.default.basename(candidate)).join(', ')} in ${path_1.default.dirname(candidates[0])}.` +
|
|
160
|
+
(context.profileNotFoundHint ? ` ${context.profileNotFoundHint}` : ''));
|
|
161
|
+
}
|
|
162
|
+
warnIgnored(selected, ignored);
|
|
163
|
+
return selected;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Arrays and environment references are whole values: a later layer replaces them, including
|
|
167
|
+
* any reference default. Everything else keeps lodash semantics: plain
|
|
168
|
+
* objects merge recursively, scalars, `null` and functions replace, and `undefined` is skipped.
|
|
169
|
+
*/
|
|
170
|
+
const replaceValues = (destination, source) => {
|
|
171
|
+
if (source === undefined)
|
|
172
|
+
return undefined;
|
|
173
|
+
return Array.isArray(source) || (0, from_env_1.isEnvReference)(source) || (0, from_env_1.isEnvReference)(destination) ? source : undefined;
|
|
102
174
|
};
|
|
103
175
|
/**
|
|
104
176
|
* Loads the handoff configuration for the given working directory and returns metadata.
|
|
105
177
|
*/
|
|
106
178
|
const initConfigWithMetadata = (configOverride, context) => {
|
|
107
179
|
var _a, _b, _c;
|
|
108
|
-
|
|
109
|
-
const { selected: configPath, ignored } = resolveConfigFilePath(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
180
|
+
const workingPath = (_a = context === null || context === void 0 ? void 0 : context.workingPath) !== null && _a !== void 0 ? _a : (0, path_2.resolveWorkingPath)();
|
|
181
|
+
const { selected: configPath, ignored } = resolveConfigFilePath(workingPath, context === null || context === void 0 ? void 0 : context.configPath);
|
|
182
|
+
warnIgnored(configPath, ignored);
|
|
183
|
+
const profileSelection = (0, exports.resolveProfileSelection)(context === null || context === void 0 ? void 0 : context.profile);
|
|
184
|
+
const profileConfigPath = profileSelection ? resolveProfileFilePath(workingPath, configPath, profileSelection, context !== null && context !== void 0 ? context : {}) : undefined;
|
|
185
|
+
// Profile environment values must be present before the merged references resolve.
|
|
186
|
+
(0, env_1.loadProfileEnv)(profileSelection === null || profileSelection === void 0 ? void 0 : profileSelection.name);
|
|
187
|
+
const layers = [(0, defaults_1.defaultConfig)()];
|
|
115
188
|
if (configPath) {
|
|
116
|
-
|
|
189
|
+
layers.push(loadConfigLayer(configPath));
|
|
190
|
+
}
|
|
191
|
+
if (profileConfigPath) {
|
|
192
|
+
layers.push(loadConfigLayer(profileConfigPath));
|
|
117
193
|
}
|
|
118
|
-
// Apply overrides if provided
|
|
119
194
|
if (configOverride) {
|
|
120
|
-
|
|
121
|
-
if (value !== undefined) {
|
|
122
|
-
config[key] = value;
|
|
123
|
-
}
|
|
124
|
-
});
|
|
195
|
+
layers.push((0, helpers_1.normalizeConfig)(configOverride));
|
|
125
196
|
}
|
|
197
|
+
const profileLabel = profileSelection
|
|
198
|
+
? `"${profileSelection.name}" (from ${profileSelection.source})${profileConfigPath ? '' : ' - no config file, the base config is used unchanged'}`
|
|
199
|
+
: 'none selected';
|
|
200
|
+
logger_1.Logger.debug(`Config profile: ${profileLabel}`);
|
|
201
|
+
logger_1.Logger.debug(`Config files loaded: ${[configPath, profileConfigPath].filter(Boolean).join(', ') || 'none, using defaults'}`);
|
|
202
|
+
const merged = (0, mergeWith_1.default)({}, ...layers, replaceValues);
|
|
203
|
+
const config = (0, resolve_env_1.resolveConfigEnv)(merged, profileSelection === null || profileSelection === void 0 ? void 0 : profileSelection.name, layers[0]);
|
|
204
|
+
// Guards run on the resolved config so a profile cannot slip a removed setting past them.
|
|
126
205
|
const entries = config.entries;
|
|
127
206
|
if (entries && ('components' in entries || 'patterns' in entries)) {
|
|
128
|
-
throw new HandoffConfigError('entries.components and entries.patterns were removed. Use catalog.include. See UPGRADE.md#catalog-items.');
|
|
207
|
+
throw new errors_1.HandoffConfigError('entries.components and entries.patterns were removed. Use catalog.include. See UPGRADE.md#catalog-items.');
|
|
129
208
|
}
|
|
130
209
|
const format = (_c = (_b = config.runtime) === null || _b === void 0 ? void 0 : _b.workspace) === null || _c === void 0 ? void 0 : _c.declarationFormat;
|
|
131
210
|
if (format !== undefined && !['ts', 'js', 'cjs'].includes(format)) {
|
|
132
|
-
throw new HandoffConfigError('Item declarations support ts, js, and cjs. Convert JSON declarations to defineCatalogItem. See UPGRADE.md#catalog-items.');
|
|
211
|
+
throw new errors_1.HandoffConfigError('Item declarations support ts, js, and cjs. Convert JSON declarations to defineCatalogItem. See UPGRADE.md#catalog-items.');
|
|
133
212
|
}
|
|
134
|
-
|
|
135
|
-
// Top-level merge stays shallow to preserve 1.x semantics, except for the `runtime`
|
|
136
|
-
// block which is deep-merged so a partial user `runtime` (e.g. only `mode`) does not
|
|
137
|
-
// wipe the defaults supplied for the rest of the block.
|
|
138
|
-
const resolvedConfig = Object.assign(Object.assign(Object.assign({}, defaults), config), { runtime: (0, merge_1.default)({}, defaults.runtime, config.runtime) });
|
|
139
|
-
return { config: resolvedConfig, configPath };
|
|
213
|
+
return { config, configPath, profile: profileSelection === null || profileSelection === void 0 ? void 0 : profileSelection.name, profileConfigPath };
|
|
140
214
|
};
|
|
141
215
|
exports.initConfigWithMetadata = initConfigWithMetadata;
|
|
142
216
|
/**
|
|
143
217
|
* Loads the handoff configuration for the given working directory.
|
|
144
218
|
*
|
|
145
219
|
* Searches for config files in order: handoff.config.ts, handoff.config.js, handoff.config.cjs, handoff.config.json,
|
|
146
|
-
* unless `context.configPath` names one explicitly.
|
|
147
|
-
*
|
|
220
|
+
* unless `context.configPath` names one explicitly. A selected profile adds a `handoff.config.<profile>.*` sidecar,
|
|
221
|
+
* which only `context.knownProfiles` makes optional.
|
|
222
|
+
* Layers resolve as defaults, base config, profile, then the given overrides, merging plain objects recursively.
|
|
148
223
|
*
|
|
149
224
|
* @param configOverride - Optional partial config to override file-loaded values.
|
|
150
|
-
* @param context - Optional working directory
|
|
225
|
+
* @param context - Optional working directory, explicit config file, and profile to load.
|
|
151
226
|
* @returns The fully resolved Config object.
|
|
152
227
|
*/
|
|
153
228
|
const initConfig = (configOverride, context) => {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Config, ResolvedConfig } from '../types/config';
|
|
2
|
+
export declare const DEFERRED_PATHS: Set<string>;
|
|
3
|
+
export declare const configEnvName: (config: ResolvedConfig | null | undefined, path: string) => string | undefined;
|
|
4
|
+
/** Resolve only the merged winner. Functions and non-plain objects retain their identity. */
|
|
5
|
+
export declare const resolveConfigEnv: (config: Config, profile?: string, defaults?: Config) => ResolvedConfig;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveConfigEnv = exports.configEnvName = exports.DEFERRED_PATHS = void 0;
|
|
7
|
+
const isPlainObject_1 = __importDefault(require("lodash/isPlainObject"));
|
|
8
|
+
const logger_1 = require("../utils/logger");
|
|
9
|
+
const from_env_1 = require("./from-env");
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
exports.DEFERRED_PATHS = new Set(['runtime.registry.databaseUrl', 'runtime.registry.assetStorage.token']);
|
|
12
|
+
const SECRET_PATHS = new Set(['dev_access_token', 'devAccessToken', 'runtime.registryConnection.accessToken']);
|
|
13
|
+
/** Path prefixes whose resolved contents are JSON-baked into the bundle, so a reference would bake its value. */
|
|
14
|
+
const BAKED_PATHS = ['runtime.registry.assetStorage.options'];
|
|
15
|
+
const isBaked = (path) => BAKED_PATHS.some((baked) => path === baked || path.startsWith(`${baked}.`));
|
|
16
|
+
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
17
|
+
const ALIASES = {
|
|
18
|
+
HANDOFF_REGISTRY_URL: 'HANDOFF_CLOUD_URL',
|
|
19
|
+
HANDOFF_REGISTRY_ACCESS_TOKEN: 'HANDOFF_CLOUD_TOKEN',
|
|
20
|
+
};
|
|
21
|
+
const warnedAliases = new Set();
|
|
22
|
+
const referenceNames = new WeakMap();
|
|
23
|
+
const configEnvName = (config, path) => { var _a; return (_a = referenceNames.get(config)) === null || _a === void 0 ? void 0 : _a.get(path); };
|
|
24
|
+
exports.configEnvName = configEnvName;
|
|
25
|
+
const readEnv = (name) => {
|
|
26
|
+
const value = process.env[name];
|
|
27
|
+
const alias = ALIASES[name];
|
|
28
|
+
if (!alias || (value === null || value === void 0 ? void 0 : value.trim()))
|
|
29
|
+
return value;
|
|
30
|
+
const inherited = process.env[alias];
|
|
31
|
+
if (!(inherited === null || inherited === void 0 ? void 0 : inherited.trim()))
|
|
32
|
+
return value;
|
|
33
|
+
if (!warnedAliases.has(alias)) {
|
|
34
|
+
warnedAliases.add(alias);
|
|
35
|
+
logger_1.Logger.warn(`${alias} is deprecated and will be removed in a future release; set ${name} instead.`);
|
|
36
|
+
}
|
|
37
|
+
return inherited;
|
|
38
|
+
};
|
|
39
|
+
/** Resolve only the merged winner. Functions and non-plain objects retain their identity. */
|
|
40
|
+
const resolveConfigEnv = (config, profile, defaults) => {
|
|
41
|
+
const names = new Map();
|
|
42
|
+
const fail = (path, message) => {
|
|
43
|
+
throw new errors_1.HandoffConfigError(`Config "${path}" (profile "${profile !== null && profile !== void 0 ? profile : 'default'}"): ${message}`);
|
|
44
|
+
};
|
|
45
|
+
const walk = (value, path, seed) => {
|
|
46
|
+
var _a;
|
|
47
|
+
const deferred = exports.DEFERRED_PATHS.has(path);
|
|
48
|
+
const secret = SECRET_PATHS.has(path);
|
|
49
|
+
if ((secret || deferred) && value !== undefined && !(0, from_env_1.isEnvReference)(value)) {
|
|
50
|
+
fail(path, 'Expected an environment reference, such as { $env: "VARIABLE_NAME" }. Literal values are not allowed.');
|
|
51
|
+
}
|
|
52
|
+
if ((0, from_env_1.isEnvReference)(value)) {
|
|
53
|
+
// Checked before the name, so the message names the supported pattern instead of the reference shape.
|
|
54
|
+
if (isBaked(path)) {
|
|
55
|
+
fail(path, 'Environment references are not allowed here because the resolved value is baked into the build. ' +
|
|
56
|
+
"Put the variable name in options and read it through the adapter factory's `env` argument.");
|
|
57
|
+
}
|
|
58
|
+
if (typeof value.$env !== 'string' || !ENV_NAME.test(value.$env)) {
|
|
59
|
+
fail(path, 'Invalid environment variable name. Use letters, digits, and underscores, starting with a letter or underscore.');
|
|
60
|
+
}
|
|
61
|
+
const name = value.$env;
|
|
62
|
+
names.set(path, name);
|
|
63
|
+
if (Object.keys(value).some((key) => key !== '$env' && key !== 'default')) {
|
|
64
|
+
fail(path, `Environment variable "${name}" has an invalid reference shape.`);
|
|
65
|
+
}
|
|
66
|
+
if (deferred) {
|
|
67
|
+
if ('default' in value)
|
|
68
|
+
fail(path, `Environment variable "${name}" cannot have a default because it is read by the deployed app.`);
|
|
69
|
+
return { $env: name };
|
|
70
|
+
}
|
|
71
|
+
// Secret defaults can mark an optional credential, but cannot embed a credential value.
|
|
72
|
+
if (secret && value.default !== undefined && value.default !== null && value.default !== '') {
|
|
73
|
+
fail(path, `Environment variable "${name}" cannot have a literal secret default.`);
|
|
74
|
+
}
|
|
75
|
+
const raw = readEnv(name);
|
|
76
|
+
if (raw === undefined) {
|
|
77
|
+
if (Object.prototype.hasOwnProperty.call(value, 'default'))
|
|
78
|
+
return secret ? value.default : walk(value.default, path, seed);
|
|
79
|
+
fail(path, `Environment variable "${name}" is not set and has no default.`);
|
|
80
|
+
}
|
|
81
|
+
const typeHint = (_a = value.default) !== null && _a !== void 0 ? _a : ((0, from_env_1.isEnvReference)(seed) ? seed.default : seed);
|
|
82
|
+
if (typeof typeHint === 'boolean')
|
|
83
|
+
return raw === 'true';
|
|
84
|
+
if (typeof typeHint === 'number') {
|
|
85
|
+
const number = Number(raw);
|
|
86
|
+
if (!Number.isFinite(number))
|
|
87
|
+
fail(path, `Environment variable "${name}" must contain a finite number.`);
|
|
88
|
+
return number;
|
|
89
|
+
}
|
|
90
|
+
return raw;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(value))
|
|
93
|
+
return value.map((entry, index) => walk(entry, `${path}.${index}`, seed === null || seed === void 0 ? void 0 : seed[index]));
|
|
94
|
+
if (!(0, isPlainObject_1.default)(value))
|
|
95
|
+
return value;
|
|
96
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, walk(entry, path ? `${path}.${key}` : key, seed === null || seed === void 0 ? void 0 : seed[key])]));
|
|
97
|
+
};
|
|
98
|
+
const resolved = walk(config, '', defaults);
|
|
99
|
+
referenceNames.set(resolved, names);
|
|
100
|
+
return resolved;
|
|
101
|
+
};
|
|
102
|
+
exports.resolveConfigEnv = resolveConfigEnv;
|
package/dist/config/runtime.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ResolvedConfig, ConfigFileEntry, RuntimeConfig } from '../types/config';
|
|
2
2
|
/**
|
|
3
3
|
* Handoff instance shape needed by initRuntimeConfig.
|
|
4
4
|
* Avoids importing the full Handoff class to prevent circular deps.
|
|
5
5
|
*/
|
|
6
6
|
interface HandoffContext {
|
|
7
|
-
config:
|
|
7
|
+
config: ResolvedConfig;
|
|
8
8
|
workingPath: string;
|
|
9
9
|
modulePath: string;
|
|
10
10
|
}
|
|
@@ -15,7 +15,7 @@ export declare const resolveComponentDeclaration: (componentDir: string, compone
|
|
|
15
15
|
/**
|
|
16
16
|
* Expands catalog.include into item directories and removes duplicate paths.
|
|
17
17
|
*/
|
|
18
|
-
export declare const getCatalogItemDirectories: (config:
|
|
18
|
+
export declare const getCatalogItemDirectories: (config: ResolvedConfig, workingPath: string) => string[];
|
|
19
19
|
/**
|
|
20
20
|
* Initializes the runtime configuration by resolving component entries,
|
|
21
21
|
* SCSS/JS paths, and transformer options from the handoff config.
|
package/dist/config/runtime.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.toLowerCaseKeysAndValues = exports.getComponentsForPath = exports.isComponentDirectory = exports.initRuntimeConfig = exports.getCatalogItemDirectories = exports.resolveComponentDeclaration = void 0;
|
|
7
7
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
8
|
+
const build_1 = require("../transformers/utils/build");
|
|
8
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
10
|
const module_1 = require("module");
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
@@ -17,7 +18,8 @@ const markdown_1 = require("../utils/markdown");
|
|
|
17
18
|
const pages_1 = require("../utils/pages");
|
|
18
19
|
const path_2 = require("../utils/path");
|
|
19
20
|
const page_1 = require("./normalizers/page");
|
|
20
|
-
|
|
21
|
+
/** `.tsx`/`.jsx` are accepted so a preview can write its `render` as JSX. */
|
|
22
|
+
const DECLARATION_EXTENSIONS = ['tsx', 'ts', 'jsx', 'js', 'cjs'];
|
|
21
23
|
const getDeclarationFiles = (componentBaseName) => DECLARATION_EXTENSIONS.map((ext) => `${componentBaseName}.handoff.${ext}`);
|
|
22
24
|
const findPreferredDeclaration = (componentDir, componentBaseName) => {
|
|
23
25
|
const exactCandidates = getDeclarationFiles(componentBaseName);
|
|
@@ -25,7 +27,7 @@ const findPreferredDeclaration = (componentDir, componentBaseName) => {
|
|
|
25
27
|
if (exactMatch)
|
|
26
28
|
return exactMatch;
|
|
27
29
|
const allFiles = fs_extra_1.default.existsSync(componentDir) ? fs_extra_1.default.readdirSync(componentDir) : [];
|
|
28
|
-
const declarationFiles = allFiles.filter((file) => /\.handoff\.(ts|js|cjs)$/.test(file)).sort((a, b) => a.localeCompare(b));
|
|
30
|
+
const declarationFiles = allFiles.filter((file) => /\.handoff\.(tsx|ts|jsx|js|cjs)$/.test(file)).sort((a, b) => a.localeCompare(b));
|
|
29
31
|
if (!declarationFiles.length)
|
|
30
32
|
return undefined;
|
|
31
33
|
for (const ext of DECLARATION_EXTENSIONS) {
|
|
@@ -52,6 +54,7 @@ const evaluateDeclaration = (filePath, handoffModulePath) => {
|
|
|
52
54
|
logLevel: 'silent',
|
|
53
55
|
jsx: 'automatic',
|
|
54
56
|
external: ['react', 'react-dom', 'handoff-app'],
|
|
57
|
+
loader: build_1.STYLE_IMPORT_LOADERS,
|
|
55
58
|
});
|
|
56
59
|
const code = (_b = (_a = buildResult.outputFiles) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text;
|
|
57
60
|
if (!code) {
|
|
@@ -178,7 +181,7 @@ const initRuntimeConfig = (handoff) => {
|
|
|
178
181
|
const declaration = (0, exports.resolveComponentDeclaration)(itemPath, itemBaseName);
|
|
179
182
|
if (!declaration) {
|
|
180
183
|
const declarationFiles = getDeclarationFiles(itemBaseName);
|
|
181
|
-
logger_1.Logger.warn(`Missing catalog declaration (use .handoff.ts, .handoff.js, or .handoff.cjs; see UPGRADE.md#catalog-items): ${path_1.default.resolve(itemPath, declarationFiles.join(' or '))}`);
|
|
184
|
+
logger_1.Logger.warn(`Missing catalog declaration (use .handoff.tsx, .handoff.ts, .handoff.jsx, .handoff.js, or .handoff.cjs; see UPGRADE.md#catalog-items): ${path_1.default.resolve(itemPath, declarationFiles.join(' or '))}`);
|
|
182
185
|
continue;
|
|
183
186
|
}
|
|
184
187
|
const declarationPath = path_1.default.resolve(itemPath, declaration.fileName);
|
|
@@ -361,8 +364,8 @@ const injectPatternPreviews = (result) => {
|
|
|
361
364
|
}
|
|
362
365
|
// Case 3: args present (with or without preview base) -> create synthetic preview
|
|
363
366
|
let resolvedValues = {};
|
|
367
|
+
const basePreview = ref.preview ? (_b = component.previews) === null || _b === void 0 ? void 0 : _b[ref.preview] : undefined;
|
|
364
368
|
if (ref.preview) {
|
|
365
|
-
const basePreview = (_b = component.previews) === null || _b === void 0 ? void 0 : _b[ref.preview];
|
|
366
369
|
if (basePreview) {
|
|
367
370
|
resolvedValues = Object.assign({}, basePreview.values);
|
|
368
371
|
}
|
|
@@ -374,12 +377,7 @@ const injectPatternPreviews = (result) => {
|
|
|
374
377
|
resolvedValues = Object.assign(Object.assign({}, resolvedValues), ref.args);
|
|
375
378
|
const syntheticKey = `__pattern_${patternId}_${i}`;
|
|
376
379
|
component.internalPatternPreviews = component.internalPatternPreviews || {};
|
|
377
|
-
component.internalPatternPreviews[syntheticKey] = {
|
|
378
|
-
title: syntheticKey,
|
|
379
|
-
values: resolvedValues,
|
|
380
|
-
url: '',
|
|
381
|
-
sourcePreview: ref.preview,
|
|
382
|
-
};
|
|
380
|
+
component.internalPatternPreviews[syntheticKey] = Object.assign({ title: syntheticKey, values: resolvedValues, url: '', sourcePreview: ref.preview }, ((basePreview === null || basePreview === void 0 ? void 0 : basePreview.render) ? { render: basePreview.render } : {}));
|
|
383
381
|
ref.resolvedPreview = syntheticKey;
|
|
384
382
|
if (ref.resolved === undefined) {
|
|
385
383
|
ref.resolved = true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ResolvedConfig } from '../types/config';
|
|
2
2
|
/**
|
|
3
3
|
* Validates that the configuration has the required Figma credentials.
|
|
4
4
|
*
|
|
@@ -6,4 +6,4 @@ import { Config } from '../types/config';
|
|
|
6
6
|
* @returns The validated config (same reference).
|
|
7
7
|
* @throws {Error} If required Figma credentials are missing.
|
|
8
8
|
*/
|
|
9
|
-
export declare const validateConfig: (config:
|
|
9
|
+
export declare const validateConfig: (config: ResolvedConfig) => ResolvedConfig;
|
package/dist/config/validator.js
CHANGED
|
@@ -11,13 +11,11 @@ const logger_1 = require("../utils/logger");
|
|
|
11
11
|
*/
|
|
12
12
|
const validateConfig = (config) => {
|
|
13
13
|
// TODO: Check to see if the exported folder exists before we run start
|
|
14
|
-
if (!config.figma_project_id
|
|
15
|
-
// check to see if we can get this from the env
|
|
14
|
+
if (!config.figma_project_id) {
|
|
16
15
|
logger_1.Logger.error('Figma Project ID missing. Please set HANDOFF_FIGMA_PROJECT_ID or run "handoff-app fetch".');
|
|
17
16
|
throw new Error('Cannot initialize configuration');
|
|
18
17
|
}
|
|
19
|
-
if (!config.dev_access_token
|
|
20
|
-
// check to see if we can get this from the env
|
|
18
|
+
if (!config.dev_access_token) {
|
|
21
19
|
logger_1.Logger.error('Figma Access Token missing. Please set HANDOFF_DEV_ACCESS_TOKEN or run "handoff-app fetch".');
|
|
22
20
|
throw new Error('Cannot initialize configuration');
|
|
23
21
|
}
|
package/dist/handlebars.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import type { CatalogItem, CatalogItemMeta } from './catalog/types';
|
|
2
|
-
export type { CatalogItem, CatalogItemMeta,
|
|
1
|
+
import type { CatalogItem, CatalogItemMeta, CatalogPreview as RendererPreview } from './catalog/types';
|
|
2
|
+
export type { CatalogItem, CatalogItemMeta, CompositionRef, SourceDescriptor } from './catalog/types';
|
|
3
|
+
/** A template renderer never calls a preview `render`, so declaring one is an error, not a no-op. */
|
|
4
|
+
export type CatalogPreview<TArgs = Record<string, unknown>> = Omit<RendererPreview<TArgs>, 'render'> & {
|
|
5
|
+
render?: never;
|
|
6
|
+
};
|
|
7
|
+
/** Preview type for a Handlebars catalog item, with its argument type applied. */
|
|
8
|
+
export type Preview<TItem> = TItem extends {
|
|
9
|
+
readonly __args?: infer TArgs;
|
|
10
|
+
} ? CatalogPreview<Partial<NonNullable<TArgs>>> : never;
|
|
3
11
|
/**
|
|
4
12
|
* Declares a Handlebars catalog item. `implementation` is the path to its `.hbs` template. The
|
|
5
13
|
* renderer comes from this module, so the build fails an item whose file does not match it.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import './config/env';
|
|
2
2
|
import { Types as CoreTypes, Handoff as HandoffRunner } from 'handoff-core';
|
|
3
3
|
import { type BuildPackage, type BuildTarget } from './app-builder';
|
|
4
4
|
import { type RegistryEntityKind } from './registry/content-kinds';
|
|
5
5
|
import type { TransferEntityKind } from './registry/transfer';
|
|
6
6
|
import { type HandoffStore } from './store';
|
|
7
|
-
import { Config, ConfigFileEntry, RuntimeConfig } from './types/config';
|
|
7
|
+
import { Config, ResolvedConfig, ConfigFileEntry, RuntimeConfig } from './types/config';
|
|
8
8
|
/** How a {@link Handoff} instance is set up. */
|
|
9
9
|
export interface HandoffOptions {
|
|
10
10
|
debug?: boolean;
|
|
@@ -21,6 +21,19 @@ export interface HandoffOptions {
|
|
|
21
21
|
config?: Partial<Config>;
|
|
22
22
|
/** Explicit config file (the CLI's `-c, --config`), resolved from the working path. */
|
|
23
23
|
configPath?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Config profile to merge onto the base config (the CLI's `--profile`). It overrides
|
|
26
|
+
* `HANDOFF_PROFILE`, and a selected profile has to resolve to a `handoff.config.<profile>.*` file
|
|
27
|
+
* unless {@link profileWithoutConfig} widens that.
|
|
28
|
+
*/
|
|
29
|
+
profile?: string;
|
|
30
|
+
/**
|
|
31
|
+
* What a selected profile may resolve to when it has no config file. `'saved-login'` accepts a name
|
|
32
|
+
* a saved registry login uses, so publish and checkout can address a registry that no config file
|
|
33
|
+
* describes. `'any'` accepts any valid name, for the `login` command that creates the login. Unset,
|
|
34
|
+
* a profile must name a config file, so a typo cannot silently run with the base config.
|
|
35
|
+
*/
|
|
36
|
+
profileWithoutConfig?: 'saved-login' | 'any';
|
|
24
37
|
}
|
|
25
38
|
/**
|
|
26
39
|
* A content kind to act on, optionally narrowed to specific ids. The plain form is the common case
|
|
@@ -31,7 +44,7 @@ export type ContentTarget = RegistryEntityKind | {
|
|
|
31
44
|
ids?: string[];
|
|
32
45
|
};
|
|
33
46
|
declare class Handoff {
|
|
34
|
-
config:
|
|
47
|
+
config: ResolvedConfig | null;
|
|
35
48
|
debug: boolean;
|
|
36
49
|
force: boolean;
|
|
37
50
|
dryRun: boolean;
|
|
@@ -56,10 +69,18 @@ declare class Handoff {
|
|
|
56
69
|
private _configFilePaths;
|
|
57
70
|
private _configFileIndex;
|
|
58
71
|
private _mainConfigFilePath?;
|
|
72
|
+
private _profileConfigFilePath?;
|
|
73
|
+
private _profile?;
|
|
59
74
|
private _documentationObjectCache?;
|
|
60
75
|
private _handoffRunner?;
|
|
61
76
|
constructor(options?: HandoffOptions);
|
|
62
77
|
private construct;
|
|
78
|
+
/**
|
|
79
|
+
* What a selected profile may resolve to besides a config file, and how to explain a name that
|
|
80
|
+
* resolves to nothing. A command that does not accept a login still names one it found, so the
|
|
81
|
+
* difference between the two kinds of profile is visible rather than puzzling.
|
|
82
|
+
*/
|
|
83
|
+
private profilesWithoutConfig;
|
|
63
84
|
init(configOverride?: Partial<Config>): Handoff;
|
|
64
85
|
reload(): Handoff;
|
|
65
86
|
preRunner(validate?: boolean): Handoff;
|
|
@@ -215,6 +236,12 @@ declare class Handoff {
|
|
|
215
236
|
* Gets the selected main config file path if one exists.
|
|
216
237
|
*/
|
|
217
238
|
getMainConfigFilePath(): string | undefined;
|
|
239
|
+
getProfile(): string | undefined;
|
|
240
|
+
/**
|
|
241
|
+
* True for the base config file and for the profile file merged onto it. Both carry project-wide
|
|
242
|
+
* settings, so a change to either one invalidates every entity rather than a single declaration.
|
|
243
|
+
*/
|
|
244
|
+
isMainConfigFile(filePath: string): boolean;
|
|
218
245
|
getConfigFileEntry(configPath: string): ConfigFileEntry | undefined;
|
|
219
246
|
/**
|
|
220
247
|
* Clears all cached data
|
|
@@ -228,8 +255,10 @@ declare class Handoff {
|
|
|
228
255
|
*/
|
|
229
256
|
private readJsonFile;
|
|
230
257
|
}
|
|
231
|
-
export type { Config, RegisterHandlebarsHelpersContext } from './types/config';
|
|
258
|
+
export type { Config, ResolvedConfig, RegisterHandlebarsHelpersContext } from './types/config';
|
|
232
259
|
export { defineConfig } from './config';
|
|
260
|
+
export { fromEnv } from './config/from-env';
|
|
261
|
+
export type { EnvValue, EnvSecret, RuntimeEnvReference } from './config/from-env';
|
|
233
262
|
export type { CatalogItem, CatalogItemEntries, CatalogItemInput, CatalogItemMeta, CatalogPreview, CompositionRef, Preview, SourceDescriptor, SourceFormat, RendererKind, } from './catalog';
|
|
234
263
|
export { defineAssetStorage } from './registry/asset-storage/define';
|
|
235
264
|
export type { AssetStorage, AssetStorageContext, AssetStorageFactory, AssetStorageInput, AssetStorageReadResult, } from './registry/asset-storage/types';
|