@launch77/plugin-runtime 0.3.1 → 0.3.3
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/dist/index.d.ts +734 -143
- package/dist/index.js +1631 -272
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,151 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type of location within a Launch77 workspace
|
|
5
|
+
*/
|
|
6
|
+
type Launch77LocationType = 'workspace-root' | 'workspace-app' | 'workspace-library' | 'workspace-plugin' | 'workspace-app-template' | 'unknown';
|
|
7
|
+
/**
|
|
8
|
+
* Context information about the current location within a Launch77 workspace
|
|
9
|
+
*/
|
|
10
|
+
interface Launch77Context {
|
|
11
|
+
isValid: boolean;
|
|
12
|
+
locationType: Launch77LocationType;
|
|
13
|
+
workspaceRoot: string;
|
|
14
|
+
appsDir: string;
|
|
15
|
+
workspaceVersion: string;
|
|
16
|
+
workspaceName: string;
|
|
17
|
+
appName?: string;
|
|
18
|
+
packageName: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Workspace manifest structure (.launch77/workspace.json)
|
|
22
|
+
*/
|
|
23
|
+
interface WorkspaceManifest {
|
|
24
|
+
version: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parsed location information from a path
|
|
28
|
+
*/
|
|
29
|
+
interface ParsedLocation {
|
|
30
|
+
locationType: Launch77LocationType;
|
|
31
|
+
appName?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Service responsible for workspace manifest file operations.
|
|
36
|
+
* Handles reading and writing the .launch77/workspace.json file.
|
|
37
|
+
*/
|
|
38
|
+
declare class WorkspaceManifestService {
|
|
39
|
+
private static readonly WORKSPACE_MANIFEST;
|
|
40
|
+
/**
|
|
41
|
+
* Check if a workspace manifest exists at the given root
|
|
42
|
+
*/
|
|
43
|
+
exists(workspaceRoot: string): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Read the workspace manifest
|
|
46
|
+
*/
|
|
47
|
+
readWorkspaceManifest(workspaceRoot: string): Promise<WorkspaceManifest | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Write the workspace manifest
|
|
50
|
+
*/
|
|
51
|
+
writeWorkspaceManifest(workspaceRoot: string, manifest: WorkspaceManifest): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Get the manifest file path for a workspace
|
|
54
|
+
*/
|
|
55
|
+
getManifestPath(workspaceRoot: string): string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validation result type
|
|
60
|
+
*/
|
|
61
|
+
interface ValidationResult$2 {
|
|
62
|
+
isValid: boolean;
|
|
63
|
+
errors?: string[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Service responsible for workspace-related operations.
|
|
67
|
+
* Handles workspace detection and workspace queries.
|
|
68
|
+
*/
|
|
69
|
+
declare class WorkspaceService {
|
|
70
|
+
private workspaceManifestService;
|
|
71
|
+
constructor(workspaceManifestService?: WorkspaceManifestService);
|
|
72
|
+
/**
|
|
73
|
+
* Check if a directory is a Launch77 workspace root
|
|
74
|
+
*/
|
|
75
|
+
isWorkspaceRoot(dir: string): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Get the workspace root directory from the current directory
|
|
78
|
+
*/
|
|
79
|
+
findWorkspaceRoot(startDir: string): Promise<string | null>;
|
|
80
|
+
/**
|
|
81
|
+
* Validate that we're in a Launch77 workspace context
|
|
82
|
+
*/
|
|
83
|
+
validateWorkspaceContext(context: Launch77Context): Promise<{
|
|
84
|
+
valid: boolean;
|
|
85
|
+
errorMessage?: string;
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Validate a workspace context using ValidationResult format
|
|
89
|
+
*/
|
|
90
|
+
validateContext(context: Launch77Context): ValidationResult$2;
|
|
91
|
+
/**
|
|
92
|
+
* Validate an app name
|
|
93
|
+
*/
|
|
94
|
+
validateAppName(name: string): ValidationResult$2;
|
|
95
|
+
/**
|
|
96
|
+
* Detect the Launch77 workspace context from the current working directory
|
|
97
|
+
*
|
|
98
|
+
* @param cwd - Current working directory path
|
|
99
|
+
* @returns Context information about the workspace location
|
|
100
|
+
*/
|
|
101
|
+
detectLaunch77Context(cwd: string): Promise<Launch77Context>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Standalone function for backward compatibility
|
|
105
|
+
* Creates a new WorkspaceService instance and calls detectLaunch77Context
|
|
106
|
+
*/
|
|
107
|
+
declare function detectLaunch77Context(cwd: string): Promise<Launch77Context>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parse the directory structure to determine location context
|
|
111
|
+
*
|
|
112
|
+
* Based on patterns:
|
|
113
|
+
* - apps/[name] → workspace-app
|
|
114
|
+
* - libraries/[name] → workspace-library
|
|
115
|
+
* - plugins/[name] → workspace-plugin
|
|
116
|
+
* - app-templates/[name] → workspace-app-template
|
|
117
|
+
* - (empty or root) → workspace-root
|
|
118
|
+
*
|
|
119
|
+
* @param cwdPath - Current working directory path
|
|
120
|
+
* @param workspaceRoot - Root path of the workspace
|
|
121
|
+
* @returns Parsed location information
|
|
122
|
+
*/
|
|
123
|
+
declare function parseLocationFromPath(cwdPath: string, workspaceRoot: string): ParsedLocation;
|
|
124
|
+
|
|
125
|
+
interface InstallPluginRequest {
|
|
126
|
+
pluginName: string;
|
|
127
|
+
}
|
|
128
|
+
interface InstallPluginResult {
|
|
129
|
+
pluginName: string;
|
|
130
|
+
filesInstalled: boolean;
|
|
131
|
+
packageJsonUpdated: boolean;
|
|
132
|
+
dependenciesInstalled: boolean;
|
|
133
|
+
}
|
|
134
|
+
interface HookResult {
|
|
135
|
+
templateVariables?: Record<string, string>;
|
|
136
|
+
hookContext?: Record<string, string>;
|
|
137
|
+
}
|
|
138
|
+
interface DeletePluginRequest {
|
|
139
|
+
pluginName: string;
|
|
140
|
+
}
|
|
141
|
+
interface DeletePluginResult {
|
|
142
|
+
pluginName: string;
|
|
143
|
+
}
|
|
144
|
+
type Target = 'app' | 'library' | 'plugin' | 'app-template';
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Plugin Module Types
|
|
148
|
+
*/
|
|
1
149
|
interface GeneratorContext {
|
|
2
150
|
appPath: string;
|
|
3
151
|
appName: string;
|
|
@@ -8,25 +156,10 @@ interface PluginMetadata {
|
|
|
8
156
|
targets: string[];
|
|
9
157
|
pluginDependencies?: Record<string, string>;
|
|
10
158
|
libraryDependencies?: Record<string, string>;
|
|
11
|
-
|
|
12
|
-
interface InstalledPluginMetadata {
|
|
13
|
-
package: string;
|
|
14
|
-
version: string;
|
|
15
|
-
installedAt: string;
|
|
16
|
-
source: 'local' | 'npm';
|
|
159
|
+
showcaseUrl?: string;
|
|
17
160
|
}
|
|
18
161
|
interface TemplateMetadata {
|
|
19
162
|
}
|
|
20
|
-
interface TemplateReference {
|
|
21
|
-
package: string;
|
|
22
|
-
version: string;
|
|
23
|
-
source: 'local' | 'npm';
|
|
24
|
-
createdAt: string;
|
|
25
|
-
}
|
|
26
|
-
interface Launch77PackageManifest {
|
|
27
|
-
installedPlugins?: Record<string, InstalledPluginMetadata>;
|
|
28
|
-
template?: TemplateReference;
|
|
29
|
-
}
|
|
30
163
|
|
|
31
164
|
/**
|
|
32
165
|
* Base abstract class for all plugin generators.
|
|
@@ -47,6 +180,106 @@ declare abstract class Generator {
|
|
|
47
180
|
abstract run(): Promise<void>;
|
|
48
181
|
}
|
|
49
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Service responsible for file system operations.
|
|
185
|
+
* Provides an abstraction layer over fs operations with proper error handling.
|
|
186
|
+
*/
|
|
187
|
+
declare class FilesystemService {
|
|
188
|
+
/**
|
|
189
|
+
* Check if a path exists
|
|
190
|
+
*/
|
|
191
|
+
pathExists(filePath: string): Promise<boolean>;
|
|
192
|
+
/**
|
|
193
|
+
* Copy a file or directory recursively
|
|
194
|
+
*/
|
|
195
|
+
copyRecursive(source: string, destination: string, options?: {
|
|
196
|
+
overwrite?: boolean;
|
|
197
|
+
}): Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Read a JSON file
|
|
200
|
+
*/
|
|
201
|
+
readJSON<T = unknown>(filePath: string): Promise<T>;
|
|
202
|
+
/**
|
|
203
|
+
* Write a JSON file
|
|
204
|
+
*/
|
|
205
|
+
writeJSON(filePath: string, data: unknown, options?: {
|
|
206
|
+
spaces?: number;
|
|
207
|
+
}): Promise<void>;
|
|
208
|
+
/**
|
|
209
|
+
* Read a text file
|
|
210
|
+
*/
|
|
211
|
+
readFile(filePath: string, encoding?: BufferEncoding): Promise<string>;
|
|
212
|
+
/**
|
|
213
|
+
* Write a text file
|
|
214
|
+
*/
|
|
215
|
+
writeFile(filePath: string, content: string, encoding?: BufferEncoding): Promise<void>;
|
|
216
|
+
/**
|
|
217
|
+
* Create a directory (including parent directories)
|
|
218
|
+
*/
|
|
219
|
+
ensureDir(dirPath: string): Promise<void>;
|
|
220
|
+
/**
|
|
221
|
+
* Remove a file or directory
|
|
222
|
+
*/
|
|
223
|
+
remove(filePath: string): Promise<void>;
|
|
224
|
+
/**
|
|
225
|
+
* List files in a directory
|
|
226
|
+
*/
|
|
227
|
+
readDir(dirPath: string): Promise<string[]>;
|
|
228
|
+
/**
|
|
229
|
+
* Get file or directory stats
|
|
230
|
+
*/
|
|
231
|
+
getStats(filePath: string): Promise<fs.Stats>;
|
|
232
|
+
/**
|
|
233
|
+
* Check if a path is a directory
|
|
234
|
+
*/
|
|
235
|
+
isDirectory(filePath: string): Promise<boolean>;
|
|
236
|
+
/**
|
|
237
|
+
* Check if a path is a file
|
|
238
|
+
*/
|
|
239
|
+
isFile(filePath: string): Promise<boolean>;
|
|
240
|
+
/**
|
|
241
|
+
* Move a file or directory
|
|
242
|
+
*/
|
|
243
|
+
move(source: string, destination: string, options?: {
|
|
244
|
+
overwrite?: boolean;
|
|
245
|
+
}): Promise<void>;
|
|
246
|
+
/**
|
|
247
|
+
* Create a temporary directory
|
|
248
|
+
*/
|
|
249
|
+
createTempDir(prefix: string): Promise<string>;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Service responsible for reading and managing metadata for plugins and templates.
|
|
254
|
+
* Handles JSON file reading and parsing without mixing validation concerns.
|
|
255
|
+
*/
|
|
256
|
+
declare class MetadataService {
|
|
257
|
+
/**
|
|
258
|
+
* Read plugin metadata from a plugin directory
|
|
259
|
+
*/
|
|
260
|
+
readPluginMetadata(pluginPath: string): Promise<PluginMetadata>;
|
|
261
|
+
/**
|
|
262
|
+
* Read template metadata from a template directory
|
|
263
|
+
*/
|
|
264
|
+
readTemplateMetadata(templatePath: string): Promise<TemplateMetadata>;
|
|
265
|
+
/**
|
|
266
|
+
* Check if a plugin metadata file exists
|
|
267
|
+
*/
|
|
268
|
+
hasPluginMetadata(pluginPath: string): Promise<boolean>;
|
|
269
|
+
/**
|
|
270
|
+
* Check if a template metadata file exists
|
|
271
|
+
*/
|
|
272
|
+
hasTemplateMetadata(templatePath: string): Promise<boolean>;
|
|
273
|
+
/**
|
|
274
|
+
* Write plugin metadata to a directory
|
|
275
|
+
*/
|
|
276
|
+
writePluginMetadata(pluginPath: string, metadata: PluginMetadata): Promise<void>;
|
|
277
|
+
/**
|
|
278
|
+
* Write template metadata to a directory
|
|
279
|
+
*/
|
|
280
|
+
writeTemplateMetadata(templatePath: string, metadata: TemplateMetadata): Promise<void>;
|
|
281
|
+
}
|
|
282
|
+
|
|
50
283
|
/**
|
|
51
284
|
* Standard generator with convention-over-configuration approach.
|
|
52
285
|
*
|
|
@@ -60,6 +293,9 @@ declare abstract class Generator {
|
|
|
60
293
|
* For full control, extend Generator instead.
|
|
61
294
|
*/
|
|
62
295
|
declare abstract class StandardGenerator extends Generator {
|
|
296
|
+
protected filesystemService: FilesystemService;
|
|
297
|
+
protected metadataService: MetadataService;
|
|
298
|
+
constructor(context: GeneratorContext);
|
|
63
299
|
run(): Promise<void>;
|
|
64
300
|
protected updateDependencies(): Promise<void>;
|
|
65
301
|
protected installDependencies(): Promise<void>;
|
|
@@ -68,168 +304,523 @@ declare abstract class StandardGenerator extends Generator {
|
|
|
68
304
|
protected showNextSteps(): void;
|
|
69
305
|
}
|
|
70
306
|
|
|
71
|
-
declare function copyRecursive(src: string, dest: string): Promise<void>;
|
|
72
|
-
declare function pathExists(filePath: string): Promise<boolean>;
|
|
73
|
-
|
|
74
307
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* Only reads plugin-specific configuration fields:
|
|
78
|
-
* - targets: where the plugin can be installed
|
|
79
|
-
* - pluginDependencies: other plugins this plugin requires
|
|
80
|
-
* - libraryDependencies: npm packages to inject into target
|
|
81
|
-
*
|
|
82
|
-
* Name and version are read from package.json during resolution.
|
|
83
|
-
*
|
|
84
|
-
* @param pluginPath - Absolute path to the plugin directory
|
|
85
|
-
* @returns Parsed plugin metadata
|
|
308
|
+
* Validation result type for plugin operations
|
|
86
309
|
*/
|
|
87
|
-
|
|
310
|
+
interface ValidationResult$1 {
|
|
311
|
+
isValid: boolean;
|
|
312
|
+
errors?: string[];
|
|
313
|
+
}
|
|
88
314
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* Currently template.json contains no fields - it exists as a placeholder
|
|
92
|
-
* for future template-specific configuration (e.g., deployment metadata).
|
|
93
|
-
*
|
|
94
|
-
* Template name and version are read from package.json during resolution.
|
|
95
|
-
*
|
|
96
|
-
* @param templatePath - Absolute path to the template directory
|
|
97
|
-
* @returns Template metadata (currently empty)
|
|
315
|
+
* Plugin consistency error type
|
|
98
316
|
*/
|
|
99
|
-
|
|
317
|
+
interface PluginConsistencyError {
|
|
318
|
+
library: string;
|
|
319
|
+
packageJsonVersion: string | 'MISSING';
|
|
320
|
+
pluginJsonVersion: string;
|
|
321
|
+
}
|
|
322
|
+
declare class PluginService {
|
|
323
|
+
private pluginResolver;
|
|
324
|
+
private metadataService;
|
|
325
|
+
private npmService;
|
|
326
|
+
constructor();
|
|
327
|
+
/**
|
|
328
|
+
* Validate a plugin name
|
|
329
|
+
*/
|
|
330
|
+
validatePluginName(name: string): ValidationResult$1;
|
|
331
|
+
/**
|
|
332
|
+
* Validate plugin metadata
|
|
333
|
+
*/
|
|
334
|
+
validatePluginMetadata(metadata: PluginMetadata): ValidationResult$1;
|
|
335
|
+
/**
|
|
336
|
+
* Validate a showcase URL
|
|
337
|
+
* Note: This is primarily used by the webapp template
|
|
338
|
+
*/
|
|
339
|
+
validateShowcaseUrl(url: string | undefined): ValidationResult$1;
|
|
340
|
+
/**
|
|
341
|
+
* Validate plugin consistency between plugin.json and package.json
|
|
342
|
+
*/
|
|
343
|
+
validatePluginConsistency(pluginPath: string): Promise<ValidationResult$1>;
|
|
344
|
+
/**
|
|
345
|
+
* Check if a package name part is valid
|
|
346
|
+
*/
|
|
347
|
+
private isValidPackageNamePart;
|
|
348
|
+
/**
|
|
349
|
+
* Validate plugin name, resolve its location, and download if needed
|
|
350
|
+
*/
|
|
351
|
+
private validateAndResolvePlugin;
|
|
352
|
+
/**
|
|
353
|
+
* Read plugin metadata and validate it supports the current target
|
|
354
|
+
*/
|
|
355
|
+
private validatePluginTargets;
|
|
356
|
+
/**
|
|
357
|
+
* Check if plugin is already installed and return early-exit result if so
|
|
358
|
+
*/
|
|
359
|
+
private checkExistingInstallation;
|
|
360
|
+
/**
|
|
361
|
+
* Install a plugin to the current package
|
|
362
|
+
*/
|
|
363
|
+
installPlugin(request: InstallPluginRequest, context: Launch77Context, logger?: (message: string) => void): Promise<InstallPluginResult>;
|
|
364
|
+
/**
|
|
365
|
+
* Download and install an npm plugin package
|
|
366
|
+
*/
|
|
367
|
+
private downloadNpmPlugin;
|
|
368
|
+
private getPackagePath;
|
|
369
|
+
private runGenerator;
|
|
370
|
+
private isPluginInstalled;
|
|
371
|
+
/**
|
|
372
|
+
* Write plugin installation metadata to the target package's package.json
|
|
373
|
+
*/
|
|
374
|
+
private writePluginManifest;
|
|
375
|
+
/**
|
|
376
|
+
* Delete a plugin from the workspace
|
|
377
|
+
*/
|
|
378
|
+
deletePlugin(request: DeletePluginRequest, context: Launch77Context): Promise<DeletePluginResult>;
|
|
379
|
+
}
|
|
100
380
|
|
|
101
381
|
/**
|
|
102
|
-
*
|
|
382
|
+
* Metadata about an installed plugin
|
|
103
383
|
*/
|
|
104
|
-
|
|
384
|
+
interface InstalledPluginMetadata {
|
|
385
|
+
package: string;
|
|
386
|
+
version: string;
|
|
387
|
+
installedAt: string;
|
|
388
|
+
source: 'local' | 'npm';
|
|
389
|
+
}
|
|
105
390
|
/**
|
|
106
|
-
*
|
|
391
|
+
* Reference to the template used to create this package
|
|
107
392
|
*/
|
|
108
|
-
interface
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
393
|
+
interface TemplateReference {
|
|
394
|
+
package: string;
|
|
395
|
+
version: string;
|
|
396
|
+
source: 'local' | 'npm';
|
|
397
|
+
createdAt: string;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* The launch77 section within package.json
|
|
401
|
+
*/
|
|
402
|
+
interface PackageManifest {
|
|
403
|
+
installedPlugins?: Record<string, InstalledPluginMetadata>;
|
|
404
|
+
template?: TemplateReference;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Service responsible for package manifest operations.
|
|
408
|
+
* Handles reading and writing launch77 metadata in package.json files.
|
|
409
|
+
* The "package manifest" refers to the launch77 section within package.json.
|
|
410
|
+
*/
|
|
411
|
+
declare class PackageManifestService {
|
|
412
|
+
private filesystemService;
|
|
413
|
+
constructor(filesystemService?: FilesystemService);
|
|
414
|
+
/**
|
|
415
|
+
* Read the launch77 section (package manifest) from a package.json
|
|
416
|
+
*/
|
|
417
|
+
readPackageManifest(packagePath: string): Promise<PackageManifest | null>;
|
|
418
|
+
/**
|
|
419
|
+
* Write the launch77 section (package manifest) to a package.json
|
|
420
|
+
*/
|
|
421
|
+
writePackageManifest(packagePath: string, manifest: PackageManifest): Promise<void>;
|
|
422
|
+
/**
|
|
423
|
+
* Add an installed plugin to the manifest
|
|
424
|
+
*/
|
|
425
|
+
addInstalledPlugin(packagePath: string, pluginKey: string, metadata: InstalledPluginMetadata): Promise<void>;
|
|
426
|
+
/**
|
|
427
|
+
* Remove an installed plugin from the manifest
|
|
428
|
+
*/
|
|
429
|
+
removeInstalledPlugin(packagePath: string, pluginKey: string): Promise<void>;
|
|
430
|
+
/**
|
|
431
|
+
* Get installed plugins from the manifest
|
|
432
|
+
*/
|
|
433
|
+
getInstalledPlugins(packagePath: string): Promise<Record<string, InstalledPluginMetadata>>;
|
|
434
|
+
/**
|
|
435
|
+
* Check if a plugin is installed
|
|
436
|
+
*/
|
|
437
|
+
isPluginInstalled(packagePath: string, pluginKey: string): Promise<boolean>;
|
|
438
|
+
/**
|
|
439
|
+
* Get a specific installed plugin metadata
|
|
440
|
+
*/
|
|
441
|
+
getInstalledPlugin(packagePath: string, pluginKey: string): Promise<InstalledPluginMetadata | null>;
|
|
442
|
+
/**
|
|
443
|
+
* Update package.json dependencies
|
|
444
|
+
*/
|
|
445
|
+
addDependency(packagePath: string, packageName: string, version: string, isDev?: boolean): Promise<void>;
|
|
446
|
+
/**
|
|
447
|
+
* Remove a dependency from package.json
|
|
448
|
+
*/
|
|
449
|
+
removeDependency(packagePath: string, packageName: string): Promise<void>;
|
|
450
|
+
/**
|
|
451
|
+
* Get the package.json content
|
|
452
|
+
*/
|
|
453
|
+
readPackageJson(packagePath: string): Promise<unknown>;
|
|
454
|
+
/**
|
|
455
|
+
* Write the package.json content
|
|
456
|
+
*/
|
|
457
|
+
writePackageJson(packagePath: string, packageJson: unknown): Promise<void>;
|
|
117
458
|
}
|
|
118
459
|
|
|
119
460
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* @param cwd - Current working directory path
|
|
123
|
-
* @returns Context information about the workspace location
|
|
461
|
+
* Resolved plugin information for installation
|
|
124
462
|
*/
|
|
125
|
-
|
|
463
|
+
interface ResolvedPlugin {
|
|
464
|
+
name: string;
|
|
465
|
+
version: string;
|
|
466
|
+
path: string;
|
|
467
|
+
source: 'local' | 'npm';
|
|
468
|
+
metadata?: PluginMetadata;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Service responsible for plugin installation workflow.
|
|
472
|
+
* Handles generator execution, dependency management, and installation tracking.
|
|
473
|
+
*/
|
|
474
|
+
declare class PluginInstaller {
|
|
475
|
+
private filesystemService;
|
|
476
|
+
constructor(filesystemService?: FilesystemService);
|
|
477
|
+
/**
|
|
478
|
+
* Install a plugin into the target location
|
|
479
|
+
*/
|
|
480
|
+
install(plugin: ResolvedPlugin, target: Target, targetPath: string, context: Launch77Context, logger?: (message: string) => void): Promise<void>;
|
|
481
|
+
/**
|
|
482
|
+
* Run a plugin's generator
|
|
483
|
+
*/
|
|
484
|
+
runGenerator(plugin: ResolvedPlugin, _target: Target, targetPath: string, launch77Context: Launch77Context, logger?: (message: string) => void): Promise<void>;
|
|
485
|
+
/**
|
|
486
|
+
* Track plugin installation in package.json
|
|
487
|
+
*/
|
|
488
|
+
trackInstallation(plugin: ResolvedPlugin, targetPath: string): Promise<void>;
|
|
489
|
+
/**
|
|
490
|
+
* Check if a plugin is already installed
|
|
491
|
+
*/
|
|
492
|
+
isInstalled(pluginName: string, targetPath: string): Promise<boolean>;
|
|
493
|
+
/**
|
|
494
|
+
* Get installed plugin metadata
|
|
495
|
+
*/
|
|
496
|
+
getInstalledPlugin(pluginName: string, targetPath: string): Promise<InstalledPluginMetadata | null>;
|
|
497
|
+
/**
|
|
498
|
+
* Uninstall a plugin
|
|
499
|
+
*/
|
|
500
|
+
uninstall(pluginName: string, targetPath: string): Promise<void>;
|
|
501
|
+
/**
|
|
502
|
+
* Install npm dependencies
|
|
503
|
+
*/
|
|
504
|
+
installDependencies(targetPath: string): Promise<void>;
|
|
505
|
+
/**
|
|
506
|
+
* Extract plugin key from package name
|
|
507
|
+
*/
|
|
508
|
+
private extractPluginKey;
|
|
509
|
+
}
|
|
126
510
|
|
|
127
511
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* Provides validation for plugin names and npm package names.
|
|
131
|
-
* Used by both CLI and plugin-runtime to ensure consistent naming rules.
|
|
512
|
+
* Result of downloading an npm package
|
|
132
513
|
*/
|
|
133
|
-
interface
|
|
514
|
+
interface DownloadPackageResult {
|
|
515
|
+
path: string;
|
|
516
|
+
version: string;
|
|
517
|
+
name: string;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Options for downloading an npm package
|
|
521
|
+
*/
|
|
522
|
+
interface DownloadPackageOptions {
|
|
523
|
+
packageName: string;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Validation result for npm package names
|
|
527
|
+
*/
|
|
528
|
+
interface ValidationResult {
|
|
134
529
|
isValid: boolean;
|
|
135
530
|
error?: string;
|
|
136
531
|
}
|
|
137
532
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* Rules:
|
|
141
|
-
* - Must start with a lowercase letter
|
|
142
|
-
* - Can contain lowercase letters, numbers, and hyphens
|
|
143
|
-
* - Cannot contain uppercase, spaces, underscores, or special characters
|
|
144
|
-
*
|
|
145
|
-
* @param name - The plugin name to validate
|
|
146
|
-
* @returns ValidationResult indicating if valid and error message if not
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* validatePluginName('my-plugin') // { isValid: true }
|
|
150
|
-
* validatePluginName('MyPlugin') // { isValid: false, error: '...' }
|
|
533
|
+
* Service responsible for NPM operations.
|
|
534
|
+
* Provides a clean abstraction over npm commands and package management.
|
|
151
535
|
*/
|
|
152
|
-
declare
|
|
536
|
+
declare class NpmService {
|
|
537
|
+
/**
|
|
538
|
+
* Validate an npm package name (scoped or unscoped)
|
|
539
|
+
*
|
|
540
|
+
* Rules for unscoped packages:
|
|
541
|
+
* - Must start with a lowercase letter
|
|
542
|
+
* - Can contain lowercase letters, numbers, hyphens, periods, underscores
|
|
543
|
+
*
|
|
544
|
+
* Rules for scoped packages:
|
|
545
|
+
* - Format: @org/package
|
|
546
|
+
* - Both org and package follow npm naming rules
|
|
547
|
+
*
|
|
548
|
+
* @param name - The npm package name to validate
|
|
549
|
+
* @returns ValidationResult indicating if valid and error message if not
|
|
550
|
+
*/
|
|
551
|
+
validatePackageName(name: string): ValidationResult;
|
|
552
|
+
/**
|
|
553
|
+
* Parse a package name input and determine its type
|
|
554
|
+
*
|
|
555
|
+
* Returns information about whether the input is:
|
|
556
|
+
* - A scoped npm package (e.g., @org/package)
|
|
557
|
+
* - An unscoped name (e.g., my-package)
|
|
558
|
+
* - Invalid
|
|
559
|
+
*/
|
|
560
|
+
parsePackageName(name: string): {
|
|
561
|
+
type: 'scoped' | 'unscoped' | 'invalid';
|
|
562
|
+
isValid: boolean;
|
|
563
|
+
name?: string;
|
|
564
|
+
org?: string;
|
|
565
|
+
package?: string;
|
|
566
|
+
error?: string;
|
|
567
|
+
};
|
|
568
|
+
/**
|
|
569
|
+
* Install npm dependencies in a directory
|
|
570
|
+
*/
|
|
571
|
+
install(cwd: string): Promise<void>;
|
|
572
|
+
/**
|
|
573
|
+
* Install npm dependencies with legacy peer deps flag
|
|
574
|
+
*/
|
|
575
|
+
installWithLegacyPeerDeps(cwd: string): Promise<void>;
|
|
576
|
+
/**
|
|
577
|
+
* Download an npm package to a temporary directory
|
|
578
|
+
*/
|
|
579
|
+
downloadPackage(options: DownloadPackageOptions): Promise<DownloadPackageResult>;
|
|
580
|
+
/**
|
|
581
|
+
* Get information about an npm package
|
|
582
|
+
*/
|
|
583
|
+
getPackageInfo(packageName: string): Promise<unknown>;
|
|
584
|
+
/**
|
|
585
|
+
* Check if a package exists on npm
|
|
586
|
+
*/
|
|
587
|
+
packageExists(packageName: string): Promise<boolean>;
|
|
588
|
+
/**
|
|
589
|
+
* Install a specific package as a dependency
|
|
590
|
+
*/
|
|
591
|
+
installPackage(packageName: string, cwd: string, isDev?: boolean): Promise<void>;
|
|
592
|
+
/**
|
|
593
|
+
* Uninstall a package
|
|
594
|
+
*/
|
|
595
|
+
uninstallPackage(packageName: string, cwd: string): Promise<void>;
|
|
596
|
+
/**
|
|
597
|
+
* Clean up a temporary download directory
|
|
598
|
+
*/
|
|
599
|
+
cleanupDownload(downloadPath: string): Promise<void>;
|
|
600
|
+
}
|
|
601
|
+
|
|
153
602
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* Rules for unscoped packages:
|
|
157
|
-
* - Must start with a lowercase letter
|
|
158
|
-
* - Can contain lowercase letters, numbers, and hyphens
|
|
159
|
-
*
|
|
160
|
-
* Rules for scoped packages:
|
|
161
|
-
* - Format: @org/package
|
|
162
|
-
* - Org must contain lowercase letters, numbers, and hyphens
|
|
163
|
-
* - Package must contain lowercase letters, numbers, and hyphens
|
|
164
|
-
*
|
|
165
|
-
* @param name - The npm package name to validate
|
|
166
|
-
* @returns ValidationResult indicating if valid and error message if not
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* isValidNpmPackageName('my-package') // { isValid: true }
|
|
170
|
-
* isValidNpmPackageName('@org/my-package') // { isValid: true }
|
|
171
|
-
* isValidNpmPackageName('@release') // { isValid: false, error: '...' }
|
|
603
|
+
* Base interface for package resolution results
|
|
172
604
|
*/
|
|
173
|
-
|
|
605
|
+
interface PackageResolution {
|
|
606
|
+
/** The source of the package */
|
|
607
|
+
source: 'local' | 'npm';
|
|
608
|
+
/** The resolved name/package to use */
|
|
609
|
+
resolvedName: string;
|
|
610
|
+
/** The local path if source is 'local' */
|
|
611
|
+
localPath?: string;
|
|
612
|
+
/** The npm package name if source is 'npm' */
|
|
613
|
+
npmPackage?: string;
|
|
614
|
+
/** The version from package.json (required for local packages, undefined for npm until installed) */
|
|
615
|
+
version?: string;
|
|
616
|
+
}
|
|
174
617
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* Returns information about whether the input is:
|
|
178
|
-
* - A scoped npm package (e.g., @org/package)
|
|
179
|
-
* - An unscoped name (e.g., my-plugin)
|
|
180
|
-
* - Invalid
|
|
618
|
+
* Abstract base class for resolving Launch77 packages
|
|
181
619
|
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
620
|
+
* Provides generic resolution logic for finding packages in:
|
|
621
|
+
* 1. Local workspace directory (e.g., plugins/, app-templates/)
|
|
622
|
+
* 2. npm packages with configured prefix (e.g., @launch77-shared/plugin-*, @launch77-shared/app-template-*)
|
|
184
623
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
|
|
190
|
-
|
|
624
|
+
* Concrete implementations must provide:
|
|
625
|
+
* - Folder name for local resolution
|
|
626
|
+
* - Package prefix for npm resolution
|
|
627
|
+
* - Verification logic to validate local packages
|
|
628
|
+
*/
|
|
629
|
+
declare abstract class PackageResolver {
|
|
630
|
+
protected npmService: NpmService;
|
|
631
|
+
constructor();
|
|
632
|
+
/**
|
|
633
|
+
* Get the local folder name where packages of this type are stored
|
|
634
|
+
* @example 'plugins' or 'app-templates'
|
|
635
|
+
*/
|
|
636
|
+
protected abstract getFolderName(): string;
|
|
637
|
+
/**
|
|
638
|
+
* Get the npm package prefix for unscoped packages
|
|
639
|
+
* @example '@launch77-shared/plugin-' or '@launch77-shared/app-template-'
|
|
640
|
+
*/
|
|
641
|
+
protected abstract getPackagePrefix(): string;
|
|
642
|
+
/**
|
|
643
|
+
* Verify that a local package is valid and complete
|
|
644
|
+
* @param localPath - The local directory path to verify
|
|
645
|
+
* @returns true if the package is valid, false otherwise
|
|
646
|
+
*/
|
|
647
|
+
protected abstract verify(localPath: string): Promise<boolean>;
|
|
648
|
+
/**
|
|
649
|
+
* Validate package input name
|
|
650
|
+
*
|
|
651
|
+
* Accepts:
|
|
652
|
+
* - Unscoped names (e.g., "release", "my-package")
|
|
653
|
+
* - Scoped npm packages (e.g., "@ibm/package-name")
|
|
654
|
+
*
|
|
655
|
+
* Rejects:
|
|
656
|
+
* - Invalid formats
|
|
657
|
+
* - Empty strings
|
|
658
|
+
* - Names with invalid characters
|
|
659
|
+
*
|
|
660
|
+
* @param name - The package name to validate
|
|
661
|
+
* @returns ValidationResult with isValid and optional error message
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* validateInput('release') // { isValid: true }
|
|
665
|
+
* validateInput('@ibm/analytics') // { isValid: true }
|
|
666
|
+
* validateInput('@invalid') // { isValid: false, error: '...' }
|
|
667
|
+
*/
|
|
668
|
+
validateInput(name: string): ValidationResult;
|
|
669
|
+
/**
|
|
670
|
+
* Convert an unscoped package name to an npm package name
|
|
671
|
+
*
|
|
672
|
+
* Rules:
|
|
673
|
+
* - Unscoped names: prefix with configured package prefix
|
|
674
|
+
* - Scoped names: use as-is
|
|
675
|
+
*
|
|
676
|
+
* @param name - The package name (must be validated first)
|
|
677
|
+
* @returns The npm package name
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* toNpmPackageName('release') // '@launch77-shared/plugin-release' (for PluginResolver)
|
|
681
|
+
* toNpmPackageName('@ibm/analytics') // '@ibm/analytics'
|
|
682
|
+
*/
|
|
683
|
+
toNpmPackageName(name: string): string;
|
|
684
|
+
/**
|
|
685
|
+
* Read version from package.json
|
|
686
|
+
* @param packagePath - The path to the package directory
|
|
687
|
+
* @returns The version string from package.json
|
|
688
|
+
* @throws If package.json doesn't exist, can't be read, or is missing the version field
|
|
689
|
+
*/
|
|
690
|
+
private readVersion;
|
|
691
|
+
/**
|
|
692
|
+
* Resolve package location from name
|
|
693
|
+
*
|
|
694
|
+
* Resolution order:
|
|
695
|
+
* 1. Check local workspace directory (configured by getFolderName())
|
|
696
|
+
* 2. Verify local package is valid (using verify())
|
|
697
|
+
* 3. Fall back to npm package name (with configured prefix)
|
|
698
|
+
* 4. Read version from package.json (if available)
|
|
699
|
+
*
|
|
700
|
+
* @param name - The package name to resolve
|
|
701
|
+
* @param workspaceRoot - The workspace root directory
|
|
702
|
+
* @returns PackageResolution with source, resolved location, and version
|
|
703
|
+
*
|
|
704
|
+
* @example
|
|
705
|
+
* // Local package found
|
|
706
|
+
* await resolveLocation('my-package', '/workspace')
|
|
707
|
+
* // { source: 'local', resolvedName: 'my-package', localPath: '/workspace/plugins/my-package', version: '1.0.0' }
|
|
708
|
+
*
|
|
709
|
+
* // Not found locally, resolve to npm
|
|
710
|
+
* await resolveLocation('release', '/workspace')
|
|
711
|
+
* // { source: 'npm', resolvedName: 'release', npmPackage: '@launch77-shared/plugin-release' }
|
|
712
|
+
*
|
|
713
|
+
* // Scoped package always resolves to npm
|
|
714
|
+
* await resolveLocation('@ibm/analytics', '/workspace')
|
|
715
|
+
* // { source: 'npm', resolvedName: '@ibm/analytics', npmPackage: '@ibm/analytics' }
|
|
716
|
+
*/
|
|
717
|
+
resolveLocation(name: string, workspaceRoot: string): Promise<PackageResolution>;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Plugin resolver implementation
|
|
191
722
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
723
|
+
* Resolves plugins from:
|
|
724
|
+
* - Local workspace plugins/ directory
|
|
725
|
+
* - npm packages with @launch77-shared/plugin- prefix
|
|
194
726
|
*/
|
|
195
|
-
declare
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
package?: string;
|
|
201
|
-
error?: string;
|
|
202
|
-
};
|
|
727
|
+
declare class PluginResolver extends PackageResolver {
|
|
728
|
+
protected getFolderName(): string;
|
|
729
|
+
protected getPackagePrefix(): string;
|
|
730
|
+
protected verify(localPath: string): Promise<boolean>;
|
|
731
|
+
}
|
|
203
732
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
packageJsonVersion: string;
|
|
207
|
-
pluginJsonVersion: string;
|
|
733
|
+
declare class PluginNotFoundError extends Error {
|
|
734
|
+
constructor(pluginName: string);
|
|
208
735
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
errors: PluginConsistencyError[];
|
|
736
|
+
declare class InvalidPluginContextError extends Error {
|
|
737
|
+
constructor(message: string);
|
|
212
738
|
}
|
|
213
739
|
/**
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* This ensures that plugin templates (which use package.json versions during development)
|
|
217
|
-
* install the same library versions for end users (which use plugin.json versions).
|
|
218
|
-
*
|
|
219
|
-
* @param pluginPath - Absolute path to the plugin directory
|
|
220
|
-
* @returns Validation result with any errors found
|
|
221
|
-
* @throws Error if package.json or plugin.json cannot be read
|
|
740
|
+
* Factory function to create standardized InvalidPluginContextError
|
|
741
|
+
* for when plugin:install is run outside of a package directory
|
|
222
742
|
*/
|
|
223
|
-
declare function
|
|
743
|
+
declare function createInvalidContextError(currentLocation: string): InvalidPluginContextError;
|
|
744
|
+
/**
|
|
745
|
+
* Error when plugin.json is missing the required 'targets' field
|
|
746
|
+
*/
|
|
747
|
+
declare class MissingPluginTargetsError extends Error {
|
|
748
|
+
constructor(pluginName: string);
|
|
749
|
+
}
|
|
224
750
|
/**
|
|
225
|
-
*
|
|
751
|
+
* Factory function to create error when plugin targets don't match current location
|
|
752
|
+
*/
|
|
753
|
+
declare function createInvalidTargetError(pluginName: string, currentTarget: string, allowedTargets: string[]): InvalidPluginContextError;
|
|
754
|
+
declare class PluginInstallationError extends Error {
|
|
755
|
+
readonly cause?: Error | undefined;
|
|
756
|
+
constructor(message: string, cause?: Error | undefined);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Error when plugin resolution fails
|
|
760
|
+
*/
|
|
761
|
+
declare class PluginResolutionError extends Error {
|
|
762
|
+
constructor(pluginName: string, reason: string);
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Error when npm package installation fails
|
|
766
|
+
*/
|
|
767
|
+
declare class NpmInstallationError extends Error {
|
|
768
|
+
readonly cause?: Error | undefined;
|
|
769
|
+
constructor(packageName: string, cause?: Error | undefined);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Error when plugin directory is not found for deletion
|
|
773
|
+
*/
|
|
774
|
+
declare class PluginDirectoryNotFoundError extends Error {
|
|
775
|
+
constructor(pluginName: string, expectedPath: string);
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Error when plugin name validation fails
|
|
779
|
+
*/
|
|
780
|
+
declare class InvalidPluginNameError extends Error {
|
|
781
|
+
constructor(message: string);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Options for downloading an npm package
|
|
786
|
+
*/
|
|
787
|
+
interface DownloadNpmPackageOptions {
|
|
788
|
+
/** The npm package name to download */
|
|
789
|
+
packageName: string;
|
|
790
|
+
/** The workspace root directory where node_modules will be */
|
|
791
|
+
workspaceRoot: string;
|
|
792
|
+
/** Optional logger function for status messages */
|
|
793
|
+
logger?: (message: string) => void;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Result of downloading an npm package
|
|
797
|
+
*/
|
|
798
|
+
interface DownloadNpmPackageResult {
|
|
799
|
+
/** Full path to the downloaded package in node_modules */
|
|
800
|
+
packagePath: string;
|
|
801
|
+
/** The package name that was downloaded */
|
|
802
|
+
packageName: string;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Download and install an npm package to workspace node_modules
|
|
226
806
|
*
|
|
227
|
-
* This
|
|
228
|
-
*
|
|
807
|
+
* This function:
|
|
808
|
+
* - Runs `npm install {packageName} --save-dev` in the workspace root
|
|
809
|
+
* - Adds the package to workspace package.json devDependencies
|
|
810
|
+
* - Returns the path to the installed package in node_modules
|
|
229
811
|
*
|
|
230
|
-
* @param
|
|
231
|
-
* @
|
|
812
|
+
* @param options - Download options
|
|
813
|
+
* @returns The path to the installed package
|
|
814
|
+
* @throws NpmInstallationError if the download fails
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* const result = await downloadNpmPackage({
|
|
818
|
+
* packageName: '@launch77-shared/plugin-release',
|
|
819
|
+
* workspaceRoot: '/path/to/workspace',
|
|
820
|
+
* logger: (msg) => console.log(msg)
|
|
821
|
+
* })
|
|
822
|
+
* // result.packagePath: '/path/to/workspace/node_modules/@launch77-shared/plugin-release'
|
|
232
823
|
*/
|
|
233
|
-
declare function
|
|
824
|
+
declare function downloadNpmPackage(options: DownloadNpmPackageOptions): Promise<DownloadNpmPackageResult>;
|
|
234
825
|
|
|
235
|
-
export { Generator, type GeneratorContext, type InstalledPluginMetadata, type Launch77Context, type Launch77LocationType, type
|
|
826
|
+
export { type DeletePluginRequest, type DeletePluginResult, type DownloadNpmPackageOptions, type DownloadNpmPackageResult, type DownloadPackageResult, FilesystemService, Generator, type GeneratorContext, type HookResult, type InstallPluginRequest, type InstallPluginResult, type InstalledPluginMetadata, InvalidPluginContextError, InvalidPluginNameError, type Launch77Context, type Launch77LocationType, MetadataService, MissingPluginTargetsError, NpmInstallationError, NpmService, type PackageManifest, PackageManifestService, type PackageResolution, PackageResolver, type ParsedLocation, type PluginConsistencyError, PluginDirectoryNotFoundError, PluginInstallationError, PluginInstaller, type PluginMetadata, PluginNotFoundError, PluginResolutionError, PluginResolver, PluginService, type ValidationResult$1 as PluginValidationResult, StandardGenerator, type Target, type TemplateMetadata, type TemplateReference, type ValidationResult$2 as ValidationResult, type WorkspaceManifest, WorkspaceManifestService, WorkspaceService, createInvalidContextError, createInvalidTargetError, detectLaunch77Context, downloadNpmPackage, parseLocationFromPath };
|