kiro-spec-engine 1.1.0 → 1.2.0
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 +43 -0
- package/README.md +88 -0
- package/README.zh.md +88 -0
- package/bin/kiro-spec-engine.js +35 -0
- package/lib/adoption/adoption-strategy.js +516 -0
- package/lib/adoption/detection-engine.js +242 -0
- package/lib/backup/backup-system.js +372 -0
- package/lib/commands/adopt.js +231 -0
- package/lib/commands/rollback.js +219 -0
- package/lib/commands/upgrade.js +231 -0
- package/lib/upgrade/migration-engine.js +364 -0
- package/lib/upgrade/migrations/.gitkeep +52 -0
- package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
- package/package.json +1 -1
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adoption Strategy
|
|
3
|
+
*
|
|
4
|
+
* Implements different adoption strategies based on project state:
|
|
5
|
+
* - Fresh: Create complete .kiro/ structure from scratch
|
|
6
|
+
* - Partial: Add missing components to existing .kiro/
|
|
7
|
+
* - Full: Upgrade existing complete .kiro/ to current version
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const {
|
|
12
|
+
pathExists,
|
|
13
|
+
ensureDirectory,
|
|
14
|
+
copyDirectory,
|
|
15
|
+
safeCopy,
|
|
16
|
+
listFiles,
|
|
17
|
+
readJSON,
|
|
18
|
+
writeJSON
|
|
19
|
+
} = require('../utils/fs-utils');
|
|
20
|
+
const VersionManager = require('../version/version-manager');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Base class for adoption strategies
|
|
24
|
+
*/
|
|
25
|
+
class AdoptionStrategy {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.versionManager = new VersionManager();
|
|
28
|
+
this.kiroDir = '.kiro';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets the path to .kiro/ directory
|
|
33
|
+
*
|
|
34
|
+
* @param {string} projectPath - Absolute path to project root
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
getKiroPath(projectPath) {
|
|
38
|
+
return path.join(projectPath, this.kiroDir);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets the path to template directory
|
|
43
|
+
* This would be embedded in the kse package
|
|
44
|
+
* For now, we'll use a placeholder
|
|
45
|
+
*
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
getTemplatePath() {
|
|
49
|
+
// In production, this would be: path.join(__dirname, '../../templates/kiro')
|
|
50
|
+
// For now, return a placeholder that can be configured
|
|
51
|
+
return path.join(__dirname, '../../templates/kiro');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Executes adoption strategy
|
|
56
|
+
* Must be implemented by subclasses
|
|
57
|
+
*
|
|
58
|
+
* @param {string} projectPath - Absolute path to project root
|
|
59
|
+
* @param {AdoptionMode} mode - Adoption mode
|
|
60
|
+
* @param {AdoptionOptions} options - Adoption options
|
|
61
|
+
* @returns {Promise<AdoptionResult>}
|
|
62
|
+
*/
|
|
63
|
+
async execute(projectPath, mode, options) {
|
|
64
|
+
throw new Error('execute() must be implemented by subclass');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates initial directory structure
|
|
69
|
+
*
|
|
70
|
+
* @param {string} kiroPath - Path to .kiro/ directory
|
|
71
|
+
* @returns {Promise<void>}
|
|
72
|
+
*/
|
|
73
|
+
async createDirectoryStructure(kiroPath) {
|
|
74
|
+
await ensureDirectory(kiroPath);
|
|
75
|
+
await ensureDirectory(path.join(kiroPath, 'specs'));
|
|
76
|
+
await ensureDirectory(path.join(kiroPath, 'steering'));
|
|
77
|
+
await ensureDirectory(path.join(kiroPath, 'tools'));
|
|
78
|
+
await ensureDirectory(path.join(kiroPath, 'backups'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Copies template files to project
|
|
83
|
+
*
|
|
84
|
+
* @param {string} projectPath - Absolute path to project root
|
|
85
|
+
* @param {Object} options - Copy options
|
|
86
|
+
* @param {boolean} options.overwrite - Whether to overwrite existing files
|
|
87
|
+
* @param {string[]} options.skip - Files to skip
|
|
88
|
+
* @returns {Promise<{created: string[], updated: string[], skipped: string[]}>}
|
|
89
|
+
*/
|
|
90
|
+
async copyTemplateFiles(projectPath, options = {}) {
|
|
91
|
+
const { overwrite = false, skip = [] } = options;
|
|
92
|
+
const kiroPath = this.getKiroPath(projectPath);
|
|
93
|
+
const templatePath = this.getTemplatePath();
|
|
94
|
+
|
|
95
|
+
const created = [];
|
|
96
|
+
const updated = [];
|
|
97
|
+
const skipped = [];
|
|
98
|
+
|
|
99
|
+
// Check if template directory exists
|
|
100
|
+
const templateExists = await pathExists(templatePath);
|
|
101
|
+
if (!templateExists) {
|
|
102
|
+
// Template directory doesn't exist yet - this is expected during development
|
|
103
|
+
// In production, templates would be bundled with the package
|
|
104
|
+
return { created, updated, skipped };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Define template structure
|
|
108
|
+
const templateFiles = [
|
|
109
|
+
'steering/CORE_PRINCIPLES.md',
|
|
110
|
+
'steering/ENVIRONMENT.md',
|
|
111
|
+
'steering/CURRENT_CONTEXT.md',
|
|
112
|
+
'steering/RULES_GUIDE.md',
|
|
113
|
+
'tools/ultrawork_enhancer.py',
|
|
114
|
+
'README.md',
|
|
115
|
+
'ultrawork-application-guide.md',
|
|
116
|
+
'ultrawork-integration-summary.md',
|
|
117
|
+
'sisyphus-deep-dive.md'
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
for (const file of templateFiles) {
|
|
121
|
+
// Check if file should be skipped
|
|
122
|
+
if (skip.includes(file)) {
|
|
123
|
+
skipped.push(file);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const sourcePath = path.join(templatePath, file);
|
|
128
|
+
const destPath = path.join(kiroPath, file);
|
|
129
|
+
|
|
130
|
+
// Check if source exists
|
|
131
|
+
const sourceExists = await pathExists(sourcePath);
|
|
132
|
+
if (!sourceExists) {
|
|
133
|
+
skipped.push(file);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check if destination exists
|
|
138
|
+
const destExists = await pathExists(destPath);
|
|
139
|
+
|
|
140
|
+
if (destExists && !overwrite) {
|
|
141
|
+
skipped.push(file);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await safeCopy(sourcePath, destPath, { overwrite });
|
|
147
|
+
|
|
148
|
+
if (destExists) {
|
|
149
|
+
updated.push(file);
|
|
150
|
+
} else {
|
|
151
|
+
created.push(file);
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
// If copy fails, add to skipped
|
|
155
|
+
skipped.push(file);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { created, updated, skipped };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Fresh Adoption Strategy
|
|
165
|
+
* Creates complete .kiro/ structure from scratch
|
|
166
|
+
*/
|
|
167
|
+
class FreshAdoption extends AdoptionStrategy {
|
|
168
|
+
/**
|
|
169
|
+
* Executes fresh adoption
|
|
170
|
+
*
|
|
171
|
+
* @param {string} projectPath - Absolute path to project root
|
|
172
|
+
* @param {AdoptionMode} mode - Should be 'fresh'
|
|
173
|
+
* @param {AdoptionOptions} options - Adoption options
|
|
174
|
+
* @returns {Promise<AdoptionResult>}
|
|
175
|
+
*/
|
|
176
|
+
async execute(projectPath, mode, options = {}) {
|
|
177
|
+
const { kseVersion = '1.0.0', dryRun = false } = options;
|
|
178
|
+
|
|
179
|
+
const filesCreated = [];
|
|
180
|
+
const filesUpdated = [];
|
|
181
|
+
const filesSkipped = [];
|
|
182
|
+
const errors = [];
|
|
183
|
+
const warnings = [];
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const kiroPath = this.getKiroPath(projectPath);
|
|
187
|
+
|
|
188
|
+
// Check if .kiro/ already exists
|
|
189
|
+
const kiroExists = await pathExists(kiroPath);
|
|
190
|
+
if (kiroExists) {
|
|
191
|
+
throw new Error('.kiro/ directory already exists - use partial or full adoption');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (dryRun) {
|
|
195
|
+
return {
|
|
196
|
+
success: true,
|
|
197
|
+
mode: 'fresh',
|
|
198
|
+
filesCreated: ['(dry-run) .kiro/ structure would be created'],
|
|
199
|
+
filesUpdated: [],
|
|
200
|
+
filesSkipped: [],
|
|
201
|
+
backupId: null,
|
|
202
|
+
errors: [],
|
|
203
|
+
warnings: []
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Create directory structure
|
|
208
|
+
await this.createDirectoryStructure(kiroPath);
|
|
209
|
+
filesCreated.push('.kiro/');
|
|
210
|
+
filesCreated.push('.kiro/specs/');
|
|
211
|
+
filesCreated.push('.kiro/steering/');
|
|
212
|
+
filesCreated.push('.kiro/tools/');
|
|
213
|
+
filesCreated.push('.kiro/backups/');
|
|
214
|
+
|
|
215
|
+
// Copy template files
|
|
216
|
+
const copyResult = await this.copyTemplateFiles(projectPath, { overwrite: false });
|
|
217
|
+
filesCreated.push(...copyResult.created);
|
|
218
|
+
filesUpdated.push(...copyResult.updated);
|
|
219
|
+
filesSkipped.push(...copyResult.skipped);
|
|
220
|
+
|
|
221
|
+
// Create version.json
|
|
222
|
+
const versionInfo = this.versionManager.createVersionInfo(kseVersion);
|
|
223
|
+
await this.versionManager.writeVersion(projectPath, versionInfo);
|
|
224
|
+
filesCreated.push('version.json');
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
success: true,
|
|
228
|
+
mode: 'fresh',
|
|
229
|
+
filesCreated,
|
|
230
|
+
filesUpdated,
|
|
231
|
+
filesSkipped,
|
|
232
|
+
backupId: null,
|
|
233
|
+
errors,
|
|
234
|
+
warnings
|
|
235
|
+
};
|
|
236
|
+
} catch (error) {
|
|
237
|
+
errors.push(error.message);
|
|
238
|
+
return {
|
|
239
|
+
success: false,
|
|
240
|
+
mode: 'fresh',
|
|
241
|
+
filesCreated,
|
|
242
|
+
filesUpdated,
|
|
243
|
+
filesSkipped,
|
|
244
|
+
backupId: null,
|
|
245
|
+
errors,
|
|
246
|
+
warnings
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Partial Adoption Strategy
|
|
254
|
+
* Adds missing components to existing .kiro/
|
|
255
|
+
*/
|
|
256
|
+
class PartialAdoption extends AdoptionStrategy {
|
|
257
|
+
/**
|
|
258
|
+
* Executes partial adoption
|
|
259
|
+
*
|
|
260
|
+
* @param {string} projectPath - Absolute path to project root
|
|
261
|
+
* @param {AdoptionMode} mode - Should be 'partial'
|
|
262
|
+
* @param {AdoptionOptions} options - Adoption options
|
|
263
|
+
* @returns {Promise<AdoptionResult>}
|
|
264
|
+
*/
|
|
265
|
+
async execute(projectPath, mode, options = {}) {
|
|
266
|
+
const { kseVersion = '1.0.0', dryRun = false, backupId = null } = options;
|
|
267
|
+
|
|
268
|
+
const filesCreated = [];
|
|
269
|
+
const filesUpdated = [];
|
|
270
|
+
const filesSkipped = [];
|
|
271
|
+
const errors = [];
|
|
272
|
+
const warnings = [];
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const kiroPath = this.getKiroPath(projectPath);
|
|
276
|
+
|
|
277
|
+
// Check if .kiro/ exists
|
|
278
|
+
const kiroExists = await pathExists(kiroPath);
|
|
279
|
+
if (!kiroExists) {
|
|
280
|
+
throw new Error('.kiro/ directory does not exist - use fresh adoption');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Check if version.json exists
|
|
284
|
+
const versionPath = path.join(kiroPath, 'version.json');
|
|
285
|
+
const versionExists = await pathExists(versionPath);
|
|
286
|
+
if (versionExists) {
|
|
287
|
+
warnings.push('version.json already exists - use full adoption for upgrades');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (dryRun) {
|
|
291
|
+
return {
|
|
292
|
+
success: true,
|
|
293
|
+
mode: 'partial',
|
|
294
|
+
filesCreated: ['(dry-run) Missing components would be added'],
|
|
295
|
+
filesUpdated: [],
|
|
296
|
+
filesSkipped: [],
|
|
297
|
+
backupId,
|
|
298
|
+
errors: [],
|
|
299
|
+
warnings
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Ensure all required directories exist
|
|
304
|
+
const specsPath = path.join(kiroPath, 'specs');
|
|
305
|
+
const steeringPath = path.join(kiroPath, 'steering');
|
|
306
|
+
const toolsPath = path.join(kiroPath, 'tools');
|
|
307
|
+
const backupsPath = path.join(kiroPath, 'backups');
|
|
308
|
+
|
|
309
|
+
if (!await pathExists(specsPath)) {
|
|
310
|
+
await ensureDirectory(specsPath);
|
|
311
|
+
filesCreated.push('specs/');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!await pathExists(steeringPath)) {
|
|
315
|
+
await ensureDirectory(steeringPath);
|
|
316
|
+
filesCreated.push('steering/');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!await pathExists(toolsPath)) {
|
|
320
|
+
await ensureDirectory(toolsPath);
|
|
321
|
+
filesCreated.push('tools/');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (!await pathExists(backupsPath)) {
|
|
325
|
+
await ensureDirectory(backupsPath);
|
|
326
|
+
filesCreated.push('backups/');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Copy template files (don't overwrite existing)
|
|
330
|
+
const copyResult = await this.copyTemplateFiles(projectPath, { overwrite: false });
|
|
331
|
+
filesCreated.push(...copyResult.created);
|
|
332
|
+
filesUpdated.push(...copyResult.updated);
|
|
333
|
+
filesSkipped.push(...copyResult.skipped);
|
|
334
|
+
|
|
335
|
+
// Create or update version.json
|
|
336
|
+
if (!versionExists) {
|
|
337
|
+
const versionInfo = this.versionManager.createVersionInfo(kseVersion);
|
|
338
|
+
await this.versionManager.writeVersion(projectPath, versionInfo);
|
|
339
|
+
filesCreated.push('version.json');
|
|
340
|
+
} else {
|
|
341
|
+
// Update existing version.json
|
|
342
|
+
const versionInfo = await this.versionManager.readVersion(projectPath);
|
|
343
|
+
if (versionInfo) {
|
|
344
|
+
versionInfo['kse-version'] = kseVersion;
|
|
345
|
+
versionInfo['template-version'] = kseVersion;
|
|
346
|
+
versionInfo['last-upgraded'] = new Date().toISOString();
|
|
347
|
+
await this.versionManager.writeVersion(projectPath, versionInfo);
|
|
348
|
+
filesUpdated.push('version.json');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
success: true,
|
|
354
|
+
mode: 'partial',
|
|
355
|
+
filesCreated,
|
|
356
|
+
filesUpdated,
|
|
357
|
+
filesSkipped,
|
|
358
|
+
backupId,
|
|
359
|
+
errors,
|
|
360
|
+
warnings
|
|
361
|
+
};
|
|
362
|
+
} catch (error) {
|
|
363
|
+
errors.push(error.message);
|
|
364
|
+
return {
|
|
365
|
+
success: false,
|
|
366
|
+
mode: 'partial',
|
|
367
|
+
filesCreated,
|
|
368
|
+
filesUpdated,
|
|
369
|
+
filesSkipped,
|
|
370
|
+
backupId,
|
|
371
|
+
errors,
|
|
372
|
+
warnings
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Full Adoption Strategy
|
|
380
|
+
* Upgrades existing complete .kiro/ to current version
|
|
381
|
+
*/
|
|
382
|
+
class FullAdoption extends AdoptionStrategy {
|
|
383
|
+
/**
|
|
384
|
+
* Executes full adoption (upgrade)
|
|
385
|
+
*
|
|
386
|
+
* @param {string} projectPath - Absolute path to project root
|
|
387
|
+
* @param {AdoptionMode} mode - Should be 'full'
|
|
388
|
+
* @param {AdoptionOptions} options - Adoption options
|
|
389
|
+
* @returns {Promise<AdoptionResult>}
|
|
390
|
+
*/
|
|
391
|
+
async execute(projectPath, mode, options = {}) {
|
|
392
|
+
const { kseVersion = '1.0.0', dryRun = false, backupId = null } = options;
|
|
393
|
+
|
|
394
|
+
const filesCreated = [];
|
|
395
|
+
const filesUpdated = [];
|
|
396
|
+
const filesSkipped = [];
|
|
397
|
+
const errors = [];
|
|
398
|
+
const warnings = [];
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
const kiroPath = this.getKiroPath(projectPath);
|
|
402
|
+
|
|
403
|
+
// Check if .kiro/ exists
|
|
404
|
+
const kiroExists = await pathExists(kiroPath);
|
|
405
|
+
if (!kiroExists) {
|
|
406
|
+
throw new Error('.kiro/ directory does not exist - use fresh adoption');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Read existing version
|
|
410
|
+
const existingVersion = await this.versionManager.readVersion(projectPath);
|
|
411
|
+
if (!existingVersion) {
|
|
412
|
+
throw new Error('version.json not found - use partial adoption');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const currentVersion = existingVersion['kse-version'];
|
|
416
|
+
|
|
417
|
+
// Check if upgrade is needed
|
|
418
|
+
if (!this.versionManager.needsUpgrade(currentVersion, kseVersion)) {
|
|
419
|
+
warnings.push(`Already at version ${kseVersion} - no upgrade needed`);
|
|
420
|
+
return {
|
|
421
|
+
success: true,
|
|
422
|
+
mode: 'full',
|
|
423
|
+
filesCreated: [],
|
|
424
|
+
filesUpdated: [],
|
|
425
|
+
filesSkipped: [],
|
|
426
|
+
backupId,
|
|
427
|
+
errors: [],
|
|
428
|
+
warnings
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (dryRun) {
|
|
433
|
+
return {
|
|
434
|
+
success: true,
|
|
435
|
+
mode: 'full',
|
|
436
|
+
filesCreated: [],
|
|
437
|
+
filesUpdated: [`(dry-run) Would upgrade from ${currentVersion} to ${kseVersion}`],
|
|
438
|
+
filesSkipped: [],
|
|
439
|
+
backupId,
|
|
440
|
+
errors: [],
|
|
441
|
+
warnings
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Copy template files (overwrite template files, preserve user content)
|
|
446
|
+
// User content is in specs/ and any custom files
|
|
447
|
+
const copyResult = await this.copyTemplateFiles(projectPath, {
|
|
448
|
+
overwrite: true,
|
|
449
|
+
skip: [] // Don't skip anything - we want to update templates
|
|
450
|
+
});
|
|
451
|
+
filesCreated.push(...copyResult.created);
|
|
452
|
+
filesUpdated.push(...copyResult.updated);
|
|
453
|
+
filesSkipped.push(...copyResult.skipped);
|
|
454
|
+
|
|
455
|
+
// Update version.json with upgrade history
|
|
456
|
+
const updatedVersion = this.versionManager.addUpgradeHistory(
|
|
457
|
+
existingVersion,
|
|
458
|
+
currentVersion,
|
|
459
|
+
kseVersion,
|
|
460
|
+
true
|
|
461
|
+
);
|
|
462
|
+
await this.versionManager.writeVersion(projectPath, updatedVersion);
|
|
463
|
+
filesUpdated.push('version.json');
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
success: true,
|
|
467
|
+
mode: 'full',
|
|
468
|
+
filesCreated,
|
|
469
|
+
filesUpdated,
|
|
470
|
+
filesSkipped,
|
|
471
|
+
backupId,
|
|
472
|
+
errors,
|
|
473
|
+
warnings
|
|
474
|
+
};
|
|
475
|
+
} catch (error) {
|
|
476
|
+
errors.push(error.message);
|
|
477
|
+
return {
|
|
478
|
+
success: false,
|
|
479
|
+
mode: 'full',
|
|
480
|
+
filesCreated,
|
|
481
|
+
filesUpdated,
|
|
482
|
+
filesSkipped,
|
|
483
|
+
backupId,
|
|
484
|
+
errors,
|
|
485
|
+
warnings
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Factory function to get the appropriate strategy
|
|
493
|
+
*
|
|
494
|
+
* @param {AdoptionMode} mode - Adoption mode ('fresh', 'partial', 'full')
|
|
495
|
+
* @returns {AdoptionStrategy}
|
|
496
|
+
*/
|
|
497
|
+
function getAdoptionStrategy(mode) {
|
|
498
|
+
switch (mode) {
|
|
499
|
+
case 'fresh':
|
|
500
|
+
return new FreshAdoption();
|
|
501
|
+
case 'partial':
|
|
502
|
+
return new PartialAdoption();
|
|
503
|
+
case 'full':
|
|
504
|
+
return new FullAdoption();
|
|
505
|
+
default:
|
|
506
|
+
throw new Error(`Unknown adoption mode: ${mode}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
module.exports = {
|
|
511
|
+
AdoptionStrategy,
|
|
512
|
+
FreshAdoption,
|
|
513
|
+
PartialAdoption,
|
|
514
|
+
FullAdoption,
|
|
515
|
+
getAdoptionStrategy
|
|
516
|
+
};
|