newo 3.3.3 → 3.4.1

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/api.d.ts +6 -1
  3. package/dist/api.js +63 -1
  4. package/dist/application/migration/MigrationEngine.d.ts +141 -0
  5. package/dist/application/migration/MigrationEngine.js +322 -0
  6. package/dist/application/migration/index.d.ts +5 -0
  7. package/dist/application/migration/index.js +5 -0
  8. package/dist/application/sync/SyncEngine.d.ts +134 -0
  9. package/dist/application/sync/SyncEngine.js +335 -0
  10. package/dist/application/sync/index.d.ts +5 -0
  11. package/dist/application/sync/index.js +5 -0
  12. package/dist/cli/commands/add-project.d.ts +3 -0
  13. package/dist/cli/commands/add-project.js +136 -0
  14. package/dist/cli/commands/create-customer.d.ts +3 -0
  15. package/dist/cli/commands/create-customer.js +159 -0
  16. package/dist/cli/commands/diff.d.ts +6 -0
  17. package/dist/cli/commands/diff.js +288 -0
  18. package/dist/cli/commands/help.js +75 -4
  19. package/dist/cli/commands/list-registries.d.ts +3 -0
  20. package/dist/cli/commands/list-registries.js +39 -0
  21. package/dist/cli/commands/list-registry-items.d.ts +3 -0
  22. package/dist/cli/commands/list-registry-items.js +112 -0
  23. package/dist/cli/commands/logs.d.ts +18 -0
  24. package/dist/cli/commands/logs.js +283 -0
  25. package/dist/cli/commands/pull.js +114 -10
  26. package/dist/cli/commands/push.js +122 -12
  27. package/dist/cli/commands/watch.d.ts +6 -0
  28. package/dist/cli/commands/watch.js +195 -0
  29. package/dist/cli-new/bootstrap.d.ts +74 -0
  30. package/dist/cli-new/bootstrap.js +154 -0
  31. package/dist/cli-new/di/Container.d.ts +64 -0
  32. package/dist/cli-new/di/Container.js +122 -0
  33. package/dist/cli-new/di/tokens.d.ts +77 -0
  34. package/dist/cli-new/di/tokens.js +76 -0
  35. package/dist/cli.js +28 -0
  36. package/dist/domain/resources/common/types.d.ts +71 -0
  37. package/dist/domain/resources/common/types.js +42 -0
  38. package/dist/domain/strategies/sync/AkbSyncStrategy.d.ts +63 -0
  39. package/dist/domain/strategies/sync/AkbSyncStrategy.js +274 -0
  40. package/dist/domain/strategies/sync/AttributeSyncStrategy.d.ts +87 -0
  41. package/dist/domain/strategies/sync/AttributeSyncStrategy.js +378 -0
  42. package/dist/domain/strategies/sync/ConversationSyncStrategy.d.ts +61 -0
  43. package/dist/domain/strategies/sync/ConversationSyncStrategy.js +232 -0
  44. package/dist/domain/strategies/sync/ISyncStrategy.d.ts +149 -0
  45. package/dist/domain/strategies/sync/ISyncStrategy.js +24 -0
  46. package/dist/domain/strategies/sync/IntegrationSyncStrategy.d.ts +68 -0
  47. package/dist/domain/strategies/sync/IntegrationSyncStrategy.js +413 -0
  48. package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +111 -0
  49. package/dist/domain/strategies/sync/ProjectSyncStrategy.js +523 -0
  50. package/dist/domain/strategies/sync/index.d.ts +13 -0
  51. package/dist/domain/strategies/sync/index.js +19 -0
  52. package/dist/sync/migrate.js +99 -23
  53. package/dist/types.d.ts +162 -0
  54. package/package.json +3 -1
  55. package/src/api.ts +77 -2
  56. package/src/application/migration/MigrationEngine.ts +492 -0
  57. package/src/application/migration/index.ts +5 -0
  58. package/src/application/sync/SyncEngine.ts +467 -0
  59. package/src/application/sync/index.ts +5 -0
  60. package/src/cli/commands/add-project.ts +159 -0
  61. package/src/cli/commands/create-customer.ts +185 -0
  62. package/src/cli/commands/diff.ts +360 -0
  63. package/src/cli/commands/help.ts +75 -4
  64. package/src/cli/commands/list-registries.ts +53 -0
  65. package/src/cli/commands/list-registry-items.ts +149 -0
  66. package/src/cli/commands/logs.ts +329 -0
  67. package/src/cli/commands/pull.ts +128 -11
  68. package/src/cli/commands/push.ts +131 -13
  69. package/src/cli/commands/watch.ts +227 -0
  70. package/src/cli-new/bootstrap.ts +252 -0
  71. package/src/cli-new/di/Container.ts +152 -0
  72. package/src/cli-new/di/tokens.ts +105 -0
  73. package/src/cli.ts +35 -0
  74. package/src/domain/resources/common/types.ts +106 -0
  75. package/src/domain/strategies/sync/AkbSyncStrategy.ts +358 -0
  76. package/src/domain/strategies/sync/AttributeSyncStrategy.ts +508 -0
  77. package/src/domain/strategies/sync/ConversationSyncStrategy.ts +299 -0
  78. package/src/domain/strategies/sync/ISyncStrategy.ts +182 -0
  79. package/src/domain/strategies/sync/IntegrationSyncStrategy.ts +522 -0
  80. package/src/domain/strategies/sync/ProjectSyncStrategy.ts +747 -0
  81. package/src/domain/strategies/sync/index.ts +46 -0
  82. package/src/sync/migrate.ts +103 -24
  83. package/src/types.ts +178 -0
@@ -1,37 +1,147 @@
1
1
  /**
2
2
  * Push command handler
3
+ *
4
+ * Supports selective resource sync with --only and --exclude flags:
5
+ * newo push --only projects,attributes
6
+ * newo push --exclude integrations
7
+ * newo push --all (explicit all resources)
8
+ *
9
+ * Available resources: projects, attributes, integrations, akb
10
+ * Note: conversations is read-only and cannot be pushed
3
11
  */
4
12
  import { makeClient } from '../../api.js';
5
13
  import { pushChanged } from '../../sync.js';
6
14
  import { getValidAccessToken } from '../../auth.js';
7
15
  import { selectSingleCustomer, interactiveCustomerSelection } from '../customer-selection.js';
16
+ import { setupCli } from '../../cli-new/bootstrap.js';
17
+ import { PUSHABLE_RESOURCE_TYPES } from '../../cli-new/di/tokens.js';
18
+ /**
19
+ * Parse resource list from comma-separated string
20
+ */
21
+ function parseResourceList(input) {
22
+ if (!input)
23
+ return [];
24
+ return input.split(',').map(r => r.trim().toLowerCase()).filter(Boolean);
25
+ }
26
+ /**
27
+ * Validate resource types for push
28
+ */
29
+ function validateResources(resources) {
30
+ const validTypes = new Set(PUSHABLE_RESOURCE_TYPES);
31
+ const valid = [];
32
+ const invalid = [];
33
+ for (const r of resources) {
34
+ if (r === 'conversations') {
35
+ invalid.push(r + ' (read-only)');
36
+ }
37
+ else if (validTypes.has(r)) {
38
+ valid.push(r);
39
+ }
40
+ else {
41
+ invalid.push(r);
42
+ }
43
+ }
44
+ return { valid, invalid };
45
+ }
46
+ /**
47
+ * Push using V2 SyncEngine with selective sync
48
+ */
49
+ async function pushWithV2Engine(customerConfig, customer, resources, verbose) {
50
+ const { syncEngine, logger } = setupCli(customerConfig, verbose);
51
+ if (resources === 'all') {
52
+ const result = await syncEngine.pushAll(customer);
53
+ logger.info(`✅ Pushed: ${result.totalCreated} created, ${result.totalUpdated} updated, ${result.totalDeleted} deleted`);
54
+ if (result.errors.length > 0) {
55
+ logger.warn(`⚠️ ${result.errors.length} error(s) occurred`);
56
+ result.errors.forEach(e => logger.error(` ${e}`));
57
+ }
58
+ }
59
+ else {
60
+ const result = await syncEngine.pushSelected(customer, resources);
61
+ logger.info(`✅ Pushed: ${result.totalCreated} created, ${result.totalUpdated} updated, ${result.totalDeleted} deleted`);
62
+ if (result.errors.length > 0) {
63
+ logger.warn(`⚠️ ${result.errors.length} error(s) occurred`);
64
+ result.errors.forEach(e => logger.error(` ${e}`));
65
+ }
66
+ }
67
+ }
8
68
  export async function handlePushCommand(customerConfig, args, verbose) {
9
69
  const { selectedCustomer, allCustomers, isMultiCustomer } = selectSingleCustomer(customerConfig, args.customer);
10
70
  const shouldPublish = !args['no-publish'];
71
+ // Check for selective sync flags
72
+ const onlyResources = parseResourceList(args.only);
73
+ const excludeResources = parseResourceList(args.exclude);
74
+ const pushAllResources = Boolean(args.all);
75
+ // Validate resource types
76
+ if (onlyResources.length > 0) {
77
+ const { invalid } = validateResources(onlyResources);
78
+ if (invalid.length > 0) {
79
+ console.error(`❌ Cannot push resource(s): ${invalid.join(', ')}`);
80
+ console.error(` Available for push: ${PUSHABLE_RESOURCE_TYPES.join(', ')}`);
81
+ process.exit(1);
82
+ }
83
+ }
84
+ if (excludeResources.length > 0) {
85
+ const { invalid } = validateResources(excludeResources);
86
+ if (invalid.length > 0 && !invalid.every(r => r.includes('read-only'))) {
87
+ console.error(`❌ Unknown resource type(s): ${invalid.join(', ')}`);
88
+ console.error(` Available: ${PUSHABLE_RESOURCE_TYPES.join(', ')}`);
89
+ process.exit(1);
90
+ }
91
+ }
92
+ // Determine which resources to push
93
+ let resourcesToPush = 'all';
94
+ if (onlyResources.length > 0) {
95
+ resourcesToPush = onlyResources;
96
+ console.log(`📦 Pushing selected resources: ${onlyResources.join(', ')}`);
97
+ }
98
+ else if (excludeResources.length > 0) {
99
+ resourcesToPush = PUSHABLE_RESOURCE_TYPES.filter(r => !excludeResources.includes(r));
100
+ console.log(`📦 Pushing resources (excluding: ${excludeResources.join(', ')})`);
101
+ }
102
+ else if (pushAllResources) {
103
+ resourcesToPush = 'all';
104
+ console.log(`📦 Pushing ALL resources`);
105
+ }
106
+ // Use V2 engine if selective sync requested, otherwise use legacy for backward compatibility
107
+ const useV2Engine = onlyResources.length > 0 || excludeResources.length > 0 || pushAllResources;
11
108
  if (selectedCustomer) {
12
- // Single customer push
13
- const accessToken = await getValidAccessToken(selectedCustomer);
14
- const client = await makeClient(verbose, accessToken);
15
- await pushChanged(client, selectedCustomer, verbose, shouldPublish);
109
+ if (useV2Engine) {
110
+ await pushWithV2Engine(customerConfig, selectedCustomer, resourcesToPush, verbose);
111
+ }
112
+ else {
113
+ // Legacy behavior
114
+ const accessToken = await getValidAccessToken(selectedCustomer);
115
+ const client = await makeClient(verbose, accessToken);
116
+ await pushChanged(client, selectedCustomer, verbose, shouldPublish);
117
+ }
16
118
  }
17
119
  else if (isMultiCustomer) {
18
120
  // Multiple customers exist with no default, ask user
19
121
  const customersToProcess = await interactiveCustomerSelection(allCustomers);
20
122
  if (customersToProcess.length === 1) {
21
- // Single customer selected
22
123
  const customer = customersToProcess[0];
23
- const accessToken = await getValidAccessToken(customer);
24
- const client = await makeClient(verbose, accessToken);
25
- await pushChanged(client, customer, verbose, shouldPublish);
124
+ if (useV2Engine) {
125
+ await pushWithV2Engine(customerConfig, customer, resourcesToPush, verbose);
126
+ }
127
+ else {
128
+ const accessToken = await getValidAccessToken(customer);
129
+ const client = await makeClient(verbose, accessToken);
130
+ await pushChanged(client, customer, verbose, shouldPublish);
131
+ }
26
132
  }
27
133
  else {
28
- // Multi-customer push (user selected "All customers")
29
134
  console.log(`🔄 Pushing to ${customersToProcess.length} customers...`);
30
135
  for (const customer of customersToProcess) {
31
136
  console.log(`\n📤 Pushing for customer: ${customer.idn}`);
32
- const accessToken = await getValidAccessToken(customer);
33
- const client = await makeClient(verbose, accessToken);
34
- await pushChanged(client, customer, verbose, shouldPublish);
137
+ if (useV2Engine) {
138
+ await pushWithV2Engine(customerConfig, customer, resourcesToPush, verbose);
139
+ }
140
+ else {
141
+ const accessToken = await getValidAccessToken(customer);
142
+ const client = await makeClient(verbose, accessToken);
143
+ await pushChanged(client, customer, verbose, shouldPublish);
144
+ }
35
145
  }
36
146
  console.log(`\n✅ Push completed for all ${customersToProcess.length} customers`);
37
147
  }
@@ -0,0 +1,6 @@
1
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
2
+ /**
3
+ * Main watch command handler
4
+ */
5
+ export declare function handleWatchCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose: boolean): Promise<void>;
6
+ //# sourceMappingURL=watch.d.ts.map
@@ -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