newo 3.4.0 → 3.4.2
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/CHANGELOG.md +16 -0
- package/dist/api.d.ts +3 -1
- package/dist/api.js +49 -1
- package/dist/application/migration/MigrationEngine.d.ts +141 -0
- package/dist/application/migration/MigrationEngine.js +322 -0
- package/dist/application/migration/index.d.ts +5 -0
- package/dist/application/migration/index.js +5 -0
- package/dist/application/sync/SyncEngine.d.ts +134 -0
- package/dist/application/sync/SyncEngine.js +335 -0
- package/dist/application/sync/index.d.ts +5 -0
- package/dist/application/sync/index.js +5 -0
- package/dist/cli/commands/create-attribute.js +1 -1
- package/dist/cli/commands/create-customer.d.ts +3 -0
- package/dist/cli/commands/create-customer.js +159 -0
- package/dist/cli/commands/diff.d.ts +6 -0
- package/dist/cli/commands/diff.js +288 -0
- package/dist/cli/commands/help.js +63 -3
- package/dist/cli/commands/logs.d.ts +18 -0
- package/dist/cli/commands/logs.js +283 -0
- package/dist/cli/commands/pull.js +114 -10
- package/dist/cli/commands/push.js +122 -12
- package/dist/cli/commands/update-attribute.d.ts +3 -0
- package/dist/cli/commands/update-attribute.js +78 -0
- package/dist/cli/commands/watch.d.ts +6 -0
- package/dist/cli/commands/watch.js +195 -0
- package/dist/cli-new/bootstrap.d.ts +74 -0
- package/dist/cli-new/bootstrap.js +154 -0
- package/dist/cli-new/di/Container.d.ts +64 -0
- package/dist/cli-new/di/Container.js +122 -0
- package/dist/cli-new/di/tokens.d.ts +77 -0
- package/dist/cli-new/di/tokens.js +76 -0
- package/dist/cli.js +20 -0
- package/dist/domain/resources/common/types.d.ts +71 -0
- package/dist/domain/resources/common/types.js +42 -0
- package/dist/domain/strategies/sync/AkbSyncStrategy.d.ts +63 -0
- package/dist/domain/strategies/sync/AkbSyncStrategy.js +274 -0
- package/dist/domain/strategies/sync/AttributeSyncStrategy.d.ts +87 -0
- package/dist/domain/strategies/sync/AttributeSyncStrategy.js +378 -0
- package/dist/domain/strategies/sync/ConversationSyncStrategy.d.ts +61 -0
- package/dist/domain/strategies/sync/ConversationSyncStrategy.js +232 -0
- package/dist/domain/strategies/sync/ISyncStrategy.d.ts +149 -0
- package/dist/domain/strategies/sync/ISyncStrategy.js +24 -0
- package/dist/domain/strategies/sync/IntegrationSyncStrategy.d.ts +68 -0
- package/dist/domain/strategies/sync/IntegrationSyncStrategy.js +413 -0
- package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +111 -0
- package/dist/domain/strategies/sync/ProjectSyncStrategy.js +523 -0
- package/dist/domain/strategies/sync/index.d.ts +13 -0
- package/dist/domain/strategies/sync/index.js +19 -0
- package/dist/sync/migrate.js +99 -23
- package/dist/types.d.ts +124 -0
- package/package.json +3 -1
- package/src/api.ts +53 -2
- package/src/application/migration/MigrationEngine.ts +492 -0
- package/src/application/migration/index.ts +5 -0
- package/src/application/sync/SyncEngine.ts +467 -0
- package/src/application/sync/index.ts +5 -0
- package/src/cli/commands/create-attribute.ts +1 -1
- package/src/cli/commands/create-customer.ts +185 -0
- package/src/cli/commands/diff.ts +360 -0
- package/src/cli/commands/help.ts +63 -3
- package/src/cli/commands/logs.ts +329 -0
- package/src/cli/commands/pull.ts +128 -11
- package/src/cli/commands/push.ts +131 -13
- package/src/cli/commands/update-attribute.ts +82 -0
- package/src/cli/commands/watch.ts +227 -0
- package/src/cli-new/bootstrap.ts +252 -0
- package/src/cli-new/di/Container.ts +152 -0
- package/src/cli-new/di/tokens.ts +105 -0
- package/src/cli.ts +25 -0
- package/src/domain/resources/common/types.ts +106 -0
- package/src/domain/strategies/sync/AkbSyncStrategy.ts +358 -0
- package/src/domain/strategies/sync/AttributeSyncStrategy.ts +508 -0
- package/src/domain/strategies/sync/ConversationSyncStrategy.ts +299 -0
- package/src/domain/strategies/sync/ISyncStrategy.ts +182 -0
- package/src/domain/strategies/sync/IntegrationSyncStrategy.ts +522 -0
- package/src/domain/strategies/sync/ProjectSyncStrategy.ts +747 -0
- package/src/domain/strategies/sync/index.ts +46 -0
- package/src/sync/migrate.ts +103 -24
- package/src/types.ts +135 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch command handler
|
|
3
|
+
*
|
|
4
|
+
* Watches for file changes and automatically pushes when changes are detected.
|
|
5
|
+
* Supports selective resource watching with --only and --exclude flags.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* newo watch # Watch and push all changes
|
|
9
|
+
* newo watch --only projects # Watch only project files
|
|
10
|
+
* newo watch --debounce 2000 # Custom debounce delay (ms)
|
|
11
|
+
*/
|
|
12
|
+
import { selectSingleCustomer } from '../customer-selection.js';
|
|
13
|
+
import { setupCli } from '../../cli-new/bootstrap.js';
|
|
14
|
+
import { PUSHABLE_RESOURCE_TYPES } from '../../cli-new/di/tokens.js';
|
|
15
|
+
import chokidar from 'chokidar';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
// Default debounce delay in milliseconds
|
|
18
|
+
const DEFAULT_DEBOUNCE_MS = 1000;
|
|
19
|
+
/**
|
|
20
|
+
* Parse resource list from comma-separated string
|
|
21
|
+
*/
|
|
22
|
+
function parseResourceList(input) {
|
|
23
|
+
if (!input)
|
|
24
|
+
return [];
|
|
25
|
+
return input.split(',').map(r => r.trim().toLowerCase()).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get file patterns to watch based on resource types
|
|
29
|
+
*/
|
|
30
|
+
function getWatchPatterns(customerDir, resources) {
|
|
31
|
+
const patterns = [];
|
|
32
|
+
for (const resource of resources) {
|
|
33
|
+
switch (resource) {
|
|
34
|
+
case 'projects':
|
|
35
|
+
// Watch .guidance and .jinja files in projects
|
|
36
|
+
patterns.push(path.join(customerDir, 'projects', '**', '*.guidance'));
|
|
37
|
+
patterns.push(path.join(customerDir, 'projects', '**', '*.jinja'));
|
|
38
|
+
patterns.push(path.join(customerDir, 'projects', '**', 'metadata.yaml'));
|
|
39
|
+
break;
|
|
40
|
+
case 'attributes':
|
|
41
|
+
// Watch attributes.yaml files
|
|
42
|
+
patterns.push(path.join(customerDir, 'attributes.yaml'));
|
|
43
|
+
patterns.push(path.join(customerDir, 'projects', '*', 'attributes.yaml'));
|
|
44
|
+
break;
|
|
45
|
+
case 'integrations':
|
|
46
|
+
// Watch integration files
|
|
47
|
+
patterns.push(path.join(customerDir, 'integrations', '**', '*.yaml'));
|
|
48
|
+
break;
|
|
49
|
+
case 'akb':
|
|
50
|
+
// Watch AKB files
|
|
51
|
+
patterns.push(path.join(customerDir, 'akb', '**', '*.yaml'));
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return patterns;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Push with V2 engine for selective resources
|
|
59
|
+
*/
|
|
60
|
+
async function pushWithV2Engine(customerConfig, customer, resources, verbose) {
|
|
61
|
+
const { syncEngine, logger } = setupCli(customerConfig, verbose);
|
|
62
|
+
const result = await syncEngine.pushSelected(customer, resources);
|
|
63
|
+
if (result.totalCreated > 0 || result.totalUpdated > 0 || result.totalDeleted > 0) {
|
|
64
|
+
logger.info(`✅ Pushed: ${result.totalCreated} created, ${result.totalUpdated} updated, ${result.totalDeleted} deleted`);
|
|
65
|
+
}
|
|
66
|
+
if (result.errors.length > 0) {
|
|
67
|
+
logger.warn(`⚠️ ${result.errors.length} error(s) occurred`);
|
|
68
|
+
result.errors.forEach(e => logger.error(` ${e}`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Main watch command handler
|
|
73
|
+
*/
|
|
74
|
+
export async function handleWatchCommand(customerConfig, args, verbose) {
|
|
75
|
+
const { selectedCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
76
|
+
if (!selectedCustomer) {
|
|
77
|
+
console.error('❌ Please specify a customer with --customer <idn> or set a default');
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
// Parse options
|
|
81
|
+
const onlyResources = parseResourceList(args.only);
|
|
82
|
+
const excludeResources = parseResourceList(args.exclude);
|
|
83
|
+
const debounceMs = typeof args.debounce === 'number'
|
|
84
|
+
? args.debounce
|
|
85
|
+
: (typeof args.debounce === 'string' ? parseInt(args.debounce, 10) : DEFAULT_DEBOUNCE_MS);
|
|
86
|
+
// Determine resources to watch
|
|
87
|
+
let resourcesToWatch;
|
|
88
|
+
if (onlyResources.length > 0) {
|
|
89
|
+
resourcesToWatch = onlyResources.filter(r => PUSHABLE_RESOURCE_TYPES.includes(r));
|
|
90
|
+
}
|
|
91
|
+
else if (excludeResources.length > 0) {
|
|
92
|
+
resourcesToWatch = PUSHABLE_RESOURCE_TYPES.filter(r => !excludeResources.includes(r));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
resourcesToWatch = [...PUSHABLE_RESOURCE_TYPES];
|
|
96
|
+
}
|
|
97
|
+
if (resourcesToWatch.length === 0) {
|
|
98
|
+
console.error('❌ No valid resources to watch');
|
|
99
|
+
console.error(` Available: ${PUSHABLE_RESOURCE_TYPES.join(', ')}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
const customerDir = path.join(process.cwd(), 'newo_customers', selectedCustomer.idn);
|
|
103
|
+
const watchPatterns = getWatchPatterns(customerDir, resourcesToWatch);
|
|
104
|
+
console.log(`👀 Watching for changes in: ${resourcesToWatch.join(', ')}`);
|
|
105
|
+
console.log(`📁 Customer: ${selectedCustomer.idn}`);
|
|
106
|
+
console.log(`⏱️ Debounce: ${debounceMs}ms`);
|
|
107
|
+
console.log('');
|
|
108
|
+
console.log('Press Ctrl+C to stop watching.');
|
|
109
|
+
console.log('');
|
|
110
|
+
// Debounce state
|
|
111
|
+
let debounceTimer = null;
|
|
112
|
+
let pendingChanges = new Set();
|
|
113
|
+
let isPushing = false;
|
|
114
|
+
// Push function with debouncing
|
|
115
|
+
const debouncedPush = () => {
|
|
116
|
+
if (debounceTimer) {
|
|
117
|
+
clearTimeout(debounceTimer);
|
|
118
|
+
}
|
|
119
|
+
debounceTimer = setTimeout(async () => {
|
|
120
|
+
if (isPushing) {
|
|
121
|
+
// If already pushing, wait and try again
|
|
122
|
+
debouncedPush();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (pendingChanges.size === 0) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const changedFiles = Array.from(pendingChanges);
|
|
129
|
+
pendingChanges.clear();
|
|
130
|
+
isPushing = true;
|
|
131
|
+
console.log(`\n🔄 Changes detected in ${changedFiles.length} file(s):`);
|
|
132
|
+
changedFiles.slice(0, 5).forEach(f => console.log(` ${path.relative(process.cwd(), f)}`));
|
|
133
|
+
if (changedFiles.length > 5) {
|
|
134
|
+
console.log(` ... and ${changedFiles.length - 5} more`);
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
// Use V2 engine for selective push
|
|
138
|
+
await pushWithV2Engine(customerConfig, selectedCustomer, resourcesToWatch, verbose);
|
|
139
|
+
console.log('✅ Push completed');
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.error('❌ Push failed:', error instanceof Error ? error.message : String(error));
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
isPushing = false;
|
|
146
|
+
}
|
|
147
|
+
}, debounceMs);
|
|
148
|
+
};
|
|
149
|
+
// Set up file watcher
|
|
150
|
+
const watcher = chokidar.watch(watchPatterns, {
|
|
151
|
+
persistent: true,
|
|
152
|
+
ignoreInitial: true,
|
|
153
|
+
awaitWriteFinish: {
|
|
154
|
+
stabilityThreshold: 300,
|
|
155
|
+
pollInterval: 100
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
watcher
|
|
159
|
+
.on('change', (filePath) => {
|
|
160
|
+
pendingChanges.add(filePath);
|
|
161
|
+
if (verbose) {
|
|
162
|
+
console.log(`📝 Changed: ${path.relative(process.cwd(), filePath)}`);
|
|
163
|
+
}
|
|
164
|
+
debouncedPush();
|
|
165
|
+
})
|
|
166
|
+
.on('add', (filePath) => {
|
|
167
|
+
pendingChanges.add(filePath);
|
|
168
|
+
if (verbose) {
|
|
169
|
+
console.log(`➕ Added: ${path.relative(process.cwd(), filePath)}`);
|
|
170
|
+
}
|
|
171
|
+
debouncedPush();
|
|
172
|
+
})
|
|
173
|
+
.on('unlink', (filePath) => {
|
|
174
|
+
pendingChanges.add(filePath);
|
|
175
|
+
if (verbose) {
|
|
176
|
+
console.log(`➖ Removed: ${path.relative(process.cwd(), filePath)}`);
|
|
177
|
+
}
|
|
178
|
+
debouncedPush();
|
|
179
|
+
})
|
|
180
|
+
.on('error', (error) => {
|
|
181
|
+
console.error('❌ Watcher error:', error);
|
|
182
|
+
});
|
|
183
|
+
// Handle graceful shutdown
|
|
184
|
+
process.on('SIGINT', () => {
|
|
185
|
+
console.log('\n\n👋 Stopping watch...');
|
|
186
|
+
watcher.close();
|
|
187
|
+
if (debounceTimer) {
|
|
188
|
+
clearTimeout(debounceTimer);
|
|
189
|
+
}
|
|
190
|
+
process.exit(0);
|
|
191
|
+
});
|
|
192
|
+
// Keep the process running
|
|
193
|
+
await new Promise(() => { }); // Never resolves - keeps process alive
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap - Application Initialization and DI Setup
|
|
3
|
+
*
|
|
4
|
+
* This file wires all dependencies together and creates the service container.
|
|
5
|
+
* It's the single entry point for configuring the application.
|
|
6
|
+
*/
|
|
7
|
+
import { ServiceContainer } from './di/Container.js';
|
|
8
|
+
import { type ILogger, type CustomerConfig, type MultiCustomerConfig } from '../domain/resources/common/types.js';
|
|
9
|
+
import { SyncEngine, type SyncEngineOptions } from '../application/sync/SyncEngine.js';
|
|
10
|
+
import { MigrationEngine } from '../application/migration/MigrationEngine.js';
|
|
11
|
+
import type { AxiosInstance } from 'axios';
|
|
12
|
+
/**
|
|
13
|
+
* API Client Factory that creates authenticated clients
|
|
14
|
+
*/
|
|
15
|
+
export declare function createApiClient(customer: CustomerConfig, verbose: boolean): Promise<AxiosInstance>;
|
|
16
|
+
/**
|
|
17
|
+
* Bootstrap options
|
|
18
|
+
*/
|
|
19
|
+
export interface BootstrapOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Enable verbose logging
|
|
22
|
+
*/
|
|
23
|
+
verbose?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Sync engine options
|
|
26
|
+
*/
|
|
27
|
+
syncEngineOptions?: SyncEngineOptions;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create and configure the service container
|
|
31
|
+
*/
|
|
32
|
+
export declare function createServiceContainer(customerConfig: MultiCustomerConfig, options?: BootstrapOptions): ServiceContainer;
|
|
33
|
+
/**
|
|
34
|
+
* Get SyncEngine from container
|
|
35
|
+
*/
|
|
36
|
+
export declare function getSyncEngine(container: ServiceContainer): SyncEngine;
|
|
37
|
+
/**
|
|
38
|
+
* Get MigrationEngine from container
|
|
39
|
+
*/
|
|
40
|
+
export declare function getMigrationEngine(container: ServiceContainer): MigrationEngine;
|
|
41
|
+
/**
|
|
42
|
+
* Get Logger from container
|
|
43
|
+
*/
|
|
44
|
+
export declare function getLogger(container: ServiceContainer): ILogger;
|
|
45
|
+
/**
|
|
46
|
+
* Quick setup for CLI commands
|
|
47
|
+
*
|
|
48
|
+
* Creates a configured container with all services ready to use.
|
|
49
|
+
*/
|
|
50
|
+
export declare function setupCli(customerConfig: MultiCustomerConfig, verbose?: boolean): {
|
|
51
|
+
container: ServiceContainer;
|
|
52
|
+
syncEngine: SyncEngine;
|
|
53
|
+
migrationEngine: MigrationEngine;
|
|
54
|
+
logger: ILogger;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Adapter function for legacy pull command
|
|
58
|
+
*
|
|
59
|
+
* This provides backward compatibility with the existing CLI.
|
|
60
|
+
*/
|
|
61
|
+
export declare function legacyPullAdapter(customerConfig: MultiCustomerConfig, customer: CustomerConfig, verbose: boolean, silentOverwrite: boolean): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Adapter function for legacy push command
|
|
64
|
+
*/
|
|
65
|
+
export declare function legacyPushAdapter(customerConfig: MultiCustomerConfig, customer: CustomerConfig, verbose: boolean): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Adapter function for legacy status command
|
|
68
|
+
*/
|
|
69
|
+
export declare function legacyStatusAdapter(customerConfig: MultiCustomerConfig, customer: CustomerConfig, verbose: boolean): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Adapter function for legacy migrate command
|
|
72
|
+
*/
|
|
73
|
+
export declare function legacyMigrateAdapter(customerConfig: MultiCustomerConfig, sourceCustomer: CustomerConfig, destCustomer: CustomerConfig, sourceClient: AxiosInstance, destClient: AxiosInstance, verbose: boolean): Promise<void>;
|
|
74
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap - Application Initialization and DI Setup
|
|
3
|
+
*
|
|
4
|
+
* This file wires all dependencies together and creates the service container.
|
|
5
|
+
* It's the single entry point for configuring the application.
|
|
6
|
+
*/
|
|
7
|
+
import { ServiceContainer } from './di/Container.js';
|
|
8
|
+
import { TOKENS } from './di/tokens.js';
|
|
9
|
+
import { ConsoleLogger } from '../domain/resources/common/types.js';
|
|
10
|
+
import { SyncEngine } from '../application/sync/SyncEngine.js';
|
|
11
|
+
import { MigrationEngine, TransformService } from '../application/migration/MigrationEngine.js';
|
|
12
|
+
import { ProjectSyncStrategy, createProjectSyncStrategy } from '../domain/strategies/sync/ProjectSyncStrategy.js';
|
|
13
|
+
import { AttributeSyncStrategy, createAttributeSyncStrategy } from '../domain/strategies/sync/AttributeSyncStrategy.js';
|
|
14
|
+
import { IntegrationSyncStrategy, createIntegrationSyncStrategy } from '../domain/strategies/sync/IntegrationSyncStrategy.js';
|
|
15
|
+
import { AkbSyncStrategy, createAkbSyncStrategy } from '../domain/strategies/sync/AkbSyncStrategy.js';
|
|
16
|
+
import { ConversationSyncStrategy, createConversationSyncStrategy } from '../domain/strategies/sync/ConversationSyncStrategy.js';
|
|
17
|
+
import { makeClient } from '../api.js';
|
|
18
|
+
import { getValidAccessToken } from '../auth.js';
|
|
19
|
+
/**
|
|
20
|
+
* API Client Factory that creates authenticated clients
|
|
21
|
+
*/
|
|
22
|
+
export async function createApiClient(customer, verbose) {
|
|
23
|
+
// Set environment variables for the customer
|
|
24
|
+
process.env.NEWO_API_KEY = customer.apiKey;
|
|
25
|
+
if (customer.projectId) {
|
|
26
|
+
process.env.NEWO_PROJECT_ID = customer.projectId;
|
|
27
|
+
}
|
|
28
|
+
const token = await getValidAccessToken();
|
|
29
|
+
return makeClient(verbose, token);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create and configure the service container
|
|
33
|
+
*/
|
|
34
|
+
export function createServiceContainer(customerConfig, options = {}) {
|
|
35
|
+
const container = new ServiceContainer();
|
|
36
|
+
const verbose = options.verbose ?? false;
|
|
37
|
+
// === Infrastructure Layer ===
|
|
38
|
+
// Logger
|
|
39
|
+
const logger = new ConsoleLogger(verbose);
|
|
40
|
+
container.registerValue(TOKENS.LOGGER, logger);
|
|
41
|
+
// Customer Config
|
|
42
|
+
container.registerValue(TOKENS.CUSTOMER_CONFIG, customerConfig);
|
|
43
|
+
// API Client Factory
|
|
44
|
+
container.registerValue(TOKENS.API_CLIENT_FACTORY, createApiClient);
|
|
45
|
+
// === Domain Layer - Sync Strategies ===
|
|
46
|
+
// Project Sync Strategy
|
|
47
|
+
container.registerSingleton(TOKENS.PROJECT_SYNC_STRATEGY, () => createProjectSyncStrategy(createApiClient, container.get(TOKENS.LOGGER)));
|
|
48
|
+
// Attribute Sync Strategy
|
|
49
|
+
container.registerSingleton(TOKENS.ATTRIBUTE_SYNC_STRATEGY, () => createAttributeSyncStrategy(createApiClient, container.get(TOKENS.LOGGER)));
|
|
50
|
+
// Integration Sync Strategy
|
|
51
|
+
container.registerSingleton(TOKENS.INTEGRATION_SYNC_STRATEGY, () => createIntegrationSyncStrategy(createApiClient, container.get(TOKENS.LOGGER)));
|
|
52
|
+
// AKB Sync Strategy
|
|
53
|
+
container.registerSingleton(TOKENS.AKB_SYNC_STRATEGY, () => createAkbSyncStrategy(createApiClient, container.get(TOKENS.LOGGER)));
|
|
54
|
+
// Conversation Sync Strategy
|
|
55
|
+
container.registerSingleton(TOKENS.CONVERSATION_SYNC_STRATEGY, () => createConversationSyncStrategy(createApiClient, container.get(TOKENS.LOGGER)));
|
|
56
|
+
// === Application Layer ===
|
|
57
|
+
// Sync Engine (uses all sync strategies)
|
|
58
|
+
container.registerSingleton(TOKENS.SYNC_ENGINE, () => {
|
|
59
|
+
const strategies = [
|
|
60
|
+
container.get(TOKENS.PROJECT_SYNC_STRATEGY),
|
|
61
|
+
container.get(TOKENS.ATTRIBUTE_SYNC_STRATEGY),
|
|
62
|
+
container.get(TOKENS.INTEGRATION_SYNC_STRATEGY),
|
|
63
|
+
container.get(TOKENS.AKB_SYNC_STRATEGY),
|
|
64
|
+
container.get(TOKENS.CONVERSATION_SYNC_STRATEGY),
|
|
65
|
+
];
|
|
66
|
+
return new SyncEngine(strategies, container.get(TOKENS.LOGGER), options.syncEngineOptions);
|
|
67
|
+
});
|
|
68
|
+
// Migration Engine (uses SyncEngine)
|
|
69
|
+
container.registerSingleton(TOKENS.MIGRATION_ENGINE, () => {
|
|
70
|
+
const syncEngine = container.get(TOKENS.SYNC_ENGINE);
|
|
71
|
+
const transformService = new TransformService(container.get(TOKENS.LOGGER));
|
|
72
|
+
return new MigrationEngine(syncEngine, transformService, container.get(TOKENS.LOGGER));
|
|
73
|
+
});
|
|
74
|
+
return container;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get SyncEngine from container
|
|
78
|
+
*/
|
|
79
|
+
export function getSyncEngine(container) {
|
|
80
|
+
return container.get(TOKENS.SYNC_ENGINE);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get MigrationEngine from container
|
|
84
|
+
*/
|
|
85
|
+
export function getMigrationEngine(container) {
|
|
86
|
+
return container.get(TOKENS.MIGRATION_ENGINE);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get Logger from container
|
|
90
|
+
*/
|
|
91
|
+
export function getLogger(container) {
|
|
92
|
+
return container.get(TOKENS.LOGGER);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Quick setup for CLI commands
|
|
96
|
+
*
|
|
97
|
+
* Creates a configured container with all services ready to use.
|
|
98
|
+
*/
|
|
99
|
+
export function setupCli(customerConfig, verbose = false) {
|
|
100
|
+
const container = createServiceContainer(customerConfig, { verbose });
|
|
101
|
+
return {
|
|
102
|
+
container,
|
|
103
|
+
syncEngine: getSyncEngine(container),
|
|
104
|
+
migrationEngine: getMigrationEngine(container),
|
|
105
|
+
logger: getLogger(container)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Adapter function for legacy pull command
|
|
110
|
+
*
|
|
111
|
+
* This provides backward compatibility with the existing CLI.
|
|
112
|
+
*/
|
|
113
|
+
export async function legacyPullAdapter(customerConfig, customer, verbose, silentOverwrite) {
|
|
114
|
+
const { syncEngine } = setupCli(customerConfig, verbose);
|
|
115
|
+
await syncEngine.pullAll(customer, {
|
|
116
|
+
silentOverwrite,
|
|
117
|
+
verbose
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Adapter function for legacy push command
|
|
122
|
+
*/
|
|
123
|
+
export async function legacyPushAdapter(customerConfig, customer, verbose) {
|
|
124
|
+
const { syncEngine } = setupCli(customerConfig, verbose);
|
|
125
|
+
await syncEngine.pushAll(customer);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Adapter function for legacy status command
|
|
129
|
+
*/
|
|
130
|
+
export async function legacyStatusAdapter(customerConfig, customer, verbose) {
|
|
131
|
+
const { syncEngine, logger } = setupCli(customerConfig, verbose);
|
|
132
|
+
const status = await syncEngine.getStatus(customer);
|
|
133
|
+
logger.info(`\nStatus for customer: ${status.customer}`);
|
|
134
|
+
logger.info(`Total changes: ${status.totalChanges}\n`);
|
|
135
|
+
for (const resource of status.resources) {
|
|
136
|
+
if (resource.changedCount > 0) {
|
|
137
|
+
logger.info(`${resource.displayName}: ${resource.changedCount} change(s)`);
|
|
138
|
+
for (const change of resource.changes) {
|
|
139
|
+
logger.info(` ${change.operation.toUpperCase()[0]} ${change.path}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (status.totalChanges === 0) {
|
|
144
|
+
logger.info('No changes to push.');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Adapter function for legacy migrate command
|
|
149
|
+
*/
|
|
150
|
+
export async function legacyMigrateAdapter(customerConfig, sourceCustomer, destCustomer, sourceClient, destClient, verbose) {
|
|
151
|
+
const { migrationEngine } = setupCli(customerConfig, verbose);
|
|
152
|
+
await migrationEngine.migrateAccount(sourceCustomer, destCustomer, sourceClient, destClient, { verbose });
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Injection Container
|
|
3
|
+
*
|
|
4
|
+
* A simple, TypeScript-native DI container that:
|
|
5
|
+
* - Supports singleton and factory registrations
|
|
6
|
+
* - Provides type-safe dependency resolution
|
|
7
|
+
* - Enables easy testing through dependency injection
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Factory function type for creating service instances
|
|
11
|
+
*/
|
|
12
|
+
type Factory<T> = (container: ServiceContainer) => T;
|
|
13
|
+
/**
|
|
14
|
+
* Service Container for Dependency Injection
|
|
15
|
+
*/
|
|
16
|
+
export declare class ServiceContainer {
|
|
17
|
+
private registrations;
|
|
18
|
+
/**
|
|
19
|
+
* Register a factory that creates a new instance each time
|
|
20
|
+
*/
|
|
21
|
+
register<T>(token: symbol, factory: Factory<T>): void;
|
|
22
|
+
/**
|
|
23
|
+
* Register a singleton - only created once
|
|
24
|
+
*/
|
|
25
|
+
registerSingleton<T>(token: symbol, instanceOrFactory: T | Factory<T>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Register a value directly
|
|
28
|
+
*/
|
|
29
|
+
registerValue<T>(token: symbol, value: T): void;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a service by its token
|
|
32
|
+
*/
|
|
33
|
+
get<T>(token: symbol): T;
|
|
34
|
+
/**
|
|
35
|
+
* Check if a token is registered
|
|
36
|
+
*/
|
|
37
|
+
has(token: symbol): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get all registered tokens
|
|
40
|
+
*/
|
|
41
|
+
getTokens(): symbol[];
|
|
42
|
+
/**
|
|
43
|
+
* Clear all registrations (useful for testing)
|
|
44
|
+
*/
|
|
45
|
+
clear(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Create a child container that inherits parent registrations
|
|
48
|
+
*/
|
|
49
|
+
createChild(): ServiceContainer;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the global container instance
|
|
53
|
+
*/
|
|
54
|
+
export declare function getContainer(): ServiceContainer;
|
|
55
|
+
/**
|
|
56
|
+
* Set the global container instance (useful for testing)
|
|
57
|
+
*/
|
|
58
|
+
export declare function setContainer(container: ServiceContainer): void;
|
|
59
|
+
/**
|
|
60
|
+
* Reset the global container (useful for testing)
|
|
61
|
+
*/
|
|
62
|
+
export declare function resetContainer(): void;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=Container.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Injection Container
|
|
3
|
+
*
|
|
4
|
+
* A simple, TypeScript-native DI container that:
|
|
5
|
+
* - Supports singleton and factory registrations
|
|
6
|
+
* - Provides type-safe dependency resolution
|
|
7
|
+
* - Enables easy testing through dependency injection
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Service Container for Dependency Injection
|
|
11
|
+
*/
|
|
12
|
+
export class ServiceContainer {
|
|
13
|
+
registrations = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Register a factory that creates a new instance each time
|
|
16
|
+
*/
|
|
17
|
+
register(token, factory) {
|
|
18
|
+
this.registrations.set(token, {
|
|
19
|
+
factory,
|
|
20
|
+
singleton: false
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Register a singleton - only created once
|
|
25
|
+
*/
|
|
26
|
+
registerSingleton(token, instanceOrFactory) {
|
|
27
|
+
if (typeof instanceOrFactory === 'function') {
|
|
28
|
+
this.registrations.set(token, {
|
|
29
|
+
factory: instanceOrFactory,
|
|
30
|
+
singleton: true
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.registrations.set(token, {
|
|
35
|
+
factory: () => instanceOrFactory,
|
|
36
|
+
singleton: true,
|
|
37
|
+
instance: instanceOrFactory
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Register a value directly
|
|
43
|
+
*/
|
|
44
|
+
registerValue(token, value) {
|
|
45
|
+
this.registrations.set(token, {
|
|
46
|
+
factory: () => value,
|
|
47
|
+
singleton: true,
|
|
48
|
+
instance: value
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a service by its token
|
|
53
|
+
*/
|
|
54
|
+
get(token) {
|
|
55
|
+
const registration = this.registrations.get(token);
|
|
56
|
+
if (!registration) {
|
|
57
|
+
throw new Error(`No registration found for token: ${token.toString()}`);
|
|
58
|
+
}
|
|
59
|
+
if (registration.singleton) {
|
|
60
|
+
if (registration.instance === undefined) {
|
|
61
|
+
registration.instance = registration.factory(this);
|
|
62
|
+
}
|
|
63
|
+
return registration.instance;
|
|
64
|
+
}
|
|
65
|
+
return registration.factory(this);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if a token is registered
|
|
69
|
+
*/
|
|
70
|
+
has(token) {
|
|
71
|
+
return this.registrations.has(token);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get all registered tokens
|
|
75
|
+
*/
|
|
76
|
+
getTokens() {
|
|
77
|
+
return Array.from(this.registrations.keys());
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Clear all registrations (useful for testing)
|
|
81
|
+
*/
|
|
82
|
+
clear() {
|
|
83
|
+
this.registrations.clear();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a child container that inherits parent registrations
|
|
87
|
+
*/
|
|
88
|
+
createChild() {
|
|
89
|
+
const child = new ServiceContainer();
|
|
90
|
+
// Copy parent registrations
|
|
91
|
+
for (const [token, registration] of this.registrations) {
|
|
92
|
+
child.registrations.set(token, { ...registration });
|
|
93
|
+
}
|
|
94
|
+
return child;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Global container instance
|
|
99
|
+
*/
|
|
100
|
+
let globalContainer = null;
|
|
101
|
+
/**
|
|
102
|
+
* Get the global container instance
|
|
103
|
+
*/
|
|
104
|
+
export function getContainer() {
|
|
105
|
+
if (!globalContainer) {
|
|
106
|
+
globalContainer = new ServiceContainer();
|
|
107
|
+
}
|
|
108
|
+
return globalContainer;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Set the global container instance (useful for testing)
|
|
112
|
+
*/
|
|
113
|
+
export function setContainer(container) {
|
|
114
|
+
globalContainer = container;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Reset the global container (useful for testing)
|
|
118
|
+
*/
|
|
119
|
+
export function resetContainer() {
|
|
120
|
+
globalContainer = null;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=Container.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Injection Tokens
|
|
3
|
+
*
|
|
4
|
+
* These symbols are used as unique identifiers for services in the DI container.
|
|
5
|
+
* Using symbols ensures no naming conflicts.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TOKENS: {
|
|
8
|
+
readonly LOGGER: symbol;
|
|
9
|
+
readonly AUTH_SERVICE: symbol;
|
|
10
|
+
readonly API_CLIENT: symbol;
|
|
11
|
+
readonly API_CLIENT_FACTORY: symbol;
|
|
12
|
+
readonly FILE_SYSTEM: symbol;
|
|
13
|
+
readonly HASH_MANAGER: symbol;
|
|
14
|
+
readonly METADATA_GENERATOR: symbol;
|
|
15
|
+
readonly PROJECT_REPOSITORY: symbol;
|
|
16
|
+
readonly INTEGRATION_REPOSITORY: symbol;
|
|
17
|
+
readonly AKB_REPOSITORY: symbol;
|
|
18
|
+
readonly ATTRIBUTE_REPOSITORY: symbol;
|
|
19
|
+
readonly CONVERSATION_REPOSITORY: symbol;
|
|
20
|
+
readonly PROJECT_SYNC_STRATEGY: symbol;
|
|
21
|
+
readonly INTEGRATION_SYNC_STRATEGY: symbol;
|
|
22
|
+
readonly AKB_SYNC_STRATEGY: symbol;
|
|
23
|
+
readonly ATTRIBUTE_SYNC_STRATEGY: symbol;
|
|
24
|
+
readonly CONVERSATION_SYNC_STRATEGY: symbol;
|
|
25
|
+
readonly PROJECT_ENTITY_STRATEGY: symbol;
|
|
26
|
+
readonly AGENT_ENTITY_STRATEGY: symbol;
|
|
27
|
+
readonly FLOW_ENTITY_STRATEGY: symbol;
|
|
28
|
+
readonly SKILL_ENTITY_STRATEGY: symbol;
|
|
29
|
+
readonly SYNC_ENGINE: symbol;
|
|
30
|
+
readonly MIGRATION_ENGINE: symbol;
|
|
31
|
+
readonly ENTITY_MANAGER: symbol;
|
|
32
|
+
readonly PULL_USE_CASE: symbol;
|
|
33
|
+
readonly PUSH_USE_CASE: symbol;
|
|
34
|
+
readonly STATUS_USE_CASE: symbol;
|
|
35
|
+
readonly MIGRATE_USE_CASE: symbol;
|
|
36
|
+
readonly CREATE_ENTITY_USE_CASE: symbol;
|
|
37
|
+
readonly DELETE_ENTITY_USE_CASE: symbol;
|
|
38
|
+
readonly COMMAND_REGISTRY: symbol;
|
|
39
|
+
readonly COMMAND_EXECUTOR: symbol;
|
|
40
|
+
readonly ERROR_HANDLER: symbol;
|
|
41
|
+
readonly CUSTOMER_SELECTOR: symbol;
|
|
42
|
+
readonly CUSTOMER_CONFIG: symbol;
|
|
43
|
+
readonly ENVIRONMENT: symbol;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Type for the tokens object
|
|
47
|
+
*/
|
|
48
|
+
export type TokenKey = keyof typeof TOKENS;
|
|
49
|
+
export type Token = (typeof TOKENS)[TokenKey];
|
|
50
|
+
/**
|
|
51
|
+
* Resource types for selective sync
|
|
52
|
+
* These match the resourceType property in each ISyncStrategy implementation
|
|
53
|
+
*/
|
|
54
|
+
export declare const RESOURCE_TYPES: {
|
|
55
|
+
readonly PROJECTS: "projects";
|
|
56
|
+
readonly ATTRIBUTES: "attributes";
|
|
57
|
+
readonly INTEGRATIONS: "integrations";
|
|
58
|
+
readonly AKB: "akb";
|
|
59
|
+
readonly CONVERSATIONS: "conversations";
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* All available resource types for sync operations
|
|
63
|
+
*/
|
|
64
|
+
export declare const ALL_RESOURCE_TYPES: ("projects" | "integrations" | "akb" | "attributes" | "conversations")[];
|
|
65
|
+
/**
|
|
66
|
+
* Pushable resource types (excludes read-only resources like conversations)
|
|
67
|
+
*/
|
|
68
|
+
export declare const PUSHABLE_RESOURCE_TYPES: readonly ["projects", "attributes", "integrations", "akb"];
|
|
69
|
+
/**
|
|
70
|
+
* Type for resource type values
|
|
71
|
+
*/
|
|
72
|
+
export type ResourceType = (typeof RESOURCE_TYPES)[keyof typeof RESOURCE_TYPES];
|
|
73
|
+
/**
|
|
74
|
+
* Type for pushable resource types
|
|
75
|
+
*/
|
|
76
|
+
export type PushableResourceType = (typeof PUSHABLE_RESOURCE_TYPES)[number];
|
|
77
|
+
//# sourceMappingURL=tokens.d.ts.map
|