@revoengine/cli 1.0.10 → 1.0.11
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 +312 -33
- package/dist/src/cli.js +58 -7
- package/dist/src/client.d.ts +356 -5
- package/dist/src/client.js +803 -19
- package/dist/src/commands/auth.js +4 -2
- package/dist/src/commands/component.js +260 -141
- package/dist/src/commands/database-schemas.d.ts +2 -0
- package/dist/src/commands/database-schemas.js +188 -0
- package/dist/src/commands/database-views.d.ts +2 -0
- package/dist/src/commands/database-views.js +123 -0
- package/dist/src/commands/endpoints.js +114 -0
- package/dist/src/commands/env.js +44 -24
- package/dist/src/commands/events.d.ts +2 -0
- package/dist/src/commands/events.js +146 -0
- package/dist/src/commands/groups.d.ts +2 -0
- package/dist/src/commands/groups.js +169 -0
- package/dist/src/commands/index.d.ts +8 -0
- package/dist/src/commands/index.js +8 -0
- package/dist/src/commands/job-templates.d.ts +2 -0
- package/dist/src/commands/job-templates.js +101 -0
- package/dist/src/commands/metadata.js +29 -7
- package/dist/src/commands/project.js +10 -3
- package/dist/src/commands/role-groups.d.ts +2 -0
- package/dist/src/commands/role-groups.js +152 -0
- package/dist/src/commands/schedules.d.ts +2 -0
- package/dist/src/commands/schedules.js +141 -0
- package/dist/src/commands/terminal-service.d.ts +38 -0
- package/dist/src/commands/terminal-service.js +210 -0
- package/dist/src/commands/terminal.d.ts +22 -0
- package/dist/src/commands/terminal.js +511 -0
- package/dist/src/component-lock.d.ts +100 -2
- package/dist/src/component-lock.js +304 -15
- package/dist/src/config.d.ts +10 -2
- package/dist/src/config.js +49 -14
- package/dist/src/database-schema-artifacts.d.ts +7 -0
- package/dist/src/database-schema-artifacts.js +8 -0
- package/dist/src/env-sync.d.ts +0 -3
- package/dist/src/env-sync.js +3 -19
- package/dist/src/metadata-backfill.d.ts +16 -6
- package/dist/src/metadata-backfill.js +1069 -18
- package/dist/src/project.d.ts +2 -0
- package/dist/src/project.js +4 -17
- package/dist/src/prompt.js +10 -18
- package/dist/src/resource-metadata.d.ts +1 -0
- package/dist/src/resource-metadata.js +3 -0
- package/dist/src/resource-syncs/database-schema-sync.d.ts +117 -0
- package/dist/src/resource-syncs/database-schema-sync.js +2289 -0
- package/dist/src/resource-syncs/database-view-sync.d.ts +124 -0
- package/dist/src/resource-syncs/database-view-sync.js +1317 -0
- package/dist/src/resource-syncs/endpoint-sync.d.ts +96 -0
- package/dist/src/resource-syncs/endpoint-sync.js +1283 -0
- package/dist/src/resource-syncs/event-sync.d.ts +99 -0
- package/dist/src/resource-syncs/event-sync.js +949 -0
- package/dist/src/resource-syncs/group-sync.d.ts +86 -0
- package/dist/src/resource-syncs/group-sync.js +882 -0
- package/dist/src/resource-syncs/job-template-sync.d.ts +85 -0
- package/dist/src/resource-syncs/job-template-sync.js +782 -0
- package/dist/src/resource-syncs/role-group-sync.d.ts +83 -0
- package/dist/src/resource-syncs/role-group-sync.js +597 -0
- package/dist/src/resource-syncs/schedule-sync.d.ts +111 -0
- package/dist/src/resource-syncs/schedule-sync.js +1302 -0
- package/dist/src/resource-syncs/util.d.ts +19 -0
- package/dist/src/resource-syncs/util.js +116 -0
- package/dist/src/runtime-view.d.ts +1 -0
- package/dist/src/runtime-view.js +6 -1
- package/dist/src/sync-output.d.ts +38 -0
- package/dist/src/sync-output.js +131 -0
- package/dist/src/tracked-resources.d.ts +1 -1
- package/dist/src/tracked-resources.js +26 -2
- package/dist/src/types.d.ts +224 -0
- package/dist/src/ui.d.ts +3 -0
- package/dist/src/ui.js +67 -18
- package/dist/src/utils.d.ts +2 -0
- package/dist/src/utils.js +64 -0
- package/dist/src/workspace-component.d.ts +2 -0
- package/dist/src/workspace-component.js +34 -0
- package/dist/src/workspace-resource.d.ts +2 -0
- package/dist/src/workspace-resource.js +52 -0
- package/package.json +8 -3
|
@@ -22,6 +22,9 @@ const VOLATILE_COMPONENT_FIELDS = new Set([
|
|
|
22
22
|
'hash',
|
|
23
23
|
'remoteHash',
|
|
24
24
|
]);
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
27
|
+
}
|
|
25
28
|
function normalizeValue(value, fallback = null) {
|
|
26
29
|
return value ?? fallback;
|
|
27
30
|
}
|
|
@@ -86,6 +89,41 @@ export function normalizePortableComponentSource(component) {
|
|
|
86
89
|
export function hashPortableComponentSource(component) {
|
|
87
90
|
return hashStable(normalizePortableComponentSource(component));
|
|
88
91
|
}
|
|
92
|
+
export const PORTABLE_COMPONENT_CONFIG_FIELDS = [
|
|
93
|
+
'name',
|
|
94
|
+
'category',
|
|
95
|
+
'desc',
|
|
96
|
+
'active',
|
|
97
|
+
'type',
|
|
98
|
+
'async',
|
|
99
|
+
];
|
|
100
|
+
export function diffPortableComponentSource(source, target) {
|
|
101
|
+
const sourcePortable = normalizePortableComponentSource(source);
|
|
102
|
+
const targetPortable = normalizePortableComponentSource(target);
|
|
103
|
+
const configurationFields = PORTABLE_COMPONENT_CONFIG_FIELDS.filter((field) => sourcePortable[field] !== targetPortable[field]);
|
|
104
|
+
const elementsChanged = hashStable(sourcePortable.elements) !== hashStable(targetPortable.elements);
|
|
105
|
+
return {
|
|
106
|
+
configurationFields,
|
|
107
|
+
elementsChanged,
|
|
108
|
+
changedFields: [
|
|
109
|
+
...configurationFields,
|
|
110
|
+
...(elementsChanged ? ['elements'] : []),
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function buildPortableComponentUpdatePayload(source, target, changedFields) {
|
|
115
|
+
if (!Number.isInteger(target.version)) {
|
|
116
|
+
throw new Error('target component version is required to update component configuration');
|
|
117
|
+
}
|
|
118
|
+
const sourcePortable = normalizePortableComponentSource(source);
|
|
119
|
+
const payload = { version: target.version };
|
|
120
|
+
for (const field of PORTABLE_COMPONENT_CONFIG_FIELDS) {
|
|
121
|
+
if (changedFields.includes(field)) {
|
|
122
|
+
payload[field] = sourcePortable[field];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return payload;
|
|
126
|
+
}
|
|
89
127
|
export function getLockPath(projectRoot) {
|
|
90
128
|
return path.join(projectRoot, REVO_LOCK_FILE);
|
|
91
129
|
}
|
|
@@ -96,6 +134,14 @@ export function readComponentLock(projectRoot) {
|
|
|
96
134
|
schemaVersion: 2,
|
|
97
135
|
componentIdentity: COMPONENT_IDENTITY_BY_ID,
|
|
98
136
|
components: {},
|
|
137
|
+
groups: {},
|
|
138
|
+
roleGroups: {},
|
|
139
|
+
jobTemplates: {},
|
|
140
|
+
schedules: {},
|
|
141
|
+
events: {},
|
|
142
|
+
endpoints: {},
|
|
143
|
+
databaseSchemas: {},
|
|
144
|
+
databaseViews: {},
|
|
99
145
|
};
|
|
100
146
|
}
|
|
101
147
|
const raw = readJsonFile(lockPath);
|
|
@@ -115,20 +161,207 @@ export function readComponentLock(projectRoot) {
|
|
|
115
161
|
: {}),
|
|
116
162
|
componentId: String(entry.componentId || remoteComponentId),
|
|
117
163
|
remoteComponentId,
|
|
164
|
+
...(entry.environments && typeof entry.environments === 'object'
|
|
165
|
+
? {
|
|
166
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
167
|
+
.filter(([, baseline]) => (typeof baseline.remoteComponentId === 'string'
|
|
168
|
+
&& typeof baseline.remoteHash === 'string'
|
|
169
|
+
&& typeof baseline.syncedAt === 'string'))
|
|
170
|
+
.map(([envName, baseline]) => [envName, {
|
|
171
|
+
remoteComponentId: String(baseline.remoteComponentId),
|
|
172
|
+
remoteVersion: typeof baseline.remoteVersion === 'number' ? baseline.remoteVersion : null,
|
|
173
|
+
remoteHash: String(baseline.remoteHash),
|
|
174
|
+
syncedAt: String(baseline.syncedAt),
|
|
175
|
+
}])),
|
|
176
|
+
}
|
|
177
|
+
: {}),
|
|
118
178
|
}];
|
|
119
179
|
}));
|
|
120
180
|
return {
|
|
121
181
|
schemaVersion: 2,
|
|
122
182
|
componentIdentity,
|
|
123
183
|
components,
|
|
184
|
+
groups: Object.fromEntries(Object.entries(raw.groups && typeof raw.groups === 'object' ? raw.groups : {})
|
|
185
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
186
|
+
&& typeof entry.path === 'string'
|
|
187
|
+
&& typeof entry.sourceHash === 'string'
|
|
188
|
+
&& entry.environments
|
|
189
|
+
&& typeof entry.environments === 'object'))
|
|
190
|
+
.map(([key, entry]) => [key, {
|
|
191
|
+
stableKey: String(entry.stableKey),
|
|
192
|
+
path: String(entry.path),
|
|
193
|
+
sourceHash: String(entry.sourceHash),
|
|
194
|
+
...((isRecord(entry.owner) && entry.owner.type === 'group' && typeof entry.owner.stableKey === 'string')
|
|
195
|
+
? { owner: { type: 'group', stableKey: entry.owner.stableKey } }
|
|
196
|
+
: (isRecord(entry.owner) && entry.owner.type === 'manual-user')
|
|
197
|
+
? { owner: { type: 'manual-user' } }
|
|
198
|
+
: (isRecord(entry.owner) && entry.owner.type === 'unresolved' && typeof entry.owner.reason === 'string')
|
|
199
|
+
? { owner: { type: 'unresolved', reason: entry.owner.reason } }
|
|
200
|
+
: {}),
|
|
201
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
202
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
203
|
+
.map(([envName, baseline]) => [envName, {
|
|
204
|
+
remoteHash: String(baseline.remoteHash),
|
|
205
|
+
syncedAt: String(baseline.syncedAt),
|
|
206
|
+
}])),
|
|
207
|
+
}])),
|
|
208
|
+
roleGroups: Object.fromEntries(Object.entries(raw.roleGroups && typeof raw.roleGroups === 'object' ? raw.roleGroups : {})
|
|
209
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
210
|
+
&& typeof entry.path === 'string'
|
|
211
|
+
&& typeof entry.sourceHash === 'string'
|
|
212
|
+
&& entry.environments
|
|
213
|
+
&& typeof entry.environments === 'object'))
|
|
214
|
+
.map(([key, entry]) => [key, {
|
|
215
|
+
stableKey: String(entry.stableKey),
|
|
216
|
+
path: String(entry.path),
|
|
217
|
+
sourceHash: String(entry.sourceHash),
|
|
218
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
219
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
220
|
+
.map(([envName, baseline]) => [envName, {
|
|
221
|
+
remoteHash: String(baseline.remoteHash),
|
|
222
|
+
syncedAt: String(baseline.syncedAt),
|
|
223
|
+
}])),
|
|
224
|
+
}])),
|
|
225
|
+
jobTemplates: Object.fromEntries(Object.entries(raw.jobTemplates && typeof raw.jobTemplates === 'object' ? raw.jobTemplates : {})
|
|
226
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
227
|
+
&& typeof entry.path === 'string'
|
|
228
|
+
&& typeof entry.sourceHash === 'string'
|
|
229
|
+
&& entry.environments
|
|
230
|
+
&& typeof entry.environments === 'object'))
|
|
231
|
+
.map(([key, entry]) => [key, {
|
|
232
|
+
stableKey: String(entry.stableKey),
|
|
233
|
+
path: String(entry.path),
|
|
234
|
+
sourceHash: String(entry.sourceHash),
|
|
235
|
+
...((isRecord(entry.migration) && (entry.migration.componentBinding === 'pinned-unsupported'
|
|
236
|
+
|| entry.migration.executionPrincipal === 'excluded')) ? {
|
|
237
|
+
migration: {
|
|
238
|
+
...(entry.migration.componentBinding === 'pinned-unsupported' ? { componentBinding: 'pinned-unsupported' } : {}),
|
|
239
|
+
...(entry.migration.executionPrincipal === 'excluded' ? { executionPrincipal: 'excluded' } : {}),
|
|
240
|
+
},
|
|
241
|
+
} : {}),
|
|
242
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
243
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
244
|
+
.map(([envName, baseline]) => [envName, {
|
|
245
|
+
remoteHash: String(baseline.remoteHash),
|
|
246
|
+
syncedAt: String(baseline.syncedAt),
|
|
247
|
+
}])),
|
|
248
|
+
}])),
|
|
249
|
+
schedules: Object.fromEntries(Object.entries(raw.schedules && typeof raw.schedules === 'object' ? raw.schedules : {})
|
|
250
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
251
|
+
&& typeof entry.path === 'string'
|
|
252
|
+
&& typeof entry.sourceHash === 'string'
|
|
253
|
+
&& entry.environments
|
|
254
|
+
&& typeof entry.environments === 'object'))
|
|
255
|
+
.map(([key, entry]) => [key, {
|
|
256
|
+
stableKey: String(entry.stableKey),
|
|
257
|
+
path: String(entry.path),
|
|
258
|
+
sourceHash: String(entry.sourceHash),
|
|
259
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
260
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
261
|
+
.map(([envName, baseline]) => [envName, {
|
|
262
|
+
remoteHash: String(baseline.remoteHash),
|
|
263
|
+
syncedAt: String(baseline.syncedAt),
|
|
264
|
+
}])),
|
|
265
|
+
}])),
|
|
266
|
+
events: Object.fromEntries(Object.entries(raw.events && typeof raw.events === 'object' ? raw.events : {})
|
|
267
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
268
|
+
&& typeof entry.path === 'string'
|
|
269
|
+
&& typeof entry.sourceHash === 'string'
|
|
270
|
+
&& entry.environments
|
|
271
|
+
&& typeof entry.environments === 'object'))
|
|
272
|
+
.map(([key, entry]) => [key, {
|
|
273
|
+
stableKey: String(entry.stableKey),
|
|
274
|
+
path: String(entry.path),
|
|
275
|
+
sourceHash: String(entry.sourceHash),
|
|
276
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
277
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
278
|
+
.map(([envName, baseline]) => [envName, {
|
|
279
|
+
remoteHash: String(baseline.remoteHash),
|
|
280
|
+
syncedAt: String(baseline.syncedAt),
|
|
281
|
+
}])),
|
|
282
|
+
}])),
|
|
283
|
+
endpoints: Object.fromEntries(Object.entries(raw.endpoints && typeof raw.endpoints === 'object' ? raw.endpoints : {})
|
|
284
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
285
|
+
&& typeof entry.path === 'string'
|
|
286
|
+
&& typeof entry.sourceHash === 'string'
|
|
287
|
+
&& entry.environments
|
|
288
|
+
&& typeof entry.environments === 'object'))
|
|
289
|
+
.map(([key, entry]) => [key, {
|
|
290
|
+
stableKey: String(entry.stableKey),
|
|
291
|
+
path: String(entry.path),
|
|
292
|
+
sourceHash: String(entry.sourceHash),
|
|
293
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
294
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
295
|
+
.map(([envName, baseline]) => [envName, {
|
|
296
|
+
remoteHash: String(baseline.remoteHash),
|
|
297
|
+
syncedAt: String(baseline.syncedAt),
|
|
298
|
+
}])),
|
|
299
|
+
}])),
|
|
300
|
+
databaseSchemas: Object.fromEntries(Object.entries(raw.databaseSchemas && typeof raw.databaseSchemas === 'object' ? raw.databaseSchemas : {})
|
|
301
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
302
|
+
&& typeof entry.path === 'string'
|
|
303
|
+
&& typeof entry.sourceHash === 'string'
|
|
304
|
+
&& entry.environments
|
|
305
|
+
&& typeof entry.environments === 'object'))
|
|
306
|
+
.map(([key, entry]) => [key, {
|
|
307
|
+
stableKey: String(entry.stableKey),
|
|
308
|
+
path: String(entry.path),
|
|
309
|
+
sourceHash: String(entry.sourceHash),
|
|
310
|
+
...(typeof entry.sourceRestricted === 'boolean' ? { sourceRestricted: entry.sourceRestricted } : {}),
|
|
311
|
+
...(typeof entry.parentStableKey === 'string' ? { parentStableKey: entry.parentStableKey } : {}),
|
|
312
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
313
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string')
|
|
314
|
+
.map(([envName, baseline]) => [envName, {
|
|
315
|
+
remoteHash: String(baseline.remoteHash),
|
|
316
|
+
...(typeof baseline.tombstone?.deletedAt === 'string' && typeof baseline.tombstone.parentStableKey === 'string'
|
|
317
|
+
? { tombstone: {
|
|
318
|
+
deletedAt: baseline.tombstone.deletedAt,
|
|
319
|
+
parentStableKey: baseline.tombstone.parentStableKey,
|
|
320
|
+
} }
|
|
321
|
+
: {}),
|
|
322
|
+
}])),
|
|
323
|
+
}])),
|
|
324
|
+
databaseViews: Object.fromEntries(Object.entries(raw.databaseViews && typeof raw.databaseViews === 'object' ? raw.databaseViews : {})
|
|
325
|
+
.filter(([, entry]) => (typeof entry.stableKey === 'string'
|
|
326
|
+
&& typeof entry.path === 'string'
|
|
327
|
+
&& typeof entry.sourceHash === 'string'
|
|
328
|
+
&& entry.environments
|
|
329
|
+
&& typeof entry.environments === 'object'))
|
|
330
|
+
.map(([key, entry]) => [key, {
|
|
331
|
+
stableKey: String(entry.stableKey),
|
|
332
|
+
path: String(entry.path),
|
|
333
|
+
sourceHash: String(entry.sourceHash),
|
|
334
|
+
environments: Object.fromEntries(Object.entries(entry.environments)
|
|
335
|
+
.filter(([, baseline]) => typeof baseline.remoteHash === 'string' && typeof baseline.syncedAt === 'string')
|
|
336
|
+
.map(([envName, baseline]) => [envName, {
|
|
337
|
+
remoteHash: String(baseline.remoteHash),
|
|
338
|
+
syncedAt: String(baseline.syncedAt),
|
|
339
|
+
}])),
|
|
340
|
+
}])),
|
|
124
341
|
};
|
|
125
342
|
}
|
|
126
343
|
export function writeComponentLock(projectRoot, lock) {
|
|
127
344
|
const sortedComponents = Object.fromEntries(Object.entries(lock.components).sort(([left], [right]) => left.localeCompare(right)));
|
|
345
|
+
const sortedRoleGroups = Object.fromEntries(Object.entries(lock.roleGroups).sort(([left], [right]) => left.localeCompare(right)));
|
|
346
|
+
const sortedGroups = Object.fromEntries(Object.entries(lock.groups || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
347
|
+
const sortedJobTemplates = Object.fromEntries(Object.entries(lock.jobTemplates || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
348
|
+
const sortedSchedules = Object.fromEntries(Object.entries(lock.schedules || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
349
|
+
const sortedEvents = Object.fromEntries(Object.entries(lock.events || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
350
|
+
const sortedEndpoints = Object.fromEntries(Object.entries(lock.endpoints || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
351
|
+
const sortedDatabaseSchemas = Object.fromEntries(Object.entries(lock.databaseSchemas || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
352
|
+
const sortedDatabaseViews = Object.fromEntries(Object.entries(lock.databaseViews || {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
128
353
|
writeJsonFile(getLockPath(projectRoot), {
|
|
129
354
|
schemaVersion: 2,
|
|
130
355
|
componentIdentity: normalizeComponentIdentityConfig(lock.componentIdentity),
|
|
131
356
|
components: sortedComponents,
|
|
357
|
+
...(Object.keys(sortedGroups).length > 0 ? { groups: sortedGroups } : {}),
|
|
358
|
+
roleGroups: sortedRoleGroups,
|
|
359
|
+
jobTemplates: sortedJobTemplates,
|
|
360
|
+
...(Object.keys(sortedSchedules).length > 0 ? { schedules: sortedSchedules } : {}),
|
|
361
|
+
...(Object.keys(sortedEvents).length > 0 ? { events: sortedEvents } : {}),
|
|
362
|
+
...(Object.keys(sortedEndpoints).length > 0 ? { endpoints: sortedEndpoints } : {}),
|
|
363
|
+
...(Object.keys(sortedDatabaseSchemas).length > 0 ? { databaseSchemas: sortedDatabaseSchemas } : {}),
|
|
364
|
+
...(Object.keys(sortedDatabaseViews).length > 0 ? { databaseViews: sortedDatabaseViews } : {}),
|
|
132
365
|
});
|
|
133
366
|
}
|
|
134
367
|
export function componentLockKey(identity, component) {
|
|
@@ -137,14 +370,49 @@ export function componentLockKey(identity, component) {
|
|
|
137
370
|
export function componentLockMatchesIdentity(lock, identity) {
|
|
138
371
|
return sameComponentIdentityConfig(lock.componentIdentity, identity);
|
|
139
372
|
}
|
|
140
|
-
export function getComponentLockEntry(lock, identity, component) {
|
|
373
|
+
export function getComponentLockEntry(lock, identity, component, environmentName) {
|
|
141
374
|
if (!componentLockMatchesIdentity(lock, identity)) {
|
|
142
375
|
return null;
|
|
143
376
|
}
|
|
144
377
|
const key = componentLockKey(identity, component);
|
|
145
|
-
|
|
378
|
+
const entry = key ? lock.components[key] || null : null;
|
|
379
|
+
if (!entry || !environmentName) {
|
|
380
|
+
return entry;
|
|
381
|
+
}
|
|
382
|
+
const baseline = entry.environments?.[environmentName];
|
|
383
|
+
if (!baseline) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
...entry,
|
|
388
|
+
remoteComponentId: baseline.remoteComponentId,
|
|
389
|
+
remoteVersion: baseline.remoteVersion,
|
|
390
|
+
remoteHash: baseline.remoteHash,
|
|
391
|
+
pulledAt: baseline.syncedAt,
|
|
392
|
+
};
|
|
146
393
|
}
|
|
147
|
-
export function
|
|
394
|
+
export function getComponentLockEntries(lock, identity, environmentName) {
|
|
395
|
+
if (!componentLockMatchesIdentity(lock, identity)) {
|
|
396
|
+
return {};
|
|
397
|
+
}
|
|
398
|
+
if (!environmentName) {
|
|
399
|
+
return lock.components;
|
|
400
|
+
}
|
|
401
|
+
return Object.fromEntries(Object.entries(lock.components).flatMap(([key, entry]) => {
|
|
402
|
+
const baseline = entry.environments?.[environmentName];
|
|
403
|
+
if (!baseline) {
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
return [[key, {
|
|
407
|
+
...entry,
|
|
408
|
+
remoteComponentId: baseline.remoteComponentId,
|
|
409
|
+
remoteVersion: baseline.remoteVersion,
|
|
410
|
+
remoteHash: baseline.remoteHash,
|
|
411
|
+
pulledAt: baseline.syncedAt,
|
|
412
|
+
}]];
|
|
413
|
+
}));
|
|
414
|
+
}
|
|
415
|
+
export function upsertComponentLockEntry(projectRoot, entry, identity = COMPONENT_IDENTITY_BY_ID, environmentName) {
|
|
148
416
|
const normalizedIdentity = normalizeComponentIdentityConfig(identity);
|
|
149
417
|
const lock = readComponentLock(projectRoot);
|
|
150
418
|
if (!componentLockMatchesIdentity(lock, normalizedIdentity)) {
|
|
@@ -170,21 +438,42 @@ export function upsertComponentLockEntry(projectRoot, entry, identity = COMPONEN
|
|
|
170
438
|
: {}),
|
|
171
439
|
};
|
|
172
440
|
const existing = lock.components[key];
|
|
441
|
+
const storedEntry = environmentName
|
|
442
|
+
? {
|
|
443
|
+
...normalizedEntry,
|
|
444
|
+
remoteComponentId: existing?.remoteComponentId || normalizedEntry.remoteComponentId,
|
|
445
|
+
remoteVersion: existing?.remoteVersion ?? normalizedEntry.remoteVersion,
|
|
446
|
+
remoteHash: existing?.remoteHash || normalizedEntry.remoteHash,
|
|
447
|
+
environments: {
|
|
448
|
+
...existing?.environments,
|
|
449
|
+
[environmentName]: {
|
|
450
|
+
remoteComponentId: normalizedEntry.remoteComponentId,
|
|
451
|
+
remoteVersion: normalizedEntry.remoteVersion,
|
|
452
|
+
remoteHash: normalizedEntry.remoteHash,
|
|
453
|
+
syncedAt: normalizedEntry.pulledAt,
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
}
|
|
457
|
+
: {
|
|
458
|
+
...normalizedEntry,
|
|
459
|
+
...(existing?.environments ? { environments: existing.environments } : {}),
|
|
460
|
+
};
|
|
173
461
|
if (existing
|
|
174
|
-
&& existing.identityMode ===
|
|
175
|
-
&& existing.metadataProperty ===
|
|
176
|
-
&& existing.stableKey ===
|
|
177
|
-
&& existing.componentId ===
|
|
178
|
-
&& existing.remoteComponentId ===
|
|
179
|
-
&& existing.componentName ===
|
|
180
|
-
&& existing.category ===
|
|
181
|
-
&& existing.path ===
|
|
182
|
-
&& existing.remoteVersion ===
|
|
183
|
-
&& existing.remoteHash ===
|
|
184
|
-
&& existing.sourceHash ===
|
|
462
|
+
&& existing.identityMode === storedEntry.identityMode
|
|
463
|
+
&& existing.metadataProperty === storedEntry.metadataProperty
|
|
464
|
+
&& existing.stableKey === storedEntry.stableKey
|
|
465
|
+
&& existing.componentId === storedEntry.componentId
|
|
466
|
+
&& existing.remoteComponentId === storedEntry.remoteComponentId
|
|
467
|
+
&& existing.componentName === storedEntry.componentName
|
|
468
|
+
&& existing.category === storedEntry.category
|
|
469
|
+
&& existing.path === storedEntry.path
|
|
470
|
+
&& existing.remoteVersion === storedEntry.remoteVersion
|
|
471
|
+
&& existing.remoteHash === storedEntry.remoteHash
|
|
472
|
+
&& existing.sourceHash === storedEntry.sourceHash
|
|
473
|
+
&& JSON.stringify(existing.environments || {}) === JSON.stringify(storedEntry.environments || {})) {
|
|
185
474
|
return;
|
|
186
475
|
}
|
|
187
476
|
lock.componentIdentity = normalizedIdentity;
|
|
188
|
-
lock.components[key] =
|
|
477
|
+
lock.components[key] = storedEntry;
|
|
189
478
|
writeComponentLock(projectRoot, lock);
|
|
190
479
|
}
|
package/dist/src/config.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type RuntimeConfig = {
|
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
instance: string;
|
|
6
6
|
token: string;
|
|
7
|
+
production?: true;
|
|
7
8
|
};
|
|
8
9
|
export type RuntimeConfigOptions = {
|
|
9
10
|
baseUrl?: string;
|
|
@@ -15,10 +16,12 @@ export type StoredEnvironmentProfile = {
|
|
|
15
16
|
baseUrl: string;
|
|
16
17
|
token: string;
|
|
17
18
|
instance?: string;
|
|
19
|
+
production?: true;
|
|
18
20
|
};
|
|
19
21
|
type StoredConfig = Omit<RuntimeConfig, 'instance'> & {
|
|
20
22
|
instance?: string;
|
|
21
23
|
environments?: Record<string, StoredEnvironmentProfile>;
|
|
24
|
+
activeEnvironment?: string;
|
|
22
25
|
};
|
|
23
26
|
export type AuthValidationState = {
|
|
24
27
|
key: string;
|
|
@@ -54,14 +57,19 @@ export declare function loadStoredConfig(options?: {
|
|
|
54
57
|
}): StoredConfig;
|
|
55
58
|
export declare function listEnvironmentProfiles(): Record<string, StoredEnvironmentProfile>;
|
|
56
59
|
export declare function getEnvironmentProfile(name: string): StoredEnvironmentProfile | null;
|
|
57
|
-
export declare
|
|
60
|
+
export declare const DEFAULT_ENV_NAME = "default";
|
|
61
|
+
export declare function getActiveEnvironmentName(): string;
|
|
62
|
+
export declare function setActiveEnvironmentName(name: string): void;
|
|
63
|
+
export declare function saveEnvironmentProfile(name: string, profile: StoredEnvironmentProfile, options?: {
|
|
64
|
+
activate?: boolean;
|
|
65
|
+
}): void;
|
|
58
66
|
export declare function removeEnvironmentProfile(name: string): boolean;
|
|
59
67
|
export declare function buildAuthValidationKey(runtime: RuntimeConfigOptions): string;
|
|
60
68
|
export declare function readAuthValidationState(key?: string): AuthValidationState;
|
|
61
69
|
export declare function saveAuthValidationState(state: AuthValidationState): void;
|
|
62
70
|
export declare function clearAuthValidationState(key?: string): void;
|
|
63
|
-
export declare const DEFAULT_ENV_NAME = "default";
|
|
64
71
|
export declare function resolveRuntimeConfig(options?: RuntimeConfigOptions): {
|
|
72
|
+
production?: true | undefined;
|
|
65
73
|
baseUrl: string;
|
|
66
74
|
instance: string;
|
|
67
75
|
token: string;
|
package/dist/src/config.js
CHANGED
|
@@ -75,11 +75,13 @@ export function saveStoredConfig(nextConfig) {
|
|
|
75
75
|
const current = readStoredConfigFile();
|
|
76
76
|
const instance = nextConfig.instance || current.instance || '';
|
|
77
77
|
const environments = nextConfig.environments ?? current.environments;
|
|
78
|
+
const activeEnvironment = nextConfig.activeEnvironment ?? current.activeEnvironment;
|
|
78
79
|
const config = {
|
|
79
80
|
baseUrl: nextConfig.baseUrl || current.baseUrl || DEFAULT_BASE_URL,
|
|
80
81
|
token: nextConfig.token || current.token || '',
|
|
81
82
|
...(instance ? { instance } : {}),
|
|
82
83
|
...(environments && Object.keys(environments).length > 0 ? { environments } : {}),
|
|
84
|
+
...(activeEnvironment && activeEnvironment !== DEFAULT_ENV_NAME ? { activeEnvironment } : {}),
|
|
83
85
|
};
|
|
84
86
|
writeJsonFile(configFile, config);
|
|
85
87
|
removeFileIfExists(credentialsFile);
|
|
@@ -88,13 +90,15 @@ export function clearStoredConfig() {
|
|
|
88
90
|
const { dir, configFile, credentialsFile, authStateFile } = getConfigPaths();
|
|
89
91
|
// Logout clears the default login but must not destroy named environment
|
|
90
92
|
// profiles stored alongside it.
|
|
91
|
-
const
|
|
93
|
+
const stored = readStoredConfigFile();
|
|
94
|
+
const environments = stored.environments;
|
|
92
95
|
if (environments && Object.keys(environments).length > 0) {
|
|
93
96
|
ensureDirectory(dir);
|
|
94
97
|
writeJsonFile(configFile, {
|
|
95
98
|
baseUrl: DEFAULT_BASE_URL,
|
|
96
99
|
token: '',
|
|
97
100
|
environments,
|
|
101
|
+
...(stored.activeEnvironment ? { activeEnvironment: stored.activeEnvironment } : {}),
|
|
98
102
|
});
|
|
99
103
|
for (const filePath of [credentialsFile, authStateFile]) {
|
|
100
104
|
try {
|
|
@@ -181,6 +185,7 @@ function parseEnvironmentProfiles(value) {
|
|
|
181
185
|
baseUrl: entry.baseUrl,
|
|
182
186
|
token: entry.token,
|
|
183
187
|
...(typeof entry.instance === 'string' && isUuid(entry.instance) ? { instance: entry.instance } : {}),
|
|
188
|
+
...(entry.production === true ? { production: true } : {}),
|
|
184
189
|
};
|
|
185
190
|
}
|
|
186
191
|
return Object.keys(environments).length > 0 ? environments : null;
|
|
@@ -195,6 +200,11 @@ function parseFlatConfig(raw, credentials) {
|
|
|
195
200
|
? credentials.token
|
|
196
201
|
: '';
|
|
197
202
|
const environments = parseEnvironmentProfiles(raw.environments);
|
|
203
|
+
const activeEnvironment = typeof raw.activeEnvironment === 'string'
|
|
204
|
+
&& raw.activeEnvironment !== DEFAULT_ENV_NAME
|
|
205
|
+
&& environments?.[raw.activeEnvironment]
|
|
206
|
+
? raw.activeEnvironment
|
|
207
|
+
: undefined;
|
|
198
208
|
return {
|
|
199
209
|
baseUrl: typeof raw.baseUrl === 'string'
|
|
200
210
|
? raw.baseUrl
|
|
@@ -203,6 +213,7 @@ function parseFlatConfig(raw, credentials) {
|
|
|
203
213
|
: DEFAULT_BASE_URL,
|
|
204
214
|
...(typeof raw.instance === 'string' && isUuid(raw.instance) ? { instance: raw.instance } : {}),
|
|
205
215
|
...(environments ? { environments } : {}),
|
|
216
|
+
...(activeEnvironment ? { activeEnvironment } : {}),
|
|
206
217
|
token,
|
|
207
218
|
};
|
|
208
219
|
}
|
|
@@ -225,6 +236,11 @@ function parseMappedConfig(raw) {
|
|
|
225
236
|
? configuredDefault
|
|
226
237
|
: Object.keys(instances)[0] || '';
|
|
227
238
|
const environments = parseEnvironmentProfiles(raw.environments);
|
|
239
|
+
const activeEnvironment = typeof raw.activeEnvironment === 'string'
|
|
240
|
+
&& raw.activeEnvironment !== DEFAULT_ENV_NAME
|
|
241
|
+
&& environments?.[raw.activeEnvironment]
|
|
242
|
+
? raw.activeEnvironment
|
|
243
|
+
: undefined;
|
|
228
244
|
const selected = defaultInstance ? instances[defaultInstance] : undefined;
|
|
229
245
|
const base = selected
|
|
230
246
|
? {
|
|
@@ -233,7 +249,11 @@ function parseMappedConfig(raw) {
|
|
|
233
249
|
token: selected.token,
|
|
234
250
|
}
|
|
235
251
|
: emptyStoredConfigFile();
|
|
236
|
-
return
|
|
252
|
+
return {
|
|
253
|
+
...base,
|
|
254
|
+
...(environments ? { environments } : {}),
|
|
255
|
+
...(activeEnvironment ? { activeEnvironment } : {}),
|
|
256
|
+
};
|
|
237
257
|
}
|
|
238
258
|
function parseLegacyConfig(config, credentials) {
|
|
239
259
|
const baseUrl = isRecord(config) && typeof config.baseUrl === 'string'
|
|
@@ -319,9 +339,19 @@ export function listEnvironmentProfiles() {
|
|
|
319
339
|
export function getEnvironmentProfile(name) {
|
|
320
340
|
return listEnvironmentProfiles()[name] || null;
|
|
321
341
|
}
|
|
322
|
-
export
|
|
342
|
+
export const DEFAULT_ENV_NAME = 'default';
|
|
343
|
+
export function getActiveEnvironmentName() {
|
|
344
|
+
return readStoredConfigFile().activeEnvironment || DEFAULT_ENV_NAME;
|
|
345
|
+
}
|
|
346
|
+
export function setActiveEnvironmentName(name) {
|
|
347
|
+
if (name !== DEFAULT_ENV_NAME && !getEnvironmentProfile(name)) {
|
|
348
|
+
throw new Error(`Unknown project "${name}". Run 'revo projects list' to see saved projects.`);
|
|
349
|
+
}
|
|
350
|
+
saveStoredConfig({ activeEnvironment: name });
|
|
351
|
+
}
|
|
352
|
+
export function saveEnvironmentProfile(name, profile, options = {}) {
|
|
323
353
|
const environments = { ...listEnvironmentProfiles(), [name]: profile };
|
|
324
|
-
saveStoredConfig({ environments });
|
|
354
|
+
saveStoredConfig({ environments, ...(options.activate ? { activeEnvironment: name } : {}) });
|
|
325
355
|
}
|
|
326
356
|
export function removeEnvironmentProfile(name) {
|
|
327
357
|
const environments = listEnvironmentProfiles();
|
|
@@ -329,7 +359,10 @@ export function removeEnvironmentProfile(name) {
|
|
|
329
359
|
return false;
|
|
330
360
|
}
|
|
331
361
|
delete environments[name];
|
|
332
|
-
saveStoredConfig({
|
|
362
|
+
saveStoredConfig({
|
|
363
|
+
environments,
|
|
364
|
+
...(getActiveEnvironmentName() === name ? { activeEnvironment: DEFAULT_ENV_NAME } : {}),
|
|
365
|
+
});
|
|
333
366
|
return true;
|
|
334
367
|
}
|
|
335
368
|
export function buildAuthValidationKey(runtime) {
|
|
@@ -422,31 +455,33 @@ export function clearAuthValidationState(key) {
|
|
|
422
455
|
}
|
|
423
456
|
}
|
|
424
457
|
}
|
|
425
|
-
export const DEFAULT_ENV_NAME = 'default';
|
|
426
458
|
export function resolveRuntimeConfig(options = {}) {
|
|
427
459
|
const env = {
|
|
428
460
|
baseUrl: resolveEnvValue(['REVO_URL', 'REVO_BASE_URL', 'REVOENGINE_URL', 'REVOENGINE_BASE_URL']),
|
|
429
461
|
instance: resolveEnvValue(['REVO_INSTANCE', 'REVOENGINE_INSTANCE']),
|
|
430
462
|
token: resolveEnvValue(['REVO_TOKEN', 'REVO_API_KEY', 'REVOENGINE_TOKEN', 'REVOENGINE_API_KEY']),
|
|
431
463
|
};
|
|
432
|
-
const stored =
|
|
464
|
+
const stored = readStoredConfigFile();
|
|
433
465
|
// A named profile is an explicit selection and resolves exclusively: no
|
|
434
466
|
// fallback to env vars or the stored default, or --from/--to could silently
|
|
435
467
|
// mix one environment's URL with another environment's token or instance.
|
|
436
|
-
|
|
437
|
-
|
|
468
|
+
const envName = options.envName || stored.activeEnvironment || DEFAULT_ENV_NAME;
|
|
469
|
+
if (envName !== DEFAULT_ENV_NAME) {
|
|
470
|
+
const profile = stored.environments?.[envName];
|
|
438
471
|
if (!profile) {
|
|
439
|
-
throw new Error(`Unknown
|
|
472
|
+
throw new Error(`Unknown project "${envName}". Run 'revo projects list' to see saved projects.`);
|
|
440
473
|
}
|
|
441
474
|
return {
|
|
442
475
|
baseUrl: options.baseUrl || profile.baseUrl || DEFAULT_BASE_URL,
|
|
443
|
-
instance: options.instance
|
|
476
|
+
instance: options.instance ?? (profile.instance || ''),
|
|
444
477
|
token: options.token || profile.token || '',
|
|
478
|
+
...(profile.production ? { production: true } : {}),
|
|
445
479
|
};
|
|
446
480
|
}
|
|
481
|
+
const defaultStored = mergeWithLegacy(stored);
|
|
447
482
|
return {
|
|
448
|
-
baseUrl: options.baseUrl || env.baseUrl ||
|
|
449
|
-
instance: options.instance
|
|
450
|
-
token: options.token || env.token ||
|
|
483
|
+
baseUrl: options.baseUrl || env.baseUrl || defaultStored.baseUrl || DEFAULT_BASE_URL,
|
|
484
|
+
instance: options.instance ?? (env.instance || defaultStored.instance || ''),
|
|
485
|
+
token: options.token || env.token || defaultStored.token || '',
|
|
451
486
|
};
|
|
452
487
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const DATABASE_BACKUP_CATEGORY = "Database Backup";
|
|
2
|
+
export declare function isDatabaseBackupArtifact(value: {
|
|
3
|
+
category?: unknown;
|
|
4
|
+
}): boolean;
|
|
5
|
+
export declare function excludeDatabaseBackupArtifacts<T extends {
|
|
6
|
+
category?: unknown;
|
|
7
|
+
}>(records: readonly T[]): T[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const DATABASE_BACKUP_CATEGORY = 'Database Backup';
|
|
2
|
+
export function isDatabaseBackupArtifact(value) {
|
|
3
|
+
return typeof value.category === 'string'
|
|
4
|
+
&& value.category.trim() === DATABASE_BACKUP_CATEGORY;
|
|
5
|
+
}
|
|
6
|
+
export function excludeDatabaseBackupArtifacts(records) {
|
|
7
|
+
return records.filter((record) => !isDatabaseBackupArtifact(record));
|
|
8
|
+
}
|
package/dist/src/env-sync.d.ts
CHANGED
|
@@ -69,8 +69,6 @@ export declare function buildStableKey(component: ComponentRecord, stableKeyName
|
|
|
69
69
|
export declare function captureEnvSnapshot(client: RevoClient, envName: string, options?: {
|
|
70
70
|
stableKeyName?: string;
|
|
71
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
72
|
export declare function buildEnvSyncPlan(source: EnvSnapshot, target: EnvSnapshot, options: {
|
|
75
73
|
prune: boolean;
|
|
76
74
|
scope?: EnvSyncScope;
|
|
@@ -83,4 +81,3 @@ export declare function applyEnvSyncPlan(input: {
|
|
|
83
81
|
stableKeyName?: string;
|
|
84
82
|
onItem?: (result: EnvApplyItemResult) => void;
|
|
85
83
|
}): Promise<EnvApplyItemResult[]>;
|
|
86
|
-
export {};
|