@revoengine/cli 1.0.9 → 1.0.10
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 +181 -4
- package/dist/src/cli.js +9 -1
- package/dist/src/client.d.ts +10 -0
- package/dist/src/client.js +157 -0
- package/dist/src/commands/component.js +604 -296
- package/dist/src/commands/env.d.ts +2 -0
- package/dist/src/commands/env.js +360 -0
- package/dist/src/commands/index.d.ts +2 -0
- package/dist/src/commands/index.js +2 -0
- package/dist/src/commands/metadata.d.ts +2 -0
- package/dist/src/commands/metadata.js +137 -0
- package/dist/src/commands/project.js +75 -1
- package/dist/src/component-lock.d.ts +28 -2
- package/dist/src/component-lock.js +89 -15
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.js +82 -2
- package/dist/src/env-sync.d.ts +86 -0
- package/dist/src/env-sync.js +331 -0
- package/dist/src/metadata-backfill.d.ts +46 -0
- package/dist/src/metadata-backfill.js +125 -0
- package/dist/src/project.d.ts +15 -9
- package/dist/src/project.js +89 -0
- package/dist/src/resource-metadata.d.ts +24 -0
- package/dist/src/resource-metadata.js +129 -0
- package/dist/src/tracked-resources.d.ts +7 -0
- package/dist/src/tracked-resources.js +37 -0
- package/dist/src/types.d.ts +3 -0
- package/dist/src/ui.js +12 -3
- package/package.json +2 -2
|
@@ -2,6 +2,7 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { REVO_PROJECT_DIR } from "./project.js";
|
|
5
|
+
import { COMPONENT_IDENTITY_BY_ID, normalizeComponentIdentityConfig, readComponentIdentityValue, sameComponentIdentityConfig, } from "./resource-metadata.js";
|
|
5
6
|
import { readJsonFile, writeJsonFile } from "./utils.js";
|
|
6
7
|
export const REVO_LOCK_FILE = path.join(REVO_PROJECT_DIR, 'revo.lock.json');
|
|
7
8
|
const VOLATILE_COMPONENT_FIELDS = new Set([
|
|
@@ -63,10 +64,10 @@ export function normalizeComponentSource(component) {
|
|
|
63
64
|
async: Boolean(source.async),
|
|
64
65
|
elements: [...(source.elements || [])]
|
|
65
66
|
.map((element) => ({
|
|
66
|
-
key: element.key,
|
|
67
|
+
key: typeof element.key === 'string' ? element.key : '',
|
|
67
68
|
desc: normalizeValue(element.desc),
|
|
68
69
|
hidden: Boolean(element.hidden),
|
|
69
|
-
order: element.order,
|
|
70
|
+
order: Number.isFinite(Number(element.order)) ? Number(element.order) : 0,
|
|
70
71
|
details: element.details ?? '',
|
|
71
72
|
}))
|
|
72
73
|
.sort((left, right) => left.order - right.order || left.key.localeCompare(right.key)),
|
|
@@ -75,6 +76,16 @@ export function normalizeComponentSource(component) {
|
|
|
75
76
|
export function hashComponentSource(component) {
|
|
76
77
|
return hashStable(normalizeComponentSource(component));
|
|
77
78
|
}
|
|
79
|
+
// componentId is instance-local, so comparing components across two RevoEngine
|
|
80
|
+
// environments must project it away; element componentElementIds are already
|
|
81
|
+
// excluded by normalizeComponentSource.
|
|
82
|
+
export function normalizePortableComponentSource(component) {
|
|
83
|
+
const { componentId, ...portable } = normalizeComponentSource(component);
|
|
84
|
+
return portable;
|
|
85
|
+
}
|
|
86
|
+
export function hashPortableComponentSource(component) {
|
|
87
|
+
return hashStable(normalizePortableComponentSource(component));
|
|
88
|
+
}
|
|
78
89
|
export function getLockPath(projectRoot) {
|
|
79
90
|
return path.join(projectRoot, REVO_LOCK_FILE);
|
|
80
91
|
}
|
|
@@ -82,35 +93,98 @@ export function readComponentLock(projectRoot) {
|
|
|
82
93
|
const lockPath = getLockPath(projectRoot);
|
|
83
94
|
if (!fs.existsSync(lockPath)) {
|
|
84
95
|
return {
|
|
85
|
-
schemaVersion:
|
|
96
|
+
schemaVersion: 2,
|
|
97
|
+
componentIdentity: COMPONENT_IDENTITY_BY_ID,
|
|
86
98
|
components: {},
|
|
87
99
|
};
|
|
88
100
|
}
|
|
89
101
|
const raw = readJsonFile(lockPath);
|
|
102
|
+
const componentIdentity = normalizeComponentIdentityConfig(raw.componentIdentity, COMPONENT_IDENTITY_BY_ID);
|
|
103
|
+
const components = Object.fromEntries(Object.entries(raw.components && typeof raw.components === 'object' ? raw.components : {})
|
|
104
|
+
.map(([key, entry]) => {
|
|
105
|
+
const remoteComponentId = String(entry.remoteComponentId || entry.componentId || key);
|
|
106
|
+
const identityMode = entry.identityMode || componentIdentity.mode;
|
|
107
|
+
return [key, {
|
|
108
|
+
...entry,
|
|
109
|
+
identityMode,
|
|
110
|
+
...(identityMode === 'stableKey'
|
|
111
|
+
? {
|
|
112
|
+
metadataProperty: entry.metadataProperty || componentIdentity.metadataProperty,
|
|
113
|
+
stableKey: entry.stableKey || key,
|
|
114
|
+
}
|
|
115
|
+
: {}),
|
|
116
|
+
componentId: String(entry.componentId || remoteComponentId),
|
|
117
|
+
remoteComponentId,
|
|
118
|
+
}];
|
|
119
|
+
}));
|
|
90
120
|
return {
|
|
91
|
-
schemaVersion:
|
|
92
|
-
|
|
121
|
+
schemaVersion: 2,
|
|
122
|
+
componentIdentity,
|
|
123
|
+
components,
|
|
93
124
|
};
|
|
94
125
|
}
|
|
95
126
|
export function writeComponentLock(projectRoot, lock) {
|
|
96
127
|
const sortedComponents = Object.fromEntries(Object.entries(lock.components).sort(([left], [right]) => left.localeCompare(right)));
|
|
97
128
|
writeJsonFile(getLockPath(projectRoot), {
|
|
98
|
-
schemaVersion:
|
|
129
|
+
schemaVersion: 2,
|
|
130
|
+
componentIdentity: normalizeComponentIdentityConfig(lock.componentIdentity),
|
|
99
131
|
components: sortedComponents,
|
|
100
132
|
});
|
|
101
133
|
}
|
|
102
|
-
export function
|
|
134
|
+
export function componentLockKey(identity, component) {
|
|
135
|
+
return readComponentIdentityValue(component, identity);
|
|
136
|
+
}
|
|
137
|
+
export function componentLockMatchesIdentity(lock, identity) {
|
|
138
|
+
return sameComponentIdentityConfig(lock.componentIdentity, identity);
|
|
139
|
+
}
|
|
140
|
+
export function getComponentLockEntry(lock, identity, component) {
|
|
141
|
+
if (!componentLockMatchesIdentity(lock, identity)) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const key = componentLockKey(identity, component);
|
|
145
|
+
return key ? lock.components[key] || null : null;
|
|
146
|
+
}
|
|
147
|
+
export function upsertComponentLockEntry(projectRoot, entry, identity = COMPONENT_IDENTITY_BY_ID) {
|
|
148
|
+
const normalizedIdentity = normalizeComponentIdentityConfig(identity);
|
|
103
149
|
const lock = readComponentLock(projectRoot);
|
|
104
|
-
|
|
150
|
+
if (!componentLockMatchesIdentity(lock, normalizedIdentity)) {
|
|
151
|
+
lock.componentIdentity = normalizedIdentity;
|
|
152
|
+
lock.components = {};
|
|
153
|
+
}
|
|
154
|
+
const remoteComponentId = entry.remoteComponentId || entry.componentId;
|
|
155
|
+
const key = normalizedIdentity.mode === 'stableKey'
|
|
156
|
+
? entry.stableKey
|
|
157
|
+
: remoteComponentId;
|
|
158
|
+
if (!key) {
|
|
159
|
+
throw new Error(`Cannot write component lock entry without ${normalizedIdentity.mode === 'stableKey' ? normalizedIdentity.metadataProperty : 'componentId'}.`);
|
|
160
|
+
}
|
|
161
|
+
const normalizedEntry = {
|
|
162
|
+
...entry,
|
|
163
|
+
identityMode: normalizedIdentity.mode,
|
|
164
|
+
remoteComponentId,
|
|
165
|
+
...(normalizedIdentity.mode === 'stableKey'
|
|
166
|
+
? {
|
|
167
|
+
metadataProperty: normalizedIdentity.metadataProperty,
|
|
168
|
+
stableKey: key,
|
|
169
|
+
}
|
|
170
|
+
: {}),
|
|
171
|
+
};
|
|
172
|
+
const existing = lock.components[key];
|
|
105
173
|
if (existing
|
|
106
|
-
&& existing.
|
|
107
|
-
&& existing.
|
|
108
|
-
&& existing.
|
|
109
|
-
&& existing.
|
|
110
|
-
&& existing.
|
|
111
|
-
&& existing.
|
|
174
|
+
&& existing.identityMode === normalizedEntry.identityMode
|
|
175
|
+
&& existing.metadataProperty === normalizedEntry.metadataProperty
|
|
176
|
+
&& existing.stableKey === normalizedEntry.stableKey
|
|
177
|
+
&& existing.componentId === normalizedEntry.componentId
|
|
178
|
+
&& existing.remoteComponentId === normalizedEntry.remoteComponentId
|
|
179
|
+
&& existing.componentName === normalizedEntry.componentName
|
|
180
|
+
&& existing.category === normalizedEntry.category
|
|
181
|
+
&& existing.path === normalizedEntry.path
|
|
182
|
+
&& existing.remoteVersion === normalizedEntry.remoteVersion
|
|
183
|
+
&& existing.remoteHash === normalizedEntry.remoteHash
|
|
184
|
+
&& existing.sourceHash === normalizedEntry.sourceHash) {
|
|
112
185
|
return;
|
|
113
186
|
}
|
|
114
|
-
lock.
|
|
187
|
+
lock.componentIdentity = normalizedIdentity;
|
|
188
|
+
lock.components[key] = normalizedEntry;
|
|
115
189
|
writeComponentLock(projectRoot, lock);
|
|
116
190
|
}
|
package/dist/src/config.d.ts
CHANGED
|
@@ -9,9 +9,16 @@ export type RuntimeConfigOptions = {
|
|
|
9
9
|
baseUrl?: string;
|
|
10
10
|
instance?: string;
|
|
11
11
|
token?: string;
|
|
12
|
+
envName?: string;
|
|
13
|
+
};
|
|
14
|
+
export type StoredEnvironmentProfile = {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
token: string;
|
|
17
|
+
instance?: string;
|
|
12
18
|
};
|
|
13
19
|
type StoredConfig = Omit<RuntimeConfig, 'instance'> & {
|
|
14
20
|
instance?: string;
|
|
21
|
+
environments?: Record<string, StoredEnvironmentProfile>;
|
|
15
22
|
};
|
|
16
23
|
export type AuthValidationState = {
|
|
17
24
|
key: string;
|
|
@@ -45,10 +52,15 @@ export declare function loadStoredConfigIndex(): {
|
|
|
45
52
|
export declare function loadStoredConfig(options?: {
|
|
46
53
|
allowLegacy?: boolean;
|
|
47
54
|
}): StoredConfig;
|
|
55
|
+
export declare function listEnvironmentProfiles(): Record<string, StoredEnvironmentProfile>;
|
|
56
|
+
export declare function getEnvironmentProfile(name: string): StoredEnvironmentProfile | null;
|
|
57
|
+
export declare function saveEnvironmentProfile(name: string, profile: StoredEnvironmentProfile): void;
|
|
58
|
+
export declare function removeEnvironmentProfile(name: string): boolean;
|
|
48
59
|
export declare function buildAuthValidationKey(runtime: RuntimeConfigOptions): string;
|
|
49
60
|
export declare function readAuthValidationState(key?: string): AuthValidationState;
|
|
50
61
|
export declare function saveAuthValidationState(state: AuthValidationState): void;
|
|
51
62
|
export declare function clearAuthValidationState(key?: string): void;
|
|
63
|
+
export declare const DEFAULT_ENV_NAME = "default";
|
|
52
64
|
export declare function resolveRuntimeConfig(options?: RuntimeConfigOptions): {
|
|
53
65
|
baseUrl: string;
|
|
54
66
|
instance: string;
|
package/dist/src/config.js
CHANGED
|
@@ -74,16 +74,40 @@ export function saveStoredConfig(nextConfig) {
|
|
|
74
74
|
ensureDirectory(dir);
|
|
75
75
|
const current = readStoredConfigFile();
|
|
76
76
|
const instance = nextConfig.instance || current.instance || '';
|
|
77
|
+
const environments = nextConfig.environments ?? current.environments;
|
|
77
78
|
const config = {
|
|
78
79
|
baseUrl: nextConfig.baseUrl || current.baseUrl || DEFAULT_BASE_URL,
|
|
79
80
|
token: nextConfig.token || current.token || '',
|
|
80
81
|
...(instance ? { instance } : {}),
|
|
82
|
+
...(environments && Object.keys(environments).length > 0 ? { environments } : {}),
|
|
81
83
|
};
|
|
82
84
|
writeJsonFile(configFile, config);
|
|
83
85
|
removeFileIfExists(credentialsFile);
|
|
84
86
|
}
|
|
85
87
|
export function clearStoredConfig() {
|
|
86
|
-
const { configFile, credentialsFile, authStateFile } = getConfigPaths();
|
|
88
|
+
const { dir, configFile, credentialsFile, authStateFile } = getConfigPaths();
|
|
89
|
+
// Logout clears the default login but must not destroy named environment
|
|
90
|
+
// profiles stored alongside it.
|
|
91
|
+
const environments = readStoredConfigFile().environments;
|
|
92
|
+
if (environments && Object.keys(environments).length > 0) {
|
|
93
|
+
ensureDirectory(dir);
|
|
94
|
+
writeJsonFile(configFile, {
|
|
95
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
96
|
+
token: '',
|
|
97
|
+
environments,
|
|
98
|
+
});
|
|
99
|
+
for (const filePath of [credentialsFile, authStateFile]) {
|
|
100
|
+
try {
|
|
101
|
+
fs.unlinkSync(filePath);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (!error || error.code !== 'ENOENT') {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
87
111
|
for (const filePath of [configFile, credentialsFile, authStateFile]) {
|
|
88
112
|
try {
|
|
89
113
|
fs.unlinkSync(filePath);
|
|
@@ -144,6 +168,23 @@ function normalizeInstanceConfig(value) {
|
|
|
144
168
|
token: typeof value.token === 'string' ? value.token : '',
|
|
145
169
|
};
|
|
146
170
|
}
|
|
171
|
+
function parseEnvironmentProfiles(value) {
|
|
172
|
+
if (!isRecord(value)) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const environments = {};
|
|
176
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
177
|
+
if (!isRecord(entry) || typeof entry.baseUrl !== 'string' || typeof entry.token !== 'string') {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
environments[name] = {
|
|
181
|
+
baseUrl: entry.baseUrl,
|
|
182
|
+
token: entry.token,
|
|
183
|
+
...(typeof entry.instance === 'string' && isUuid(entry.instance) ? { instance: entry.instance } : {}),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return Object.keys(environments).length > 0 ? environments : null;
|
|
187
|
+
}
|
|
147
188
|
function parseFlatConfig(raw, credentials) {
|
|
148
189
|
if (!isRecord(raw) || isRecord(raw.instances)) {
|
|
149
190
|
return null;
|
|
@@ -153,6 +194,7 @@ function parseFlatConfig(raw, credentials) {
|
|
|
153
194
|
: isRecord(credentials) && typeof credentials.token === 'string'
|
|
154
195
|
? credentials.token
|
|
155
196
|
: '';
|
|
197
|
+
const environments = parseEnvironmentProfiles(raw.environments);
|
|
156
198
|
return {
|
|
157
199
|
baseUrl: typeof raw.baseUrl === 'string'
|
|
158
200
|
? raw.baseUrl
|
|
@@ -160,6 +202,7 @@ function parseFlatConfig(raw, credentials) {
|
|
|
160
202
|
? raw.url
|
|
161
203
|
: DEFAULT_BASE_URL,
|
|
162
204
|
...(typeof raw.instance === 'string' && isUuid(raw.instance) ? { instance: raw.instance } : {}),
|
|
205
|
+
...(environments ? { environments } : {}),
|
|
163
206
|
token,
|
|
164
207
|
};
|
|
165
208
|
}
|
|
@@ -181,14 +224,16 @@ function parseMappedConfig(raw) {
|
|
|
181
224
|
const defaultInstance = isUuid(configuredDefault) && instances[configuredDefault]
|
|
182
225
|
? configuredDefault
|
|
183
226
|
: Object.keys(instances)[0] || '';
|
|
227
|
+
const environments = parseEnvironmentProfiles(raw.environments);
|
|
184
228
|
const selected = defaultInstance ? instances[defaultInstance] : undefined;
|
|
185
|
-
|
|
229
|
+
const base = selected
|
|
186
230
|
? {
|
|
187
231
|
baseUrl: selected.baseUrl,
|
|
188
232
|
instance: defaultInstance,
|
|
189
233
|
token: selected.token,
|
|
190
234
|
}
|
|
191
235
|
: emptyStoredConfigFile();
|
|
236
|
+
return environments ? { ...base, environments } : base;
|
|
192
237
|
}
|
|
193
238
|
function parseLegacyConfig(config, credentials) {
|
|
194
239
|
const baseUrl = isRecord(config) && typeof config.baseUrl === 'string'
|
|
@@ -256,6 +301,7 @@ function mergeWithLegacy(stored, options = {}) {
|
|
|
256
301
|
return stored;
|
|
257
302
|
}
|
|
258
303
|
const merged = {
|
|
304
|
+
...stored,
|
|
259
305
|
baseUrl: stored.baseUrl || legacy.baseUrl || DEFAULT_BASE_URL,
|
|
260
306
|
token: stored.token || legacy.token || '',
|
|
261
307
|
};
|
|
@@ -267,6 +313,25 @@ function mergeWithLegacy(stored, options = {}) {
|
|
|
267
313
|
export function loadStoredConfig(options = {}) {
|
|
268
314
|
return mergeWithLegacy(readStoredConfig(), options);
|
|
269
315
|
}
|
|
316
|
+
export function listEnvironmentProfiles() {
|
|
317
|
+
return readStoredConfigFile().environments || {};
|
|
318
|
+
}
|
|
319
|
+
export function getEnvironmentProfile(name) {
|
|
320
|
+
return listEnvironmentProfiles()[name] || null;
|
|
321
|
+
}
|
|
322
|
+
export function saveEnvironmentProfile(name, profile) {
|
|
323
|
+
const environments = { ...listEnvironmentProfiles(), [name]: profile };
|
|
324
|
+
saveStoredConfig({ environments });
|
|
325
|
+
}
|
|
326
|
+
export function removeEnvironmentProfile(name) {
|
|
327
|
+
const environments = listEnvironmentProfiles();
|
|
328
|
+
if (!(name in environments)) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
delete environments[name];
|
|
332
|
+
saveStoredConfig({ environments });
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
270
335
|
export function buildAuthValidationKey(runtime) {
|
|
271
336
|
return createHash('sha256')
|
|
272
337
|
.update(JSON.stringify({
|
|
@@ -357,6 +422,7 @@ export function clearAuthValidationState(key) {
|
|
|
357
422
|
}
|
|
358
423
|
}
|
|
359
424
|
}
|
|
425
|
+
export const DEFAULT_ENV_NAME = 'default';
|
|
360
426
|
export function resolveRuntimeConfig(options = {}) {
|
|
361
427
|
const env = {
|
|
362
428
|
baseUrl: resolveEnvValue(['REVO_URL', 'REVO_BASE_URL', 'REVOENGINE_URL', 'REVOENGINE_BASE_URL']),
|
|
@@ -364,6 +430,20 @@ export function resolveRuntimeConfig(options = {}) {
|
|
|
364
430
|
token: resolveEnvValue(['REVO_TOKEN', 'REVO_API_KEY', 'REVOENGINE_TOKEN', 'REVOENGINE_API_KEY']),
|
|
365
431
|
};
|
|
366
432
|
const stored = loadStoredConfig();
|
|
433
|
+
// A named profile is an explicit selection and resolves exclusively: no
|
|
434
|
+
// fallback to env vars or the stored default, or --from/--to could silently
|
|
435
|
+
// mix one environment's URL with another environment's token or instance.
|
|
436
|
+
if (options.envName && options.envName !== DEFAULT_ENV_NAME) {
|
|
437
|
+
const profile = getEnvironmentProfile(options.envName);
|
|
438
|
+
if (!profile) {
|
|
439
|
+
throw new Error(`Unknown environment "${options.envName}". Run 'revo env list' to see saved environments.`);
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
baseUrl: options.baseUrl || profile.baseUrl || DEFAULT_BASE_URL,
|
|
443
|
+
instance: options.instance || profile.instance || '',
|
|
444
|
+
token: options.token || profile.token || '',
|
|
445
|
+
};
|
|
446
|
+
}
|
|
367
447
|
return {
|
|
368
448
|
baseUrl: options.baseUrl || env.baseUrl || stored.baseUrl || DEFAULT_BASE_URL,
|
|
369
449
|
instance: options.instance || env.instance || stored.instance || '',
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { RevoClient } from './client.ts';
|
|
2
|
+
import { normalizePortableComponentSource } from './component-lock.ts';
|
|
3
|
+
import type { ComponentRecord } from './types.ts';
|
|
4
|
+
export type EnvRef = {
|
|
5
|
+
env: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
instance: string;
|
|
8
|
+
};
|
|
9
|
+
export type PortableComponentSource = ReturnType<typeof normalizePortableComponentSource>;
|
|
10
|
+
export type EnvComponentEntry = {
|
|
11
|
+
stableKey: string;
|
|
12
|
+
componentId: string;
|
|
13
|
+
component: ComponentRecord;
|
|
14
|
+
portable: PortableComponentSource;
|
|
15
|
+
portableHash: string;
|
|
16
|
+
};
|
|
17
|
+
export type EnvCollision = {
|
|
18
|
+
stableKey: string;
|
|
19
|
+
components: Array<{
|
|
20
|
+
componentId: string;
|
|
21
|
+
name: string;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
export type EnvSnapshot = {
|
|
25
|
+
ref: EnvRef;
|
|
26
|
+
components: Map<string, EnvComponentEntry>;
|
|
27
|
+
collisions: EnvCollision[];
|
|
28
|
+
};
|
|
29
|
+
export type EnvSyncAction = 'create' | 'update' | 'delete' | 'orphan' | 'noop' | 'collision';
|
|
30
|
+
export type EnvSyncPlanItem = {
|
|
31
|
+
resourceType: 'component';
|
|
32
|
+
stableKey: string;
|
|
33
|
+
action: EnvSyncAction;
|
|
34
|
+
name: string;
|
|
35
|
+
category: string | null;
|
|
36
|
+
sourceComponentId: string | null;
|
|
37
|
+
targetComponentId: string | null;
|
|
38
|
+
sourceHash: string | null;
|
|
39
|
+
targetHash: string | null;
|
|
40
|
+
changedFields?: string[];
|
|
41
|
+
addedElementKeys?: string[];
|
|
42
|
+
removedElementKeys?: string[];
|
|
43
|
+
changedElementKeys?: string[];
|
|
44
|
+
reason: string;
|
|
45
|
+
};
|
|
46
|
+
export type EnvSyncScope = {
|
|
47
|
+
keys?: string[];
|
|
48
|
+
categories?: string[];
|
|
49
|
+
};
|
|
50
|
+
export type EnvSyncPlan = {
|
|
51
|
+
schemaVersion: 1;
|
|
52
|
+
generatedAt: string;
|
|
53
|
+
from: EnvRef;
|
|
54
|
+
to: EnvRef;
|
|
55
|
+
prune: boolean;
|
|
56
|
+
scope?: EnvSyncScope;
|
|
57
|
+
counts: Record<EnvSyncAction, number>;
|
|
58
|
+
items: EnvSyncPlanItem[];
|
|
59
|
+
};
|
|
60
|
+
export type EnvApplyItemResult = {
|
|
61
|
+
stableKey: string;
|
|
62
|
+
action: EnvSyncAction;
|
|
63
|
+
status: 'applied' | 'failed';
|
|
64
|
+
verified?: boolean;
|
|
65
|
+
error?: string;
|
|
66
|
+
};
|
|
67
|
+
export declare function normalizeKeyPart(value: string | null | undefined): string;
|
|
68
|
+
export declare function buildStableKey(component: ComponentRecord, stableKeyName?: string): string;
|
|
69
|
+
export declare function captureEnvSnapshot(client: RevoClient, envName: string, options?: {
|
|
70
|
+
stableKeyName?: string;
|
|
71
|
+
}): Promise<EnvSnapshot>;
|
|
72
|
+
declare const PORTABLE_METADATA_FIELDS: readonly ["name", "category", "desc", "active", "type", "async"];
|
|
73
|
+
export type PortableMetadataField = (typeof PORTABLE_METADATA_FIELDS)[number];
|
|
74
|
+
export declare function buildEnvSyncPlan(source: EnvSnapshot, target: EnvSnapshot, options: {
|
|
75
|
+
prune: boolean;
|
|
76
|
+
scope?: EnvSyncScope;
|
|
77
|
+
}): EnvSyncPlan;
|
|
78
|
+
export declare function applyEnvSyncPlan(input: {
|
|
79
|
+
targetClient: RevoClient;
|
|
80
|
+
plan: EnvSyncPlan;
|
|
81
|
+
source: EnvSnapshot;
|
|
82
|
+
target: EnvSnapshot;
|
|
83
|
+
stableKeyName?: string;
|
|
84
|
+
onItem?: (result: EnvApplyItemResult) => void;
|
|
85
|
+
}): Promise<EnvApplyItemResult[]>;
|
|
86
|
+
export {};
|