@revoengine/cli 1.0.8 → 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 +607 -298
- 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 -3
- package/dist/src/component-lock.js +92 -9
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.js +89 -4
- 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([
|
|
@@ -60,14 +61,13 @@ export function normalizeComponentSource(component) {
|
|
|
60
61
|
desc: normalizeValue(source.desc),
|
|
61
62
|
active: source.active ?? true,
|
|
62
63
|
type: source.type || '',
|
|
63
|
-
compiler: source.compiler || '',
|
|
64
64
|
async: Boolean(source.async),
|
|
65
65
|
elements: [...(source.elements || [])]
|
|
66
66
|
.map((element) => ({
|
|
67
|
-
key: element.key,
|
|
67
|
+
key: typeof element.key === 'string' ? element.key : '',
|
|
68
68
|
desc: normalizeValue(element.desc),
|
|
69
69
|
hidden: Boolean(element.hidden),
|
|
70
|
-
order: element.order,
|
|
70
|
+
order: Number.isFinite(Number(element.order)) ? Number(element.order) : 0,
|
|
71
71
|
details: element.details ?? '',
|
|
72
72
|
}))
|
|
73
73
|
.sort((left, right) => left.order - right.order || left.key.localeCompare(right.key)),
|
|
@@ -76,6 +76,16 @@ export function normalizeComponentSource(component) {
|
|
|
76
76
|
export function hashComponentSource(component) {
|
|
77
77
|
return hashStable(normalizeComponentSource(component));
|
|
78
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
|
+
}
|
|
79
89
|
export function getLockPath(projectRoot) {
|
|
80
90
|
return path.join(projectRoot, REVO_LOCK_FILE);
|
|
81
91
|
}
|
|
@@ -83,25 +93,98 @@ export function readComponentLock(projectRoot) {
|
|
|
83
93
|
const lockPath = getLockPath(projectRoot);
|
|
84
94
|
if (!fs.existsSync(lockPath)) {
|
|
85
95
|
return {
|
|
86
|
-
schemaVersion:
|
|
96
|
+
schemaVersion: 2,
|
|
97
|
+
componentIdentity: COMPONENT_IDENTITY_BY_ID,
|
|
87
98
|
components: {},
|
|
88
99
|
};
|
|
89
100
|
}
|
|
90
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
|
+
}));
|
|
91
120
|
return {
|
|
92
|
-
schemaVersion:
|
|
93
|
-
|
|
121
|
+
schemaVersion: 2,
|
|
122
|
+
componentIdentity,
|
|
123
|
+
components,
|
|
94
124
|
};
|
|
95
125
|
}
|
|
96
126
|
export function writeComponentLock(projectRoot, lock) {
|
|
97
127
|
const sortedComponents = Object.fromEntries(Object.entries(lock.components).sort(([left], [right]) => left.localeCompare(right)));
|
|
98
128
|
writeJsonFile(getLockPath(projectRoot), {
|
|
99
|
-
schemaVersion:
|
|
129
|
+
schemaVersion: 2,
|
|
130
|
+
componentIdentity: normalizeComponentIdentityConfig(lock.componentIdentity),
|
|
100
131
|
components: sortedComponents,
|
|
101
132
|
});
|
|
102
133
|
}
|
|
103
|
-
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);
|
|
104
149
|
const lock = readComponentLock(projectRoot);
|
|
105
|
-
lock
|
|
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];
|
|
173
|
+
if (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) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
lock.componentIdentity = normalizedIdentity;
|
|
188
|
+
lock.components[key] = normalizedEntry;
|
|
106
189
|
writeComponentLock(projectRoot, lock);
|
|
107
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({
|
|
@@ -316,8 +381,13 @@ function readAuthValidationStateStore() {
|
|
|
316
381
|
if (!fs.existsSync(authStateFile)) {
|
|
317
382
|
return emptyAuthValidationStateStore();
|
|
318
383
|
}
|
|
319
|
-
|
|
320
|
-
|
|
384
|
+
try {
|
|
385
|
+
const raw = readJsonFile(authStateFile);
|
|
386
|
+
return parseAuthValidationStateStore(raw);
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
return emptyAuthValidationStateStore();
|
|
390
|
+
}
|
|
321
391
|
}
|
|
322
392
|
export function readAuthValidationState(key) {
|
|
323
393
|
const store = readAuthValidationStateStore();
|
|
@@ -352,6 +422,7 @@ export function clearAuthValidationState(key) {
|
|
|
352
422
|
}
|
|
353
423
|
}
|
|
354
424
|
}
|
|
425
|
+
export const DEFAULT_ENV_NAME = 'default';
|
|
355
426
|
export function resolveRuntimeConfig(options = {}) {
|
|
356
427
|
const env = {
|
|
357
428
|
baseUrl: resolveEnvValue(['REVO_URL', 'REVO_BASE_URL', 'REVOENGINE_URL', 'REVOENGINE_BASE_URL']),
|
|
@@ -359,6 +430,20 @@ export function resolveRuntimeConfig(options = {}) {
|
|
|
359
430
|
token: resolveEnvValue(['REVO_TOKEN', 'REVO_API_KEY', 'REVOENGINE_TOKEN', 'REVOENGINE_API_KEY']),
|
|
360
431
|
};
|
|
361
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
|
+
}
|
|
362
447
|
return {
|
|
363
448
|
baseUrl: options.baseUrl || env.baseUrl || stored.baseUrl || DEFAULT_BASE_URL,
|
|
364
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 {};
|