@revoengine/cli 1.0.9 → 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 +464 -8
- package/dist/src/cli.js +65 -6
- package/dist/src/client.d.ts +366 -5
- package/dist/src/client.js +954 -13
- package/dist/src/commands/auth.js +4 -2
- package/dist/src/commands/component.js +839 -412
- 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.d.ts +2 -0
- package/dist/src/commands/env.js +380 -0
- 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 +10 -0
- package/dist/src/commands/index.js +10 -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.d.ts +2 -0
- package/dist/src/commands/metadata.js +159 -0
- package/dist/src/commands/project.js +82 -1
- 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 +126 -2
- package/dist/src/component-lock.js +378 -15
- package/dist/src/config.d.ts +20 -0
- package/dist/src/config.js +121 -6
- 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 +83 -0
- package/dist/src/env-sync.js +315 -0
- package/dist/src/metadata-backfill.d.ts +56 -0
- package/dist/src/metadata-backfill.js +1176 -0
- package/dist/src/project.d.ts +17 -9
- package/dist/src/project.js +87 -11
- package/dist/src/prompt.js +10 -18
- package/dist/src/resource-metadata.d.ts +25 -0
- package/dist/src/resource-metadata.js +132 -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 +7 -0
- package/dist/src/tracked-resources.js +61 -0
- package/dist/src/types.d.ts +227 -0
- package/dist/src/ui.d.ts +3 -0
- package/dist/src/ui.js +68 -10
- 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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
export declare function unwrapData<T>(value: unknown): T;
|
|
3
|
+
export declare function requireProjectLayout(cwd: string, resourceLabel: string): {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
workspaceRoot: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function stableKeySuffix(stableKey: string): string;
|
|
8
|
+
export declare function findManifestFiles(directory: string, manifestFileName: string): string[];
|
|
9
|
+
export declare function writeManifestIfChanged(filePath: string, manifest: unknown): boolean;
|
|
10
|
+
export declare function writePulledManifest(input: {
|
|
11
|
+
filePath: string;
|
|
12
|
+
manifest: unknown;
|
|
13
|
+
previousPath?: string;
|
|
14
|
+
root: string;
|
|
15
|
+
force?: boolean;
|
|
16
|
+
}): void;
|
|
17
|
+
export declare function removeRelocatedManifest(filePath: string, root: string): void;
|
|
18
|
+
export declare function localTargetPath(cwd: string, manifestPath: string): string;
|
|
19
|
+
export declare function toPortableRelativePath(root: string, filePath: string): string;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { hashStable } from "../component-lock.js";
|
|
4
|
+
import { resolveProjectRoot, resolveProjectWorkspace } from "../project.js";
|
|
5
|
+
import { sanitizeSegment, writeJsonFile } from "../utils.js";
|
|
6
|
+
import { replaceWorkspaceResourceDirectory } from "../workspace-resource.js";
|
|
7
|
+
export function isRecord(value) {
|
|
8
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
export function unwrapData(value) {
|
|
11
|
+
return isRecord(value) && isRecord(value.data) ? value.data : value;
|
|
12
|
+
}
|
|
13
|
+
export function requireProjectLayout(cwd, resourceLabel) {
|
|
14
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
15
|
+
const workspaceRoot = resolveProjectWorkspace(cwd);
|
|
16
|
+
if (!projectRoot || !workspaceRoot) {
|
|
17
|
+
throw new Error(`${resourceLabel} sync requires a configured Revo project. Run \`revo project init [path]\` first.`);
|
|
18
|
+
}
|
|
19
|
+
return { projectRoot, workspaceRoot };
|
|
20
|
+
}
|
|
21
|
+
export function stableKeySuffix(stableKey) {
|
|
22
|
+
const suffix = stableKey.includes('#') ? stableKey.slice(stableKey.lastIndexOf('#') + 1) : '';
|
|
23
|
+
return sanitizeSegment(suffix || hashStable(stableKey).slice(0, 8));
|
|
24
|
+
}
|
|
25
|
+
export function findManifestFiles(directory, manifestFileName) {
|
|
26
|
+
if (!fs.existsSync(directory))
|
|
27
|
+
return [];
|
|
28
|
+
const files = [];
|
|
29
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
30
|
+
const entryPath = path.join(directory, entry.name);
|
|
31
|
+
if (entry.isDirectory())
|
|
32
|
+
files.push(...findManifestFiles(entryPath, manifestFileName));
|
|
33
|
+
else if (entry.isFile() && entry.name === manifestFileName)
|
|
34
|
+
files.push(entryPath);
|
|
35
|
+
}
|
|
36
|
+
return files.sort((left, right) => left.localeCompare(right));
|
|
37
|
+
}
|
|
38
|
+
export function writeManifestIfChanged(filePath, manifest) {
|
|
39
|
+
const serialized = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
40
|
+
if (fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === serialized)
|
|
41
|
+
return false;
|
|
42
|
+
writeJsonFile(filePath, manifest);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
// Nested manifests belong to separate resources (notably DB partitions).
|
|
46
|
+
// Preserve those directories until their own pull or explicit prune handles them.
|
|
47
|
+
function copyNestedResourceDirectories(source, target, manifestName) {
|
|
48
|
+
if (!fs.existsSync(source)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
52
|
+
if (!entry.isDirectory()) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const sourceDirectory = path.join(source, entry.name);
|
|
56
|
+
const targetDirectory = path.join(target, entry.name);
|
|
57
|
+
if (fs.existsSync(path.join(sourceDirectory, manifestName))) {
|
|
58
|
+
fs.cpSync(sourceDirectory, targetDirectory, { recursive: true, dereference: false });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
copyNestedResourceDirectories(sourceDirectory, targetDirectory, manifestName);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function writePulledManifest(input) {
|
|
66
|
+
const directory = path.dirname(input.filePath);
|
|
67
|
+
const manifestName = path.basename(input.filePath);
|
|
68
|
+
if (input.force) {
|
|
69
|
+
const serialized = `${JSON.stringify(input.manifest, null, 2)}\n`;
|
|
70
|
+
replaceWorkspaceResourceDirectory(input.root, directory, (staged) => {
|
|
71
|
+
copyNestedResourceDirectories(directory, staged, manifestName);
|
|
72
|
+
fs.writeFileSync(path.join(staged, manifestName), serialized);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
writeManifestIfChanged(input.filePath, input.manifest);
|
|
77
|
+
}
|
|
78
|
+
if (!input.previousPath || input.previousPath === input.filePath || !fs.existsSync(input.previousPath)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const previousDirectory = path.dirname(input.previousPath);
|
|
82
|
+
const previousStat = fs.statSync(previousDirectory);
|
|
83
|
+
const currentStat = fs.statSync(directory);
|
|
84
|
+
if (previousStat.dev === currentStat.dev && previousStat.ino === currentStat.ino) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (input.force) {
|
|
88
|
+
replaceWorkspaceResourceDirectory(input.root, previousDirectory, (staged) => {
|
|
89
|
+
copyNestedResourceDirectories(previousDirectory, staged, manifestName);
|
|
90
|
+
});
|
|
91
|
+
removeEmptyResourceParents(previousDirectory, input.root);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
removeRelocatedManifest(input.previousPath, input.root);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function removeEmptyResourceParents(directory, root) {
|
|
98
|
+
const resolvedRoot = path.resolve(root);
|
|
99
|
+
let current = path.resolve(directory);
|
|
100
|
+
while (current.startsWith(`${resolvedRoot}${path.sep}`) && fs.readdirSync(current).length === 0) {
|
|
101
|
+
fs.rmdirSync(current);
|
|
102
|
+
current = path.dirname(current);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function removeRelocatedManifest(filePath, root) {
|
|
106
|
+
if (!fs.existsSync(filePath))
|
|
107
|
+
return;
|
|
108
|
+
fs.unlinkSync(filePath);
|
|
109
|
+
removeEmptyResourceParents(path.dirname(filePath), root);
|
|
110
|
+
}
|
|
111
|
+
export function localTargetPath(cwd, manifestPath) {
|
|
112
|
+
return path.relative(cwd, path.dirname(manifestPath)) || '.';
|
|
113
|
+
}
|
|
114
|
+
export function toPortableRelativePath(root, filePath) {
|
|
115
|
+
return path.relative(root, filePath).split(path.sep).join('/');
|
|
116
|
+
}
|
|
@@ -10,6 +10,7 @@ export type RuntimeViewModel = {
|
|
|
10
10
|
nodeVersion: string;
|
|
11
11
|
configDir: string;
|
|
12
12
|
runtime: ReturnType<typeof resolveRuntimeConfig>;
|
|
13
|
+
environmentName: string;
|
|
13
14
|
auth: AuthViewState;
|
|
14
15
|
};
|
|
15
16
|
export declare function buildRuntimeViewModel(context: CommandContext, options?: {
|
package/dist/src/runtime-view.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { getConfigDir, resolveRuntimeConfig } from "./config.js";
|
|
1
|
+
import { getActiveEnvironmentName, getConfigDir, resolveRuntimeConfig } from "./config.js";
|
|
2
2
|
import { AuthenticationError } from "./client.js";
|
|
3
3
|
function resolveArgsRuntime(context) {
|
|
4
4
|
return resolveRuntimeConfig({
|
|
5
|
+
envName: typeof context.args.env === 'string' ? context.args.env : undefined,
|
|
5
6
|
baseUrl: typeof context.args.url === 'string'
|
|
6
7
|
? context.args.url
|
|
7
8
|
: typeof context.args.baseUrl === 'string'
|
|
@@ -44,6 +45,9 @@ function extractEmail(profile) {
|
|
|
44
45
|
}
|
|
45
46
|
export async function buildRuntimeViewModel(context, options = {}) {
|
|
46
47
|
const runtime = resolveArgsRuntime(context);
|
|
48
|
+
const environmentName = typeof context.args.env === 'string'
|
|
49
|
+
? context.args.env
|
|
50
|
+
: getActiveEnvironmentName();
|
|
47
51
|
const auth = {
|
|
48
52
|
status: 'not_authenticated',
|
|
49
53
|
validation: null,
|
|
@@ -75,6 +79,7 @@ export async function buildRuntimeViewModel(context, options = {}) {
|
|
|
75
79
|
nodeVersion: process.version,
|
|
76
80
|
configDir: getConfigDir(),
|
|
77
81
|
runtime,
|
|
82
|
+
environmentName,
|
|
78
83
|
auth,
|
|
79
84
|
};
|
|
80
85
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CommandContext } from './types.ts';
|
|
2
|
+
export type WorkspaceSyncStatus = 'pulled' | 'pruned' | 'deployed' | 'skipped' | 'failed' | 'write-unverified';
|
|
3
|
+
export type WorkspaceSyncResult = {
|
|
4
|
+
status: WorkspaceSyncStatus;
|
|
5
|
+
targetPath: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
};
|
|
8
|
+
export type SyncOutputStatus = 'Pulled' | 'Pruned' | 'Skipped' | 'Deployed' | 'Activated' | 'Deactivated' | 'Failed' | 'Unverified';
|
|
9
|
+
export type SyncPlanDetail = {
|
|
10
|
+
label: string;
|
|
11
|
+
values: string | ReadonlyArray<string>;
|
|
12
|
+
};
|
|
13
|
+
export type SyncPlanItem = {
|
|
14
|
+
status: string;
|
|
15
|
+
stableKey?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
target?: string;
|
|
18
|
+
reason?: string;
|
|
19
|
+
changedFields?: ReadonlyArray<string>;
|
|
20
|
+
details?: ReadonlyArray<SyncPlanDetail>;
|
|
21
|
+
};
|
|
22
|
+
export type SyncPlanOutput = {
|
|
23
|
+
title: string;
|
|
24
|
+
environment: string;
|
|
25
|
+
counts: Readonly<Record<string, number>>;
|
|
26
|
+
items: ReadonlyArray<SyncPlanItem>;
|
|
27
|
+
warnings?: ReadonlyArray<string>;
|
|
28
|
+
blockers?: ReadonlyArray<string>;
|
|
29
|
+
};
|
|
30
|
+
export declare function printSyncPlan(println: CommandContext['println'], input: SyncPlanOutput): void;
|
|
31
|
+
export declare function printSyncStatus(println: CommandContext['println'], input: {
|
|
32
|
+
status: SyncOutputStatus;
|
|
33
|
+
direction: 'pull' | 'push';
|
|
34
|
+
targetPath: string;
|
|
35
|
+
reason?: string;
|
|
36
|
+
}): void;
|
|
37
|
+
export declare function printSyncNotice(println: CommandContext['println'], message: string): void;
|
|
38
|
+
export declare function printSyncSummary(println: CommandContext['println'], label: 'Pulled' | 'Deployed' | 'Activated', results: ReadonlyArray<Pick<WorkspaceSyncResult, 'status'>>, durationMs: number): void;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const ANSI = {
|
|
2
|
+
reset: '\u001b[0m',
|
|
3
|
+
bold: '\u001b[1m',
|
|
4
|
+
dim: '\u001b[2m',
|
|
5
|
+
red: '\u001b[31m',
|
|
6
|
+
green: '\u001b[32m',
|
|
7
|
+
yellow: '\u001b[33m',
|
|
8
|
+
cyan: '\u001b[36m',
|
|
9
|
+
blue: '\u001b[34m',
|
|
10
|
+
magenta: '\u001b[35m',
|
|
11
|
+
};
|
|
12
|
+
function supportsColor() {
|
|
13
|
+
return Boolean(process.stdout.isTTY || process.env.FORCE_COLOR);
|
|
14
|
+
}
|
|
15
|
+
function paint(text, code) {
|
|
16
|
+
return supportsColor() ? `${code}${text}${ANSI.reset}` : text;
|
|
17
|
+
}
|
|
18
|
+
function paintStatus(status) {
|
|
19
|
+
if (status === 'Skipped' || status === 'Pruned') {
|
|
20
|
+
return paint(status.padEnd(9), ANSI.bold + ANSI.yellow);
|
|
21
|
+
}
|
|
22
|
+
if (status === 'Failed') {
|
|
23
|
+
return paint(status.padEnd(9), ANSI.bold + ANSI.red);
|
|
24
|
+
}
|
|
25
|
+
if (status === 'Unverified') {
|
|
26
|
+
return paint(status.padEnd(9), ANSI.bold + ANSI.magenta);
|
|
27
|
+
}
|
|
28
|
+
if (status === 'Notice') {
|
|
29
|
+
return paint(status.padEnd(9), ANSI.bold + ANSI.magenta);
|
|
30
|
+
}
|
|
31
|
+
return paint(status.padEnd(9), ANSI.bold + ANSI.green);
|
|
32
|
+
}
|
|
33
|
+
function formatDuration(durationMs) {
|
|
34
|
+
if (durationMs < 1_000) {
|
|
35
|
+
return `${durationMs}ms`;
|
|
36
|
+
}
|
|
37
|
+
const seconds = durationMs / 1_000;
|
|
38
|
+
return seconds < 10 ? `${seconds.toFixed(1)}s` : `${Math.round(seconds)}s`;
|
|
39
|
+
}
|
|
40
|
+
const PLAN_STATUS_MARKS = {
|
|
41
|
+
clean: { symbol: '=', code: ANSI.green },
|
|
42
|
+
create: { symbol: '+', code: ANSI.bold + ANSI.green },
|
|
43
|
+
restore: { symbol: '+', code: ANSI.bold + ANSI.yellow },
|
|
44
|
+
'restore-blocked': { symbol: '!', code: ANSI.bold + ANSI.red },
|
|
45
|
+
'manual-create': { symbol: '+', code: ANSI.bold + ANSI.yellow },
|
|
46
|
+
'manual-owner': { symbol: '-', code: ANSI.bold + ANSI.yellow },
|
|
47
|
+
'local-changed': { symbol: '~', code: ANSI.bold + ANSI.yellow },
|
|
48
|
+
'safe-update': { symbol: '~', code: ANSI.bold + ANSI.yellow },
|
|
49
|
+
'remote-changed': { symbol: '>', code: ANSI.bold + ANSI.magenta },
|
|
50
|
+
conflict: { symbol: '!', code: ANSI.bold + ANSI.red },
|
|
51
|
+
'missing-lock': { symbol: '?', code: ANSI.bold + ANSI.red },
|
|
52
|
+
reconciled: { symbol: '=', code: ANSI.bold + ANSI.cyan },
|
|
53
|
+
'manual-schema-migration-required': { symbol: '!', code: ANSI.bold + ANSI.red },
|
|
54
|
+
'access-policy-required': { symbol: '!', code: ANSI.bold + ANSI.yellow },
|
|
55
|
+
'activation-drift': { symbol: '^', code: ANSI.bold + ANSI.yellow },
|
|
56
|
+
orphan: { symbol: 'o', code: ANSI.dim + ANSI.cyan },
|
|
57
|
+
'orphan-root': { symbol: 'o', code: ANSI.dim + ANSI.cyan },
|
|
58
|
+
'orphan-partition': { symbol: 'o', code: ANSI.dim + ANSI.cyan },
|
|
59
|
+
unmanaged: { symbol: '-', code: ANSI.dim + ANSI.yellow },
|
|
60
|
+
collision: { symbol: '!', code: ANSI.bold + ANSI.red },
|
|
61
|
+
skipped: { symbol: '-', code: ANSI.dim + ANSI.yellow },
|
|
62
|
+
delete: { symbol: '-', code: ANSI.bold + ANSI.red },
|
|
63
|
+
'delete-partition': { symbol: '-', code: ANSI.bold + ANSI.red },
|
|
64
|
+
'previously-deleted-partition': { symbol: '!', code: ANSI.bold + ANSI.red },
|
|
65
|
+
'replacement-requires-two-phases': { symbol: '!', code: ANSI.bold + ANSI.yellow },
|
|
66
|
+
};
|
|
67
|
+
function planDetailValue(values) {
|
|
68
|
+
return typeof values === 'string' ? values : values.join(', ');
|
|
69
|
+
}
|
|
70
|
+
function printPlanSection(println, label, messages) {
|
|
71
|
+
if (messages.length === 0) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
println('');
|
|
75
|
+
const code = label === 'Blockers' ? ANSI.bold + ANSI.red : ANSI.bold + ANSI.yellow;
|
|
76
|
+
println(paint(`${label} (${messages.length})`, code));
|
|
77
|
+
for (const message of messages)
|
|
78
|
+
println(` ${paint('!', code)} ${message}`);
|
|
79
|
+
}
|
|
80
|
+
export function printSyncPlan(println, input) {
|
|
81
|
+
println(`${paint(`${input.title} plan`, ANSI.bold)} for ${paint(input.environment, ANSI.bold + ANSI.cyan)}`);
|
|
82
|
+
for (const item of input.items) {
|
|
83
|
+
const mark = PLAN_STATUS_MARKS[item.status] || { symbol: '-', code: ANSI.dim };
|
|
84
|
+
const target = item.target || item.stableKey || item.name || 'unknown';
|
|
85
|
+
println(`${paint(mark.symbol, mark.code)} ${paint(item.status.padEnd(14), mark.code)} ${paint(target, ANSI.cyan)}`);
|
|
86
|
+
const details = [
|
|
87
|
+
...(item.reason ? [{ label: 'reason', values: item.reason }] : []),
|
|
88
|
+
...(item.changedFields?.length ? [{ label: 'fields', values: item.changedFields }] : []),
|
|
89
|
+
...(item.details || []),
|
|
90
|
+
];
|
|
91
|
+
for (const detail of details) {
|
|
92
|
+
const value = planDetailValue(detail.values);
|
|
93
|
+
if (value) {
|
|
94
|
+
println(` ${paint(detail.label.padEnd(14), ANSI.dim)} ${value}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const summary = Object.entries(input.counts)
|
|
99
|
+
.filter(([, count]) => count > 0)
|
|
100
|
+
.map(([status, count]) => `${count} ${status}`);
|
|
101
|
+
println(`${paint('Summary', ANSI.bold)} ${summary.length > 0 ? summary.join(', ') : 'no resources'}`);
|
|
102
|
+
printPlanSection(println, 'Warnings', input.warnings || []);
|
|
103
|
+
printPlanSection(println, 'Blockers', input.blockers || []);
|
|
104
|
+
}
|
|
105
|
+
export function printSyncStatus(println, input) {
|
|
106
|
+
const engine = paint('RevoEngine', ANSI.bold + ANSI.blue);
|
|
107
|
+
const arrow = paint(input.direction === 'pull' ? '->' : '<-', ANSI.bold + ANSI.magenta);
|
|
108
|
+
const targetPath = paint(input.targetPath, ANSI.cyan);
|
|
109
|
+
const reason = input.reason ? ` ${paint(`(${input.reason})`, ANSI.dim + ANSI.yellow)}` : '';
|
|
110
|
+
println(`${paintStatus(input.status)} ${engine} ${arrow} ${targetPath}${reason}`);
|
|
111
|
+
}
|
|
112
|
+
export function printSyncNotice(println, message) {
|
|
113
|
+
println(`${paintStatus('Notice')} ${message}`);
|
|
114
|
+
}
|
|
115
|
+
export function printSyncSummary(println, label, results, durationMs) {
|
|
116
|
+
const total = results.length;
|
|
117
|
+
const primaryStatus = label === 'Activated' ? 'deployed' : label.toLowerCase();
|
|
118
|
+
const primary = results.filter((result) => result.status === primaryStatus).length;
|
|
119
|
+
const skipped = results.filter((result) => result.status === 'skipped').length;
|
|
120
|
+
const pruned = results.filter((result) => result.status === 'pruned').length;
|
|
121
|
+
const failed = results.filter((result) => result.status === 'failed').length;
|
|
122
|
+
const unverified = results.filter((result) => result.status === 'write-unverified').length;
|
|
123
|
+
const details = [
|
|
124
|
+
`${label} ${primary}/${total}`,
|
|
125
|
+
...(pruned > 0 ? [`Pruned ${pruned}/${total}`] : []),
|
|
126
|
+
...(skipped > 0 ? [`Skipped ${skipped}/${total}`] : []),
|
|
127
|
+
...(failed > 0 ? [`Failed ${failed}/${total}`] : []),
|
|
128
|
+
...(unverified > 0 ? [`Unverified ${unverified}/${total}`] : []),
|
|
129
|
+
];
|
|
130
|
+
println(`${details.join(', ')} in ${formatDuration(durationMs)}`);
|
|
131
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const TRACK_ALL_RESOURCES = "all";
|
|
2
|
+
export declare const TRACKABLE_RESOURCE_TYPES: readonly ["component", "endpoint", "group", "role-group", "job-template", "schedule", "event", "database-schema", "database-view"];
|
|
3
|
+
export type TrackableResourceType = typeof TRACKABLE_RESOURCE_TYPES[number];
|
|
4
|
+
export type TrackedResource = typeof TRACK_ALL_RESOURCES | TrackableResourceType;
|
|
5
|
+
export declare function normalizeTrackedResources(value: string | string[] | null | undefined): TrackedResource[];
|
|
6
|
+
export declare function formatTrackedResources(resources: readonly TrackedResource[]): string;
|
|
7
|
+
export declare function shouldTrackResource(resources: readonly TrackedResource[], resource: TrackableResourceType): boolean;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const TRACK_ALL_RESOURCES = 'all';
|
|
2
|
+
export const TRACKABLE_RESOURCE_TYPES = ['component', 'endpoint', 'group', 'role-group', 'job-template', 'schedule', 'event', 'database-schema', 'database-view'];
|
|
3
|
+
const RESOURCE_ALIASES = {
|
|
4
|
+
all: TRACK_ALL_RESOURCES,
|
|
5
|
+
'*': TRACK_ALL_RESOURCES,
|
|
6
|
+
component: 'component',
|
|
7
|
+
components: 'component',
|
|
8
|
+
endpoint: 'endpoint',
|
|
9
|
+
endpoints: 'endpoint',
|
|
10
|
+
group: 'group',
|
|
11
|
+
groups: 'group',
|
|
12
|
+
'role-group': 'role-group',
|
|
13
|
+
'role-groups': 'role-group',
|
|
14
|
+
rolegroup: 'role-group',
|
|
15
|
+
rolegroups: 'role-group',
|
|
16
|
+
'job-template': 'job-template',
|
|
17
|
+
'job-templates': 'job-template',
|
|
18
|
+
jobtemplate: 'job-template',
|
|
19
|
+
jobtemplates: 'job-template',
|
|
20
|
+
schedule: 'schedule',
|
|
21
|
+
schedules: 'schedule',
|
|
22
|
+
event: 'event',
|
|
23
|
+
events: 'event',
|
|
24
|
+
'database-schema': 'database-schema',
|
|
25
|
+
'database-schemas': 'database-schema',
|
|
26
|
+
database: 'database-schema',
|
|
27
|
+
databases: 'database-schema',
|
|
28
|
+
'database-view': 'database-view',
|
|
29
|
+
'database-views': 'database-view',
|
|
30
|
+
databaseview: 'database-view',
|
|
31
|
+
databaseviews: 'database-view',
|
|
32
|
+
};
|
|
33
|
+
export function normalizeTrackedResources(value) {
|
|
34
|
+
const rawValues = Array.isArray(value) ? value : value ? [value] : [TRACK_ALL_RESOURCES];
|
|
35
|
+
const normalized = [];
|
|
36
|
+
for (const rawValue of rawValues) {
|
|
37
|
+
for (const part of rawValue.split(',')) {
|
|
38
|
+
const key = part.trim().toLowerCase();
|
|
39
|
+
if (!key) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const resource = RESOURCE_ALIASES[key];
|
|
43
|
+
if (!resource) {
|
|
44
|
+
throw new Error(`Unsupported tracked resource "${part.trim()}". Supported values: all, component, endpoint, group, role-group, job-template, schedule, event, database-schema, database-view.`);
|
|
45
|
+
}
|
|
46
|
+
if (!normalized.includes(resource)) {
|
|
47
|
+
normalized.push(resource);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (normalized.length === 0 || normalized.includes(TRACK_ALL_RESOURCES)) {
|
|
52
|
+
return [TRACK_ALL_RESOURCES];
|
|
53
|
+
}
|
|
54
|
+
return normalized;
|
|
55
|
+
}
|
|
56
|
+
export function formatTrackedResources(resources) {
|
|
57
|
+
return resources.join(',');
|
|
58
|
+
}
|
|
59
|
+
export function shouldTrackResource(resources, resource) {
|
|
60
|
+
return resources.includes(TRACK_ALL_RESOURCES) || resources.includes(resource);
|
|
61
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -6,11 +6,13 @@ export type ParsedArgs = {
|
|
|
6
6
|
export type CommandContext = {
|
|
7
7
|
args: ParsedArgs;
|
|
8
8
|
client: RevoClient;
|
|
9
|
+
componentEnvironment?: string;
|
|
9
10
|
cwd: string;
|
|
10
11
|
packageVersion: string;
|
|
11
12
|
print: (value: unknown) => void;
|
|
12
13
|
println: (message: string) => void;
|
|
13
14
|
error: (message: string) => void;
|
|
15
|
+
warn?: (message: string) => void;
|
|
14
16
|
};
|
|
15
17
|
export type ComponentElementRecord = {
|
|
16
18
|
componentElementId?: string;
|
|
@@ -32,6 +34,231 @@ export type ComponentRecord = {
|
|
|
32
34
|
active?: boolean;
|
|
33
35
|
async?: boolean;
|
|
34
36
|
version?: number;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
metaData?: Record<string, unknown>;
|
|
39
|
+
resourceMetadata?: Record<string, unknown>;
|
|
35
40
|
elements?: ComponentElementRecord[];
|
|
36
41
|
[key: string]: unknown;
|
|
37
42
|
};
|
|
43
|
+
export type DatabaseSchemaRecord = {
|
|
44
|
+
databaseId?: string;
|
|
45
|
+
id?: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
category?: string | null;
|
|
48
|
+
desc?: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
type?: string;
|
|
51
|
+
version?: number;
|
|
52
|
+
metadata?: Record<string, unknown>;
|
|
53
|
+
metaData?: Record<string, unknown>;
|
|
54
|
+
resourceMetadata?: Record<string, unknown>;
|
|
55
|
+
definition?: unknown[];
|
|
56
|
+
partition?: Record<string, unknown> | null;
|
|
57
|
+
parent?: string | {
|
|
58
|
+
databaseId?: string;
|
|
59
|
+
id?: string;
|
|
60
|
+
name?: string;
|
|
61
|
+
} | null;
|
|
62
|
+
parentId?: string | null;
|
|
63
|
+
master?: string | {
|
|
64
|
+
databaseId?: string;
|
|
65
|
+
id?: string;
|
|
66
|
+
} | null;
|
|
67
|
+
masterId?: string | null;
|
|
68
|
+
foreignReferences?: unknown[];
|
|
69
|
+
audit?: unknown;
|
|
70
|
+
restricted?: boolean;
|
|
71
|
+
groups?: unknown[];
|
|
72
|
+
users?: unknown[];
|
|
73
|
+
tags?: unknown[];
|
|
74
|
+
deletedAt?: string | null;
|
|
75
|
+
deletedBy?: string | null;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
};
|
|
78
|
+
export type DatabaseViewRecord = {
|
|
79
|
+
databaseViewId?: string;
|
|
80
|
+
id?: string;
|
|
81
|
+
name?: string;
|
|
82
|
+
category?: string | null;
|
|
83
|
+
desc?: string;
|
|
84
|
+
description?: string;
|
|
85
|
+
metadata?: Record<string, unknown>;
|
|
86
|
+
metaData?: Record<string, unknown>;
|
|
87
|
+
resourceMetadata?: Record<string, unknown>;
|
|
88
|
+
type?: string;
|
|
89
|
+
definitionMode?: string;
|
|
90
|
+
request?: Record<string, unknown> | null;
|
|
91
|
+
rawQuery?: string | null;
|
|
92
|
+
definition?: Array<Record<string, unknown>>;
|
|
93
|
+
sourceDatabaseIds?: string[];
|
|
94
|
+
sourceFieldDependencies?: Record<string, string[]>;
|
|
95
|
+
fieldLineage?: unknown[];
|
|
96
|
+
uniqueKey?: string[] | null;
|
|
97
|
+
indexes?: unknown[];
|
|
98
|
+
restricted?: boolean;
|
|
99
|
+
users?: unknown[];
|
|
100
|
+
groups?: unknown[];
|
|
101
|
+
version?: number;
|
|
102
|
+
internalName?: string;
|
|
103
|
+
createdAt?: string;
|
|
104
|
+
createdBy?: string;
|
|
105
|
+
lastUpdateAt?: string;
|
|
106
|
+
lastUpdateBy?: string;
|
|
107
|
+
deletedAt?: string | null;
|
|
108
|
+
deletedBy?: string | null;
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
};
|
|
111
|
+
export type EndpointOptions = {
|
|
112
|
+
input?: Record<string, unknown>;
|
|
113
|
+
guard?: Record<string, unknown>;
|
|
114
|
+
response?: unknown;
|
|
115
|
+
httpCode?: number;
|
|
116
|
+
noResult?: boolean;
|
|
117
|
+
popResult?: string;
|
|
118
|
+
customValidateResponse?: unknown;
|
|
119
|
+
validateResponse?: number;
|
|
120
|
+
validateHideResponse?: boolean;
|
|
121
|
+
includeLibs?: string[];
|
|
122
|
+
excludeLibs?: string[];
|
|
123
|
+
skipLibs?: boolean;
|
|
124
|
+
legacyResults?: boolean;
|
|
125
|
+
hideRequest?: boolean;
|
|
126
|
+
hideResponse?: boolean;
|
|
127
|
+
memory?: number;
|
|
128
|
+
componentSchemaId?: string | null;
|
|
129
|
+
componentSchemaVersion?: number | null;
|
|
130
|
+
componentSchemaElement?: string | null;
|
|
131
|
+
[key: string]: unknown;
|
|
132
|
+
};
|
|
133
|
+
export type EndpointRecord = {
|
|
134
|
+
endpointId?: string;
|
|
135
|
+
id?: string;
|
|
136
|
+
name?: string;
|
|
137
|
+
category?: string | null;
|
|
138
|
+
desc?: string;
|
|
139
|
+
description?: string;
|
|
140
|
+
timeout?: number;
|
|
141
|
+
path?: string;
|
|
142
|
+
method?: string;
|
|
143
|
+
componentId?: string | null;
|
|
144
|
+
componentVersion?: number | null;
|
|
145
|
+
active?: boolean;
|
|
146
|
+
parts?: number;
|
|
147
|
+
options?: EndpointOptions | null;
|
|
148
|
+
version?: number;
|
|
149
|
+
metadata?: Record<string, unknown>;
|
|
150
|
+
metaData?: Record<string, unknown>;
|
|
151
|
+
resourceMetadata?: Record<string, unknown>;
|
|
152
|
+
createdAt?: string;
|
|
153
|
+
createdBy?: string;
|
|
154
|
+
lastUpdateAt?: string;
|
|
155
|
+
lastUpdateBy?: string;
|
|
156
|
+
deletedAt?: string | null;
|
|
157
|
+
deletedBy?: string | null;
|
|
158
|
+
[key: string]: unknown;
|
|
159
|
+
};
|
|
160
|
+
export type RoleGroupRecord = {
|
|
161
|
+
roleGroupId?: string;
|
|
162
|
+
id?: string;
|
|
163
|
+
name?: string;
|
|
164
|
+
category?: string | null;
|
|
165
|
+
desc?: string;
|
|
166
|
+
description?: string;
|
|
167
|
+
version?: number;
|
|
168
|
+
metadata?: Record<string, unknown>;
|
|
169
|
+
metaData?: Record<string, unknown>;
|
|
170
|
+
resourceMetadata?: Record<string, unknown>;
|
|
171
|
+
roles?: unknown[];
|
|
172
|
+
members?: unknown[];
|
|
173
|
+
users?: unknown[];
|
|
174
|
+
[key: string]: unknown;
|
|
175
|
+
};
|
|
176
|
+
export type GroupRecord = {
|
|
177
|
+
groupId?: string;
|
|
178
|
+
id?: string;
|
|
179
|
+
name?: string;
|
|
180
|
+
category?: string | null;
|
|
181
|
+
desc?: string;
|
|
182
|
+
description?: string;
|
|
183
|
+
accessType?: string;
|
|
184
|
+
ownerId?: string;
|
|
185
|
+
ownerType?: string;
|
|
186
|
+
leaderId?: string;
|
|
187
|
+
avatar?: string | null;
|
|
188
|
+
memberCount?: number;
|
|
189
|
+
version?: number;
|
|
190
|
+
metadata?: Record<string, unknown>;
|
|
191
|
+
metaData?: Record<string, unknown>;
|
|
192
|
+
resourceMetadata?: Record<string, unknown>;
|
|
193
|
+
members?: unknown[];
|
|
194
|
+
users?: unknown[];
|
|
195
|
+
[key: string]: unknown;
|
|
196
|
+
};
|
|
197
|
+
export type JobTemplateRecord = {
|
|
198
|
+
jobTemplateId?: string;
|
|
199
|
+
id?: string;
|
|
200
|
+
name?: string;
|
|
201
|
+
category?: string | null;
|
|
202
|
+
desc?: string;
|
|
203
|
+
description?: string;
|
|
204
|
+
version?: number;
|
|
205
|
+
metadata?: Record<string, unknown>;
|
|
206
|
+
metaData?: Record<string, unknown>;
|
|
207
|
+
resourceMetadata?: Record<string, unknown>;
|
|
208
|
+
componentId?: string;
|
|
209
|
+
component?: ComponentRecord;
|
|
210
|
+
componentVersion?: number | null;
|
|
211
|
+
timeout?: number;
|
|
212
|
+
triggerUser?: string | null;
|
|
213
|
+
options?: unknown;
|
|
214
|
+
inputs?: unknown;
|
|
215
|
+
active?: boolean;
|
|
216
|
+
deletedAt?: string | null;
|
|
217
|
+
[key: string]: unknown;
|
|
218
|
+
};
|
|
219
|
+
export type ScheduleRecord = {
|
|
220
|
+
scheduleId?: string;
|
|
221
|
+
id?: string;
|
|
222
|
+
name?: string;
|
|
223
|
+
category?: string | null;
|
|
224
|
+
desc?: string;
|
|
225
|
+
description?: string;
|
|
226
|
+
active?: boolean;
|
|
227
|
+
cron?: string;
|
|
228
|
+
timezone?: string;
|
|
229
|
+
targetType?: string;
|
|
230
|
+
targetId?: string;
|
|
231
|
+
targetVersion?: number | null;
|
|
232
|
+
targetConfig?: Record<string, unknown> | null;
|
|
233
|
+
systemManaged?: boolean;
|
|
234
|
+
version?: number;
|
|
235
|
+
metadata?: Record<string, unknown>;
|
|
236
|
+
metaData?: Record<string, unknown>;
|
|
237
|
+
resourceMetadata?: Record<string, unknown>;
|
|
238
|
+
deletedAt?: string | null;
|
|
239
|
+
deletedBy?: string | null;
|
|
240
|
+
[key: string]: unknown;
|
|
241
|
+
};
|
|
242
|
+
export type EventRecord = {
|
|
243
|
+
jobEventId?: string;
|
|
244
|
+
eventId?: string;
|
|
245
|
+
id?: string;
|
|
246
|
+
name?: string;
|
|
247
|
+
category?: string | null;
|
|
248
|
+
desc?: string;
|
|
249
|
+
description?: string;
|
|
250
|
+
active?: boolean;
|
|
251
|
+
type?: string;
|
|
252
|
+
targetType?: string;
|
|
253
|
+
targetId?: string;
|
|
254
|
+
targetVersion?: number | null;
|
|
255
|
+
targetConfig?: Record<string, unknown> | null;
|
|
256
|
+
filter?: Record<string, unknown> | null;
|
|
257
|
+
version?: number;
|
|
258
|
+
metadata?: Record<string, unknown>;
|
|
259
|
+
metaData?: Record<string, unknown>;
|
|
260
|
+
resourceMetadata?: Record<string, unknown>;
|
|
261
|
+
deletedAt?: string | null;
|
|
262
|
+
deletedBy?: string | null;
|
|
263
|
+
[key: string]: unknown;
|
|
264
|
+
};
|
package/dist/src/ui.d.ts
CHANGED
|
@@ -10,11 +10,13 @@ export declare function renderRuntimeInfo(input: {
|
|
|
10
10
|
nodeVersion: string;
|
|
11
11
|
configDir: string;
|
|
12
12
|
runtime: RuntimeConfig;
|
|
13
|
+
environmentName?: string;
|
|
13
14
|
auth: AuthViewState;
|
|
14
15
|
}): string;
|
|
15
16
|
export declare function renderAuthStatus(input: {
|
|
16
17
|
configDir: string;
|
|
17
18
|
runtime: RuntimeConfig;
|
|
19
|
+
environmentName?: string;
|
|
18
20
|
auth: AuthViewState;
|
|
19
21
|
}): string;
|
|
20
22
|
export declare function renderOverview(input: {
|
|
@@ -22,5 +24,6 @@ export declare function renderOverview(input: {
|
|
|
22
24
|
nodeVersion: string;
|
|
23
25
|
configDir: string;
|
|
24
26
|
runtime: RuntimeConfig;
|
|
27
|
+
environmentName?: string;
|
|
25
28
|
auth: AuthViewState;
|
|
26
29
|
}): string;
|