@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
package/dist/src/project.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ParsedArgs } from './types.ts';
|
|
2
|
+
import { type ComponentIdentityConfig, type ComponentIdentityMode } from './resource-metadata.ts';
|
|
3
|
+
import { type TrackedResource } from './tracked-resources.ts';
|
|
2
4
|
export declare const REVO_PROJECT_DIR = ".revoengine";
|
|
3
5
|
export declare const REVO_TYPES_DIR: string;
|
|
4
6
|
export declare const REVO_TYPES_FILE: string;
|
|
@@ -15,6 +17,9 @@ export type ProjectInvocation = {
|
|
|
15
17
|
action: 'init' | 'update';
|
|
16
18
|
targetArg?: string;
|
|
17
19
|
extraArgs: string[];
|
|
20
|
+
identityMode?: ComponentIdentityMode;
|
|
21
|
+
stableKeyName?: string;
|
|
22
|
+
trackedResources?: TrackedResource[];
|
|
18
23
|
};
|
|
19
24
|
export type EditorTypesBundle = {
|
|
20
25
|
code: string;
|
|
@@ -34,6 +39,10 @@ export type RevoProjectMetadata = {
|
|
|
34
39
|
hash: string;
|
|
35
40
|
lastSyncAt: string;
|
|
36
41
|
workspace?: string;
|
|
42
|
+
componentIdentity?: ComponentIdentityConfig;
|
|
43
|
+
/** Legacy project metadata field. Read for migration, but no longer written. */
|
|
44
|
+
stableKeyName?: string;
|
|
45
|
+
trackedResources?: TrackedResource[];
|
|
37
46
|
};
|
|
38
47
|
export type ProjectSyncInput = {
|
|
39
48
|
endpoint: string;
|
|
@@ -43,6 +52,9 @@ export type ProjectSyncInput = {
|
|
|
43
52
|
libVersion: string;
|
|
44
53
|
hash: string;
|
|
45
54
|
lastSyncAt: string;
|
|
55
|
+
authProfile?: string;
|
|
56
|
+
componentIdentity?: ComponentIdentityConfig;
|
|
57
|
+
trackedResources?: TrackedResource[];
|
|
46
58
|
};
|
|
47
59
|
export type ProjectConfigResult = {
|
|
48
60
|
filePath: string;
|
|
@@ -69,6 +81,7 @@ export declare function buildProjectMetadata(input: {
|
|
|
69
81
|
baseUrl: string;
|
|
70
82
|
bundle: EditorTypesBundle;
|
|
71
83
|
now?: Date;
|
|
84
|
+
authProfile?: string;
|
|
72
85
|
}): {
|
|
73
86
|
schemaVersion: 1;
|
|
74
87
|
endpoint: string;
|
|
@@ -80,21 +93,16 @@ export declare function buildProjectMetadata(input: {
|
|
|
80
93
|
};
|
|
81
94
|
export declare function findProjectMetadataFile(startDir?: string): string;
|
|
82
95
|
export declare function readProjectMetadata(startDir?: string): RevoProjectMetadata | null;
|
|
96
|
+
export declare function readProjectStableKeyName(startDir?: string): string;
|
|
97
|
+
export declare function readProjectComponentIdentity(startDir?: string): ComponentIdentityConfig;
|
|
98
|
+
export declare function readProjectTrackedResources(startDir?: string): TrackedResource[];
|
|
83
99
|
export declare function resolveProjectRoot(startDir?: string): string;
|
|
84
100
|
export declare function resolveProjectWorkspace(startDir?: string): string;
|
|
85
101
|
export declare function buildProjectSyncState(input: {
|
|
86
102
|
fallbackEndpoint: string;
|
|
87
103
|
bundle: EditorTypesBundle;
|
|
88
104
|
now?: Date;
|
|
89
|
-
}):
|
|
90
|
-
endpoint: string;
|
|
91
|
-
code: string;
|
|
92
|
-
definitions: unknown[];
|
|
93
|
-
apiVersion: string;
|
|
94
|
-
libVersion: string;
|
|
95
|
-
hash: string;
|
|
96
|
-
lastSyncAt: string;
|
|
97
|
-
};
|
|
105
|
+
}): ProjectSyncInput;
|
|
98
106
|
export declare function syncProjectFiles(layoutOrTargetDir: ProjectTargetLayout | string, input: ProjectSyncInput): {
|
|
99
107
|
projectDir: string;
|
|
100
108
|
typesFile: string;
|
package/dist/src/project.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { readJsonFile } from "./utils.js";
|
|
4
|
+
import { COMPONENT_IDENTITY_BY_ID, DEFAULT_COMPONENT_IDENTITY, DEFAULT_STABLE_KEY_NAME, normalizeComponentIdentityConfig, normalizeComponentIdentityMode, normalizeStableKeyName, } from "./resource-metadata.js";
|
|
5
|
+
import { TRACK_ALL_RESOURCES, normalizeTrackedResources, } from "./tracked-resources.js";
|
|
4
6
|
export const REVO_PROJECT_DIR = '.revoengine';
|
|
5
7
|
export const REVO_TYPES_DIR = path.join(REVO_PROJECT_DIR, 'types');
|
|
6
8
|
export const REVO_TYPES_FILE = path.join(REVO_TYPES_DIR, 'revo.editor.d.ts');
|
|
@@ -180,6 +182,22 @@ function appendUnique(list, value) {
|
|
|
180
182
|
}
|
|
181
183
|
return list;
|
|
182
184
|
}
|
|
185
|
+
function readResourceSelectionArgs(args) {
|
|
186
|
+
const values = [];
|
|
187
|
+
for (const name of ['resources', 'resource', 'tracked-resources', 'trackedResources']) {
|
|
188
|
+
const value = args[name];
|
|
189
|
+
if (typeof value === 'string') {
|
|
190
|
+
values.push(value);
|
|
191
|
+
}
|
|
192
|
+
else if (Array.isArray(value)) {
|
|
193
|
+
values.push(...value);
|
|
194
|
+
}
|
|
195
|
+
else if (value === true) {
|
|
196
|
+
throw new Error(`--${name} requires a value such as "all" or "component".`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return values.length > 0 ? values : undefined;
|
|
200
|
+
}
|
|
183
201
|
function writeJsonFile(filePath, value) {
|
|
184
202
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
185
203
|
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
@@ -187,27 +205,37 @@ function writeJsonFile(filePath, value) {
|
|
|
187
205
|
export function resolveProjectInvocation(args) {
|
|
188
206
|
const projectArgs = args._.slice(1);
|
|
189
207
|
const [first, second, ...rest] = projectArgs;
|
|
208
|
+
const explicitStableKeyName = readString(args.stableKeyName)
|
|
209
|
+
|| readString(args['stable-key-name']);
|
|
210
|
+
const stableKeyName = explicitStableKeyName
|
|
211
|
+
? normalizeStableKeyName(explicitStableKeyName)
|
|
212
|
+
: undefined;
|
|
213
|
+
const explicitIdentityMode = readString(args.identityMode)
|
|
214
|
+
|| readString(args['identity-mode']);
|
|
215
|
+
const identityMode = explicitIdentityMode
|
|
216
|
+
? normalizeComponentIdentityMode(explicitIdentityMode)
|
|
217
|
+
: stableKeyName
|
|
218
|
+
? 'stableKey'
|
|
219
|
+
: undefined;
|
|
220
|
+
const trackedResourceArgs = readResourceSelectionArgs(args);
|
|
221
|
+
const trackedResources = trackedResourceArgs ? normalizeTrackedResources(trackedResourceArgs) : undefined;
|
|
190
222
|
if (!first) {
|
|
191
|
-
|
|
192
|
-
action: 'init',
|
|
193
|
-
extraArgs: [],
|
|
194
|
-
};
|
|
223
|
+
throw new Error('Missing project action. Use `revo project init [path]` or `revo project update [path]`.');
|
|
195
224
|
}
|
|
196
225
|
if (first === 'init' || first === 'update') {
|
|
197
226
|
return {
|
|
198
227
|
action: first,
|
|
199
228
|
targetArg: second,
|
|
200
229
|
extraArgs: rest,
|
|
230
|
+
identityMode,
|
|
231
|
+
stableKeyName,
|
|
232
|
+
...(trackedResources ? { trackedResources } : {}),
|
|
201
233
|
};
|
|
202
234
|
}
|
|
203
235
|
if (first === 'switch') {
|
|
204
236
|
throw new Error('`revo project switch` was removed because credentials are no longer instance-scoped.');
|
|
205
237
|
}
|
|
206
|
-
|
|
207
|
-
action: 'init',
|
|
208
|
-
targetArg: first,
|
|
209
|
-
extraArgs: second ? [second, ...rest] : rest,
|
|
210
|
-
};
|
|
238
|
+
throw new Error(`Unknown project action "${first}". Use \`revo project init [path]\` or \`revo project update [path]\`.`);
|
|
211
239
|
}
|
|
212
240
|
function normalizeWorkspaceDirectory(projectRoot, workspaceRoot) {
|
|
213
241
|
const relative = path.relative(projectRoot, workspaceRoot).split(path.sep).join('/');
|
|
@@ -320,7 +348,7 @@ export function buildProjectMetadata(input) {
|
|
|
320
348
|
return {
|
|
321
349
|
schemaVersion: 1,
|
|
322
350
|
endpoint: bundle.endpoint || buildEditorEndpoint(baseUrl),
|
|
323
|
-
authProfile: 'default',
|
|
351
|
+
authProfile: input.authProfile || 'default',
|
|
324
352
|
apiVersion: getRequiredMetadataValue(bundle, 'apiVersion'),
|
|
325
353
|
libVersion: getRequiredMetadataValue(bundle, 'libVersion'),
|
|
326
354
|
hash: getRequiredMetadataValue(bundle, 'hash'),
|
|
@@ -363,6 +391,40 @@ export function readProjectMetadata(startDir = process.cwd()) {
|
|
|
363
391
|
}
|
|
364
392
|
return raw;
|
|
365
393
|
}
|
|
394
|
+
export function readProjectStableKeyName(startDir = process.cwd()) {
|
|
395
|
+
const metadata = readProjectMetadata(startDir);
|
|
396
|
+
const identity = readProjectComponentIdentity(startDir);
|
|
397
|
+
return normalizeStableKeyName(identity.mode === 'stableKey'
|
|
398
|
+
? identity.metadataProperty
|
|
399
|
+
: metadata?.stableKeyName || DEFAULT_STABLE_KEY_NAME);
|
|
400
|
+
}
|
|
401
|
+
export function readProjectComponentIdentity(startDir = process.cwd()) {
|
|
402
|
+
const metadata = readProjectMetadata(startDir);
|
|
403
|
+
if (!metadata) {
|
|
404
|
+
return COMPONENT_IDENTITY_BY_ID;
|
|
405
|
+
}
|
|
406
|
+
if (metadata.componentIdentity && isRecord(metadata.componentIdentity)) {
|
|
407
|
+
const mode = readString(metadata.componentIdentity.mode);
|
|
408
|
+
const metadataProperty = readString(metadata.componentIdentity.metadataProperty);
|
|
409
|
+
if (mode) {
|
|
410
|
+
return normalizeComponentIdentityConfig({
|
|
411
|
+
mode: normalizeComponentIdentityMode(mode),
|
|
412
|
+
...(metadataProperty ? { metadataProperty } : {}),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (metadata.stableKeyName) {
|
|
417
|
+
return normalizeComponentIdentityConfig({
|
|
418
|
+
mode: 'stableKey',
|
|
419
|
+
metadataProperty: metadata.stableKeyName,
|
|
420
|
+
}, DEFAULT_COMPONENT_IDENTITY);
|
|
421
|
+
}
|
|
422
|
+
return COMPONENT_IDENTITY_BY_ID;
|
|
423
|
+
}
|
|
424
|
+
export function readProjectTrackedResources(startDir = process.cwd()) {
|
|
425
|
+
const metadata = readProjectMetadata(startDir);
|
|
426
|
+
return normalizeTrackedResources(metadata?.trackedResources || TRACK_ALL_RESOURCES);
|
|
427
|
+
}
|
|
366
428
|
export function resolveProjectRoot(startDir = process.cwd()) {
|
|
367
429
|
const metadataFile = findProjectMetadataFile(startDir);
|
|
368
430
|
return metadataFile ? path.dirname(path.dirname(metadataFile)) : '';
|
|
@@ -435,11 +497,13 @@ export function syncProjectFiles(layoutOrTargetDir, input) {
|
|
|
435
497
|
writeJsonFile(metadataFile, {
|
|
436
498
|
schemaVersion: 1,
|
|
437
499
|
endpoint: input.endpoint,
|
|
438
|
-
authProfile: 'default',
|
|
500
|
+
authProfile: input.authProfile || 'default',
|
|
439
501
|
apiVersion: input.apiVersion,
|
|
440
502
|
libVersion: input.libVersion,
|
|
441
503
|
hash: input.hash,
|
|
442
504
|
lastSyncAt: input.lastSyncAt,
|
|
505
|
+
componentIdentity: normalizeComponentIdentityConfig(input.componentIdentity, DEFAULT_COMPONENT_IDENTITY),
|
|
506
|
+
trackedResources: normalizeTrackedResources(input.trackedResources || TRACK_ALL_RESOURCES),
|
|
443
507
|
...(workspaceDirectory !== '.' ? { workspace: workspaceDirectory } : {}),
|
|
444
508
|
});
|
|
445
509
|
const configResult = ensureProjectConfig(workspaceRoot, toPosixRelative(workspaceRoot, typesFile));
|
|
@@ -480,6 +544,9 @@ export function ensureProjectConfig(rootDir, typesInclude = REVO_TYPES_FILE) {
|
|
|
480
544
|
if (!fs.existsSync(tsconfigPath) && !fs.existsSync(jsconfigPath)) {
|
|
481
545
|
writeJsonFile(tsconfigPath, {
|
|
482
546
|
compilerOptions: {
|
|
547
|
+
target: 'ES2022',
|
|
548
|
+
module: 'ESNext',
|
|
549
|
+
moduleDetection: 'force',
|
|
483
550
|
allowJs: true,
|
|
484
551
|
checkJs: false,
|
|
485
552
|
noEmit: true,
|
|
@@ -499,6 +566,15 @@ export function ensureProjectConfig(rootDir, typesInclude = REVO_TYPES_FILE) {
|
|
|
499
566
|
const filePath = fs.existsSync(tsconfigPath) ? tsconfigPath : jsconfigPath;
|
|
500
567
|
const parsed = parseConfigFile(filePath);
|
|
501
568
|
const compilerOptions = ensureObjectField(parsed, 'compilerOptions', filePath);
|
|
569
|
+
if (compilerOptions.target === undefined) {
|
|
570
|
+
compilerOptions.target = 'ES2022';
|
|
571
|
+
}
|
|
572
|
+
if (compilerOptions.module === undefined) {
|
|
573
|
+
compilerOptions.module = 'ESNext';
|
|
574
|
+
}
|
|
575
|
+
if (compilerOptions.moduleDetection === undefined) {
|
|
576
|
+
compilerOptions.moduleDetection = 'force';
|
|
577
|
+
}
|
|
502
578
|
if (compilerOptions.allowJs === undefined) {
|
|
503
579
|
compilerOptions.allowJs = true;
|
|
504
580
|
}
|
package/dist/src/prompt.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import readline from 'node:readline/promises';
|
|
2
|
+
import { Writable } from 'node:stream';
|
|
2
3
|
export function isInteractiveTerminal() {
|
|
3
4
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
4
5
|
}
|
|
@@ -23,32 +24,23 @@ export async function promptSecret(message) {
|
|
|
23
24
|
if (!isInteractiveTerminal()) {
|
|
24
25
|
return '';
|
|
25
26
|
}
|
|
27
|
+
const mutedOutput = new Writable({
|
|
28
|
+
write(_chunk, _encoding, callback) {
|
|
29
|
+
callback();
|
|
30
|
+
},
|
|
31
|
+
});
|
|
26
32
|
const rl = readline.createInterface({
|
|
27
33
|
input: process.stdin,
|
|
28
|
-
output:
|
|
34
|
+
output: mutedOutput,
|
|
29
35
|
terminal: true,
|
|
30
36
|
});
|
|
31
|
-
let muted = true;
|
|
32
|
-
const originalWriteToOutput = rl._writeToOutput.bind(rl);
|
|
33
|
-
rl._writeToOutput = (value) => {
|
|
34
|
-
if (!muted) {
|
|
35
|
-
originalWriteToOutput(value);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (value.startsWith(`${message}: `)) {
|
|
39
|
-
originalWriteToOutput(value);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
// Preserve terminal editing/paste behavior while masking visible content.
|
|
43
|
-
originalWriteToOutput('*');
|
|
44
|
-
};
|
|
45
37
|
try {
|
|
46
|
-
|
|
47
|
-
return
|
|
38
|
+
process.stdout.write(`${message}: `);
|
|
39
|
+
return await rl.question('');
|
|
48
40
|
}
|
|
49
41
|
finally {
|
|
50
|
-
muted = false;
|
|
51
42
|
rl.close();
|
|
43
|
+
process.stdout.write('\n');
|
|
52
44
|
}
|
|
53
45
|
}
|
|
54
46
|
export async function promptConfirm(message, defaultValue = false) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ComponentRecord } from './types.ts';
|
|
2
|
+
export declare const DEFAULT_STABLE_KEY_NAME = "stableKey";
|
|
3
|
+
export declare const NULL_CATEGORY_KEY = "__no_category__";
|
|
4
|
+
export type ComponentIdentityMode = 'componentId' | 'stableKey';
|
|
5
|
+
export type ComponentIdentityConfig = {
|
|
6
|
+
mode: ComponentIdentityMode;
|
|
7
|
+
metadataProperty?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const COMPONENT_IDENTITY_BY_ID: ComponentIdentityConfig;
|
|
10
|
+
export declare const DEFAULT_COMPONENT_IDENTITY: ComponentIdentityConfig;
|
|
11
|
+
export declare function normalizeStableKeyValue(value: string): string;
|
|
12
|
+
export declare function normalizeStableKeyName(value: string | null | undefined): string;
|
|
13
|
+
export declare function normalizeComponentIdentityMode(value: string | null | undefined): ComponentIdentityMode;
|
|
14
|
+
export declare function normalizeComponentIdentityConfig(value: Partial<ComponentIdentityConfig> | null | undefined, fallback?: ComponentIdentityConfig): ComponentIdentityConfig;
|
|
15
|
+
export declare function sameComponentIdentityConfig(left: ComponentIdentityConfig, right: ComponentIdentityConfig): boolean;
|
|
16
|
+
export declare function readConfiguredResourceMetadataStableKey(component: ComponentRecord, stableKeyName?: string): string | null;
|
|
17
|
+
export declare function readComponentIdentityValue(component: ComponentRecord, identity: ComponentIdentityConfig): string | null;
|
|
18
|
+
export declare function readResourceMetadataStableKey(component: ComponentRecord, stableKeyName?: string): string | null;
|
|
19
|
+
export declare function buildStableKey(component: ComponentRecord, stableKeyName?: string): string;
|
|
20
|
+
export declare function buildStableKeyValue(component: ComponentRecord, stableKeyName?: string): string;
|
|
21
|
+
export declare function buildResourceIdentityStableKeyValue(component: ComponentRecord): string;
|
|
22
|
+
export declare function buildResourceMetadataWithStableKey(component: ComponentRecord, stableKeyName?: string): {
|
|
23
|
+
[x: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
export declare function sanitizeUserMetadata(value: unknown): Record<string, unknown>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export const DEFAULT_STABLE_KEY_NAME = 'stableKey';
|
|
2
|
+
export const NULL_CATEGORY_KEY = '__no_category__';
|
|
3
|
+
export const COMPONENT_IDENTITY_BY_ID = {
|
|
4
|
+
mode: 'componentId',
|
|
5
|
+
};
|
|
6
|
+
export const DEFAULT_COMPONENT_IDENTITY = {
|
|
7
|
+
mode: 'stableKey',
|
|
8
|
+
metadataProperty: DEFAULT_STABLE_KEY_NAME,
|
|
9
|
+
};
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function readString(value) {
|
|
14
|
+
return typeof value === 'string' && value.trim() ? value : null;
|
|
15
|
+
}
|
|
16
|
+
function normalizeKeyPart(value) {
|
|
17
|
+
return (value ?? '').trim().replace(/\s+/g, ' ').toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
export function normalizeStableKeyValue(value) {
|
|
20
|
+
return normalizeKeyPart(value);
|
|
21
|
+
}
|
|
22
|
+
export function normalizeStableKeyName(value) {
|
|
23
|
+
const normalized = (value || DEFAULT_STABLE_KEY_NAME).trim();
|
|
24
|
+
if (!normalized) {
|
|
25
|
+
return DEFAULT_STABLE_KEY_NAME;
|
|
26
|
+
}
|
|
27
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$-]*$/.test(normalized)) {
|
|
28
|
+
throw new Error('Stable key metadata name must start with a letter, "_", or "$" and contain only letters, digits, "_", "$", or "-".');
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
}
|
|
32
|
+
export function normalizeComponentIdentityMode(value) {
|
|
33
|
+
const normalized = (value || '').trim().toLowerCase().replace(/[-_\s]/g, '');
|
|
34
|
+
if (normalized === 'stablekey' || normalized === 'metadata') {
|
|
35
|
+
return 'stableKey';
|
|
36
|
+
}
|
|
37
|
+
if (normalized === 'componentid' || normalized === 'id' || normalized === 'uuid') {
|
|
38
|
+
return 'componentId';
|
|
39
|
+
}
|
|
40
|
+
throw new Error('Component identity mode must be "stableKey" or "componentId".');
|
|
41
|
+
}
|
|
42
|
+
export function normalizeComponentIdentityConfig(value, fallback = COMPONENT_IDENTITY_BY_ID) {
|
|
43
|
+
const mode = value?.mode
|
|
44
|
+
? normalizeComponentIdentityMode(value.mode)
|
|
45
|
+
: fallback.mode;
|
|
46
|
+
if (mode === 'componentId') {
|
|
47
|
+
return { mode };
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
mode,
|
|
51
|
+
metadataProperty: normalizeStableKeyName(value?.metadataProperty || fallback.metadataProperty),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function sameComponentIdentityConfig(left, right) {
|
|
55
|
+
const normalizedLeft = normalizeComponentIdentityConfig(left);
|
|
56
|
+
const normalizedRight = normalizeComponentIdentityConfig(right);
|
|
57
|
+
return normalizedLeft.mode === normalizedRight.mode
|
|
58
|
+
&& normalizedLeft.metadataProperty === normalizedRight.metadataProperty;
|
|
59
|
+
}
|
|
60
|
+
function readConfiguredStableKeyFromRecord(value, stableKeyName) {
|
|
61
|
+
if (!isRecord(value)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return readString(value[stableKeyName])
|
|
65
|
+
?? (isRecord(value.details) ? readString(value.details[stableKeyName]) : null);
|
|
66
|
+
}
|
|
67
|
+
function readAnyStableKeyFromRecord(value, stableKeyName) {
|
|
68
|
+
return readConfiguredStableKeyFromRecord(value, stableKeyName)
|
|
69
|
+
?? (stableKeyName === DEFAULT_STABLE_KEY_NAME ? null : readConfiguredStableKeyFromRecord(value, DEFAULT_STABLE_KEY_NAME));
|
|
70
|
+
}
|
|
71
|
+
export function readConfiguredResourceMetadataStableKey(component, stableKeyName = DEFAULT_STABLE_KEY_NAME) {
|
|
72
|
+
const fieldName = normalizeStableKeyName(stableKeyName);
|
|
73
|
+
return readConfiguredStableKeyFromRecord(component.metadata, fieldName)
|
|
74
|
+
?? readConfiguredStableKeyFromRecord(component.metaData, fieldName)
|
|
75
|
+
?? readConfiguredStableKeyFromRecord(component.resourceMetadata, fieldName);
|
|
76
|
+
}
|
|
77
|
+
export function readComponentIdentityValue(component, identity) {
|
|
78
|
+
const normalized = normalizeComponentIdentityConfig(identity);
|
|
79
|
+
if (normalized.mode === 'componentId') {
|
|
80
|
+
return readString(component.componentId) ?? readString(component.id);
|
|
81
|
+
}
|
|
82
|
+
return readConfiguredResourceMetadataStableKey(component, normalized.metadataProperty);
|
|
83
|
+
}
|
|
84
|
+
export function readResourceMetadataStableKey(component, stableKeyName = DEFAULT_STABLE_KEY_NAME) {
|
|
85
|
+
const fieldName = normalizeStableKeyName(stableKeyName);
|
|
86
|
+
return readAnyStableKeyFromRecord(component.metadata, fieldName)
|
|
87
|
+
?? readAnyStableKeyFromRecord(component.metaData, fieldName)
|
|
88
|
+
?? readAnyStableKeyFromRecord(component.resourceMetadata, fieldName);
|
|
89
|
+
}
|
|
90
|
+
export function buildStableKey(component, stableKeyName = DEFAULT_STABLE_KEY_NAME) {
|
|
91
|
+
const metadataStableKey = readResourceMetadataStableKey(component, stableKeyName);
|
|
92
|
+
if (metadataStableKey) {
|
|
93
|
+
return normalizeKeyPart(metadataStableKey);
|
|
94
|
+
}
|
|
95
|
+
const category = normalizeKeyPart(component.category) || NULL_CATEGORY_KEY;
|
|
96
|
+
const name = normalizeKeyPart(component.name);
|
|
97
|
+
return `${category}/${name}`;
|
|
98
|
+
}
|
|
99
|
+
export function buildStableKeyValue(component, stableKeyName = DEFAULT_STABLE_KEY_NAME) {
|
|
100
|
+
const metadataStableKey = readResourceMetadataStableKey(component, stableKeyName);
|
|
101
|
+
if (metadataStableKey) {
|
|
102
|
+
return metadataStableKey;
|
|
103
|
+
}
|
|
104
|
+
return buildResourceIdentityStableKeyValue(component);
|
|
105
|
+
}
|
|
106
|
+
export function buildResourceIdentityStableKeyValue(component) {
|
|
107
|
+
const category = typeof component.category === 'string' && component.category.trim()
|
|
108
|
+
? component.category.trim()
|
|
109
|
+
: NULL_CATEGORY_KEY;
|
|
110
|
+
const name = typeof component.name === 'string' ? component.name.trim() : '';
|
|
111
|
+
return `${category}/${name}`;
|
|
112
|
+
}
|
|
113
|
+
export function buildResourceMetadataWithStableKey(component, stableKeyName = DEFAULT_STABLE_KEY_NAME) {
|
|
114
|
+
const fieldName = normalizeStableKeyName(stableKeyName);
|
|
115
|
+
return {
|
|
116
|
+
...sanitizeUserMetadata(component.metadata),
|
|
117
|
+
[fieldName]: buildStableKeyValue(component, fieldName),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export function sanitizeUserMetadata(value) {
|
|
121
|
+
if (!isRecord(value)) {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
const sanitized = {};
|
|
125
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
126
|
+
if (key.startsWith('__')) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
sanitized[key] = entry;
|
|
130
|
+
}
|
|
131
|
+
return sanitized;
|
|
132
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { DatabaseSchemaStatsRecord, RevoClient } from '../client.ts';
|
|
2
|
+
export declare const DATABASE_SCHEMAS_DIRECTORY = "DatabaseSchemas";
|
|
3
|
+
export declare const DATABASE_SCHEMA_MANIFEST_FILE = "database-schema.json";
|
|
4
|
+
type PortableDefinition = Record<string, unknown>;
|
|
5
|
+
export type DatabaseSchemaManifest = {
|
|
6
|
+
kind: 'database-schema' | 'database-partition';
|
|
7
|
+
stableKey: string;
|
|
8
|
+
name: string;
|
|
9
|
+
category?: string | null;
|
|
10
|
+
description?: string;
|
|
11
|
+
metadata: Record<string, unknown>;
|
|
12
|
+
type?: string;
|
|
13
|
+
definition?: PortableDefinition[];
|
|
14
|
+
partition?: Record<string, unknown>;
|
|
15
|
+
parentStableKey?: string;
|
|
16
|
+
migration?: {
|
|
17
|
+
foreignReferences: 'manual';
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type DatabaseSchemaPullResult = {
|
|
21
|
+
pulled: Array<{
|
|
22
|
+
manifest: DatabaseSchemaManifest;
|
|
23
|
+
targetPath: string;
|
|
24
|
+
}>;
|
|
25
|
+
pruned: Array<{
|
|
26
|
+
stableKey: string;
|
|
27
|
+
targetPath: string;
|
|
28
|
+
}>;
|
|
29
|
+
skipped: Array<{
|
|
30
|
+
targetPath: string;
|
|
31
|
+
reason: string;
|
|
32
|
+
}>;
|
|
33
|
+
diagnostics: Array<{
|
|
34
|
+
stableKey: string;
|
|
35
|
+
audit?: unknown;
|
|
36
|
+
acl?: unknown;
|
|
37
|
+
tags?: unknown;
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
40
|
+
export type DatabaseSchemaPlanStatus = 'clean' | 'create' | 'safe-update' | 'remote-changed' | 'conflict' | 'missing-lock' | 'orphan-root' | 'orphan-partition' | 'delete-partition' | 'previously-deleted-partition' | 'replacement-requires-two-phases' | 'unmanaged' | 'collision' | 'reconciled' | 'manual-schema-migration-required' | 'access-policy-required';
|
|
41
|
+
export type DatabaseSchemaChange = {
|
|
42
|
+
kind: 'field-changed' | 'immutable-field' | 'column-added' | 'column-removed' | 'column-changed' | 'column-renamed' | 'column-stable-key-missing' | 'database-view-dependency' | 'column-order' | 'foreign-reference' | 'audit-trigger-rebuild';
|
|
43
|
+
field: string;
|
|
44
|
+
risk: 'safe' | 'manual' | 'operational';
|
|
45
|
+
before?: unknown;
|
|
46
|
+
after?: unknown;
|
|
47
|
+
column?: string;
|
|
48
|
+
hint: string;
|
|
49
|
+
};
|
|
50
|
+
export type DatabaseSchemaPlanItem = {
|
|
51
|
+
status: DatabaseSchemaPlanStatus;
|
|
52
|
+
stableKey?: string;
|
|
53
|
+
name: string;
|
|
54
|
+
kind?: DatabaseSchemaManifest['kind'];
|
|
55
|
+
parentStableKey?: string;
|
|
56
|
+
reason?: string;
|
|
57
|
+
changedFields?: string[];
|
|
58
|
+
changes?: DatabaseSchemaChange[];
|
|
59
|
+
hints: string[];
|
|
60
|
+
desiredHash?: string;
|
|
61
|
+
currentHash?: string;
|
|
62
|
+
baselineHash?: string;
|
|
63
|
+
stats?: DatabaseSchemaStatsRecord['size'];
|
|
64
|
+
};
|
|
65
|
+
export type DatabaseSchemasPlan = {
|
|
66
|
+
resourceType: 'database-schema';
|
|
67
|
+
env: string;
|
|
68
|
+
withStats: boolean;
|
|
69
|
+
skipPartitions: boolean;
|
|
70
|
+
counts: Record<DatabaseSchemaPlanStatus, number>;
|
|
71
|
+
blockers: string[];
|
|
72
|
+
warnings: string[];
|
|
73
|
+
exclusions: string[];
|
|
74
|
+
items: DatabaseSchemaPlanItem[];
|
|
75
|
+
};
|
|
76
|
+
export type DatabaseSchemaPushResult = {
|
|
77
|
+
stableKey: string;
|
|
78
|
+
action: 'create' | 'update' | 'reconcile' | 'delete' | 'skipped';
|
|
79
|
+
status: 'deployed' | 'skipped' | 'failed' | 'write-unverified';
|
|
80
|
+
targetPath: string;
|
|
81
|
+
reason?: string;
|
|
82
|
+
error?: string;
|
|
83
|
+
};
|
|
84
|
+
export declare function hashPortableDatabaseSchema(manifest: DatabaseSchemaManifest): string;
|
|
85
|
+
export declare function buildDatabaseSchemasPlan(input: {
|
|
86
|
+
client: RevoClient;
|
|
87
|
+
cwd: string;
|
|
88
|
+
envName: string;
|
|
89
|
+
stableKeyName: string;
|
|
90
|
+
withStats?: boolean;
|
|
91
|
+
skipPartitions?: boolean;
|
|
92
|
+
}): Promise<DatabaseSchemasPlan>;
|
|
93
|
+
export declare function pullDatabaseSchemasToWorkspace(input: {
|
|
94
|
+
client: RevoClient;
|
|
95
|
+
cwd: string;
|
|
96
|
+
envName: string;
|
|
97
|
+
stableKeyName: string;
|
|
98
|
+
force?: boolean;
|
|
99
|
+
strict?: boolean;
|
|
100
|
+
pruneOrphans?: boolean;
|
|
101
|
+
}): Promise<DatabaseSchemaPullResult>;
|
|
102
|
+
export declare function pushDatabaseSchemas(input: {
|
|
103
|
+
client: RevoClient;
|
|
104
|
+
cwd: string;
|
|
105
|
+
envName: string;
|
|
106
|
+
stableKeyName: string;
|
|
107
|
+
stableKey?: string;
|
|
108
|
+
force?: boolean;
|
|
109
|
+
prunePartitions?: boolean;
|
|
110
|
+
prunePartitionsConfirmed?: boolean;
|
|
111
|
+
pruneOrphans?: boolean;
|
|
112
|
+
pruneOrphansConfirmed?: boolean;
|
|
113
|
+
}): Promise<{
|
|
114
|
+
plan: DatabaseSchemasPlan;
|
|
115
|
+
results: DatabaseSchemaPushResult[];
|
|
116
|
+
}>;
|
|
117
|
+
export {};
|