@uipath/packager-tool-webapp 1.0.4

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.
@@ -0,0 +1,29 @@
1
+ import { type IFileSystem, type IProjectBuildOptions, type IProjectPackOptions, type IProjectRestoreOptions, type IProjectValidateOptions, type IToolLogger, ProjectTool, ToolResult } from "@uipath/solutionpackager-tool-core";
2
+ /**
3
+ * WebApp project tool implementation
4
+ * Packages WebApp-type projects into .nupkg artifacts
5
+ */
6
+ export declare class WebAppTool extends ProjectTool {
7
+ private readonly _temporaryStorage;
8
+ private _tempBuildFolder;
9
+ constructor(fileSystem: IFileSystem, logger: IToolLogger);
10
+ restoreAsync(_options: IProjectRestoreOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
11
+ validateAsync(options: IProjectValidateOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
12
+ buildAsync(_options: IProjectBuildOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
13
+ packAsync(options: IProjectPackOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
14
+ dispose(): Promise<void>;
15
+ /**
16
+ * Common helper that validates manifest file presence, loads manifest
17
+ * and instantiates a variant strategy. Throws Error with user-friendly
18
+ * message when any step fails.
19
+ */
20
+ private getManifestAndStrategy;
21
+ /**
22
+ * Standardized error handling helper
23
+ */
24
+ private handleError;
25
+ /**
26
+ * Create a logger wrapper for strategy methods
27
+ */
28
+ private createStrategyLogger;
29
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@uipath/packager-tool-webapp",
3
+ "version": "1.0.4",
4
+ "description": "UiPath WebApp tool implementation",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "source": "./src/index.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/UiPath/cli.git",
15
+ "directory": "packages/packager/packager-tool-webapp"
16
+ },
17
+ "publishConfig": {
18
+ "registry": "https://registry.npmjs.org/"
19
+ },
20
+ "types": "./dist/index.d.ts",
21
+ "scripts": {
22
+ "build": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/solutionpackager-tool-core && tsc --emitDeclarationOnly --outDir dist",
23
+ "dev": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/solutionpackager-tool-core --watch",
24
+ "test": "vitest run",
25
+ "test:browser": "vitest run --config=vitest.browser.config.ts",
26
+ "test:coverage": "vitest run --coverage",
27
+ "test:browser:coverage": "vitest run --config=vitest.browser.config.ts --coverage",
28
+ "test:all": "bun run test && bun run test:browser",
29
+ "test:all:coverage": "bun run test:coverage && bun run test:browser:coverage",
30
+ "prepack": "bun run build",
31
+ "publish:dry": "bun publish --dry-run",
32
+ "publish:gh": "bun publish",
33
+ "version:patch": "bun version patch --no-git-tag-version",
34
+ "version:minor": "bun version minor --no-git-tag-version",
35
+ "version:major": "bun version major --no-git-tag-version",
36
+ "lint": "biome check ."
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "!dist/**/*.map",
41
+ "src"
42
+ ],
43
+ "author": "",
44
+ "license": "ISC",
45
+ "peerDependencies": {
46
+ "@uipath/solutionpackager-tool-core": "0.0.31"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.5.0",
50
+ "@uipath/solutionpackager-tool-core": "0.0.31",
51
+ "@vitest/browser": "^4.0.14",
52
+ "@vitest/browser-playwright": "^4.0.14",
53
+ "@vitest/coverage-v8": "^4.0.14",
54
+ "playwright": "^1.57.0",
55
+ "typescript": "^5.9.3",
56
+ "vitest": "^4.0.14"
57
+ }
58
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Constants used throughout the WebApp tool implementation
3
+ */
4
+
5
+ /**
6
+ * File names
7
+ */
8
+ export const PROJECT_JSON_FILE = "project.json";
9
+
10
+ /**
11
+ * Default bundle path if not specified in manifest
12
+ */
13
+ export const DEFAULT_BUNDLE_PATH = "source/dist";
14
+
15
+ /**
16
+ * Target runtime for operate.json
17
+ */
18
+ export const TARGET_RUNTIME = "Coded";
19
+
20
+ /**
21
+ * Default entry point type
22
+ */
23
+ export const DEFAULT_ENTRY_POINT_TYPE = "api";
24
+
25
+ /**
26
+ * Error messages
27
+ */
28
+ export const ERROR_MESSAGES = {
29
+ MANIFEST_NOT_FOUND: (file: string): string =>
30
+ `WebApp manifest not found: ${file}. This file is required for WebApp projects.`,
31
+
32
+ PROJECT_JSON_FOUND:
33
+ "project.json found in WebApp project. The WebApp tool only supports Coded web apps without project.json.",
34
+
35
+ MANIFEST_LOAD_FAILED: (file: string): string =>
36
+ `Failed to load or parse ${file}`,
37
+
38
+ BUNDLE_NOT_FOUND: (path: string): string =>
39
+ `Compiled bundle not found at ${path}. Ensure the project is built before packing.`,
40
+
41
+ BUNDLE_NOT_DIRECTORY: (path: string): string =>
42
+ `Bundle path ${path} exists but is not a directory.`,
43
+
44
+ BUILD_NOT_SUPPORTED:
45
+ "Build execution (isCompiled=false) is not yet supported. Please build the project manually and set isCompiled=true.",
46
+
47
+ VALIDATION_FAILED: (context: string): string =>
48
+ `Validation failed: ${context}`,
49
+
50
+ PACKING_FAILED: (context: string): string =>
51
+ `An error occurred while packing WebApp project: ${context}`,
52
+
53
+ PACKAGE_NAME_REQUIRED: "Package name is required",
54
+
55
+ PACKAGE_VERSION_REQUIRED: "Package version is required",
56
+
57
+ PROJECT_PATH_REQUIRED: "Project path is required",
58
+
59
+ OUTPUT_PATH_REQUIRED: "Output path is required",
60
+ } as const;
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { toolsFactoryRepository } from "@uipath/solutionpackager-tool-core";
2
+ import { WebAppToolFactory } from "./webapp-tool-factory.js";
3
+
4
+ export { WebAppTool } from "./webapp-tool.js";
5
+ export { WebAppToolFactory } from "./webapp-tool-factory.js";
6
+
7
+ toolsFactoryRepository.registerProjectToolFactory(new WebAppToolFactory());
@@ -0,0 +1,45 @@
1
+ export enum WebAppVariantType {
2
+ Coded = "Coded",
3
+ JS = "JS",
4
+ }
5
+
6
+ /** @public */
7
+ export enum AppSubType {
8
+ /** Coded app subtype that is registered with Solutions */
9
+ Coded = "Coded",
10
+ /** CodedAction app subtype that is registered with Solutions */
11
+ CodedAction = "CodedAction",
12
+ }
13
+
14
+ /**
15
+ * Variant-specific configurations
16
+ */
17
+ export interface CodedAppConfig {
18
+ /** If true: pack only; If false: build → pack (future) */
19
+ isCompiled?: boolean;
20
+ /** Compiled bundle folder location (default: dist/) */
21
+ bundlePath?: string;
22
+ /** Optional build command for future scenarios */
23
+ buildCommand?: string;
24
+ isActionApp?: boolean;
25
+ }
26
+
27
+ export interface JsConfig {
28
+ // Placeholder for future properties specific to JS variant
29
+ [key: string]: unknown;
30
+ }
31
+
32
+ /** Map each variant to its specific config type */
33
+ type ManifestConfigMap = {
34
+ [WebAppVariantType.Coded]: CodedAppConfig;
35
+ [WebAppVariantType.JS]: JsConfig;
36
+ };
37
+
38
+ /** WebApp manifest type parameterized by variant */
39
+ export type WebAppManifest<T extends WebAppVariantType = WebAppVariantType> = {
40
+ type: T;
41
+ solutionResourceSubType: string;
42
+ config?: ManifestConfigMap[T];
43
+ };
44
+
45
+ export const WEBAPP_MANIFEST_FILE_NAME = "webAppManifest.json";
@@ -0,0 +1,413 @@
1
+ import type {
2
+ IFileSystem,
3
+ OperateFileModel,
4
+ PackageDescriptorFileModel,
5
+ } from "@uipath/solutionpackager-tool-core";
6
+ import {
7
+ NugetConstants,
8
+ NugetPackager,
9
+ Path,
10
+ ProjectTypes,
11
+ TargetFramework,
12
+ } from "@uipath/solutionpackager-tool-core";
13
+ import {
14
+ DEFAULT_BUNDLE_PATH,
15
+ DEFAULT_ENTRY_POINT_TYPE,
16
+ ERROR_MESSAGES,
17
+ TARGET_RUNTIME,
18
+ } from "../constants.js";
19
+ import type { WebAppManifest } from "../models/webapp-manifest.js";
20
+ import {
21
+ WEBAPP_MANIFEST_FILE_NAME,
22
+ type WebAppVariantType,
23
+ } from "../models/webapp-manifest.js";
24
+ import { copyDirectoryAsync } from "../utils/fs-helpers.js";
25
+ import type { PackageArgs, VariantStrategy } from "./variant-strategy.js";
26
+
27
+ /**
28
+ * Coded app strategy for Coded variant
29
+ * Supports isCompiled=true (v1) and isCompiled=false (future)
30
+ */
31
+ export class CodedAppStrategy implements VariantStrategy {
32
+ constructor(private readonly fileSystem: IFileSystem) {}
33
+
34
+ /**
35
+ * Variant-specific validation: warn if compiled bundle is expected but missing
36
+ */
37
+ async validateAsync(args: {
38
+ fileSystem: IFileSystem;
39
+ projectPath: string;
40
+ manifest: WebAppManifest;
41
+ logger?: {
42
+ info: (message: string) => void;
43
+ error: (message: string) => void;
44
+ progress: (message: string) => void;
45
+ };
46
+ }): Promise<void> {
47
+ const { projectPath, manifest, logger } = args;
48
+
49
+ // Narrow manifest to Coded to read typed config safely
50
+ const typed = manifest as WebAppManifest<WebAppVariantType.Coded>;
51
+ let bundlePath = typed.config?.bundlePath;
52
+ if (typeof bundlePath !== "string" || bundlePath.length === 0) {
53
+ bundlePath = DEFAULT_BUNDLE_PATH;
54
+ }
55
+ const isCompiled = typed.config?.isCompiled ?? true;
56
+
57
+ if (isCompiled) {
58
+ const fullBundlePath = Path.join(projectPath, bundlePath);
59
+ const exists = await this.fileSystem.exists(fullBundlePath);
60
+ if (!exists) {
61
+ const message = ERROR_MESSAGES.BUNDLE_NOT_FOUND(fullBundlePath);
62
+
63
+ // Prefer `warn` if available on the provided logger, otherwise fall back to `info`.
64
+ if ((logger as any)?.warn) {
65
+ (logger as any).warn(message);
66
+ } else {
67
+ logger?.info(`Warning: ${message}`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ async packageAsync(args: PackageArgs): Promise<string> {
74
+ const { projectPath, manifest, outputPath, packageInfo, logger } = args;
75
+
76
+ logger?.info(
77
+ `Packaging Coded variant: ${packageInfo.id}@${packageInfo.version}`,
78
+ );
79
+
80
+ // Get bundle path from manifest or use default
81
+ let bundlePath = (manifest as WebAppManifest<WebAppVariantType.Coded>)
82
+ .config?.bundlePath;
83
+ if (typeof bundlePath !== "string" || bundlePath.length === 0) {
84
+ bundlePath = DEFAULT_BUNDLE_PATH;
85
+ }
86
+ const isCompiled =
87
+ (manifest as WebAppManifest<WebAppVariantType.Coded>).config
88
+ ?.isCompiled ?? true;
89
+
90
+ const fullBundlePath = Path.join(projectPath, bundlePath);
91
+
92
+ // Validate compiled bundle exists (for v1, isCompiled=true)
93
+ if (isCompiled) {
94
+ logger?.progress("Validating compiled bundle...");
95
+ const bundleExists = await this.fileSystem.exists(fullBundlePath);
96
+ if (!bundleExists) {
97
+ throw new Error(
98
+ ERROR_MESSAGES.BUNDLE_NOT_FOUND(fullBundlePath),
99
+ );
100
+ }
101
+
102
+ const bundleStat = await this.fileSystem.stat(fullBundlePath);
103
+ if (!bundleStat?.isDirectory()) {
104
+ throw new Error(
105
+ ERROR_MESSAGES.BUNDLE_NOT_DIRECTORY(fullBundlePath),
106
+ );
107
+ }
108
+ } else {
109
+ // Future: Run build command
110
+ logger?.info(
111
+ "Build mode (isCompiled=false) is not yet implemented in v1.",
112
+ );
113
+ throw new Error(ERROR_MESSAGES.BUILD_NOT_SUPPORTED);
114
+ }
115
+
116
+ // Create bundle directory structure (similar to api-workflow)
117
+ const localBuildFolder = Path.join(
118
+ outputPath,
119
+ NugetConstants.OutputFolderName,
120
+ );
121
+ const contentFolder = Path.join(
122
+ localBuildFolder,
123
+ NugetConstants.ContentFolderName,
124
+ );
125
+ await this.fileSystem.mkdir(contentFolder);
126
+
127
+ try {
128
+ // Copy bundle to content folder
129
+ logger?.progress("Copying bundle to content folder...");
130
+ await copyDirectoryAsync(
131
+ this.fileSystem,
132
+ fullBundlePath,
133
+ contentFolder,
134
+ );
135
+
136
+ // Prepare metadata files
137
+ logger?.progress("Preparing metadata files...");
138
+ await this.prepareMetadataFiles(
139
+ localBuildFolder,
140
+ contentFolder,
141
+ packageInfo,
142
+ manifest,
143
+ projectPath,
144
+ );
145
+
146
+ // Create the NuGet package using native packager
147
+ logger?.progress("Creating NuGet package...");
148
+ const nupkgFileName = `${packageInfo.id}.${packageInfo.version}.nupkg`;
149
+ const nupkgPath = Path.join(outputPath, nupkgFileName);
150
+
151
+ const packager = new NugetPackager(this.fileSystem);
152
+ const result = await packager.packAsync(
153
+ localBuildFolder,
154
+ packageInfo,
155
+ nupkgPath,
156
+ );
157
+
158
+ logger?.info(`Package created successfully: ${result.outputPath}`);
159
+ return result.outputPath;
160
+ } finally {
161
+ // Always cleanup the temporary build folder
162
+ try {
163
+ await this.fileSystem.rm(localBuildFolder);
164
+ } catch (cleanupError) {
165
+ logger?.error(
166
+ `Failed to cleanup build folder: ${
167
+ cleanupError instanceof Error
168
+ ? cleanupError.message
169
+ : String(cleanupError)
170
+ }`,
171
+ );
172
+ }
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Prepare metadata files (operate.json, package-descriptor.json, bindings.json, bindings_v2.json, entry-points.json)
178
+ */
179
+ private async prepareMetadataFiles(
180
+ _localBuildFolder: string,
181
+ contentFolder: string,
182
+ packageInfo: {
183
+ id: string;
184
+ version: string;
185
+ author?: string;
186
+ description?: string;
187
+ },
188
+ _manifest: any,
189
+ projectPath: string,
190
+ ): Promise<void> {
191
+ // Determine main file - check for index.html or use default
192
+ let mainFile = "index.html";
193
+ const indexHtmlPath = Path.join(contentFolder, "index.html");
194
+ const indexHtmlExists = await this.fileSystem.exists(indexHtmlPath);
195
+ if (!indexHtmlExists) {
196
+ // Check for other common entry points
197
+ const possibleEntries = ["index.html", "main.html", "app.html"];
198
+ for (const entry of possibleEntries) {
199
+ const entryPath = Path.join(contentFolder, entry);
200
+ if (await this.fileSystem.exists(entryPath)) {
201
+ mainFile = entry;
202
+ break;
203
+ }
204
+ }
205
+ }
206
+
207
+ // Create operate.json in content folder
208
+ const operatePath = Path.join(
209
+ contentFolder,
210
+ NugetConstants.OperateFileName,
211
+ );
212
+ const operateModel: OperateFileModel & { targetRuntime: string } = {
213
+ projectId: packageInfo.id,
214
+ main: mainFile,
215
+ contentType: ProjectTypes.WebApp,
216
+ targetFramework: TargetFramework.Portable,
217
+ targetRuntime: TARGET_RUNTIME,
218
+ runtimeOptions: {
219
+ requiresUserInteraction: false,
220
+ isAttended: false,
221
+ },
222
+ };
223
+
224
+ const operateJson = JSON.stringify(operateModel, null, 2);
225
+ await this.fileSystem.writeFile(operatePath, operateJson);
226
+
227
+ // Copy webAppManifest.json from project root into content (as-is)
228
+ const manifestSourcePath = Path.join(
229
+ projectPath,
230
+ WEBAPP_MANIFEST_FILE_NAME,
231
+ );
232
+ const manifestDestPath = Path.join(
233
+ contentFolder,
234
+ WEBAPP_MANIFEST_FILE_NAME,
235
+ );
236
+ const manifestExists = await this.fileSystem.exists(manifestSourcePath);
237
+ if (manifestExists) {
238
+ const manifestContent =
239
+ await this.fileSystem.readFile(manifestSourcePath);
240
+ if (manifestContent) {
241
+ await this.fileSystem.writeFile(
242
+ manifestDestPath,
243
+ manifestContent,
244
+ );
245
+ }
246
+ }
247
+
248
+ // Copy uipath.json from project root into content (if it exists)
249
+ const uipathJsonSourcePath = Path.join(projectPath, "uipath.json");
250
+ const uipathJsonDestPath = Path.join(contentFolder, "uipath.json");
251
+ const uipathJsonExists =
252
+ await this.fileSystem.exists(uipathJsonSourcePath);
253
+ if (uipathJsonExists) {
254
+ const uipathJsonContent =
255
+ await this.fileSystem.readFile(uipathJsonSourcePath);
256
+ if (uipathJsonContent) {
257
+ await this.fileSystem.writeFile(
258
+ uipathJsonDestPath,
259
+ uipathJsonContent,
260
+ );
261
+ }
262
+ }
263
+
264
+ // Create or copy bindings.json (version 1.0)
265
+ const bindingsPath = Path.join(contentFolder, "bindings.json");
266
+ const bindingsExists = await this.fileSystem.exists(bindingsPath);
267
+
268
+ if (!bindingsExists) {
269
+ const bindingsJson = JSON.stringify(
270
+ {
271
+ version: "1.0",
272
+ resources: [],
273
+ },
274
+ null,
275
+ 2,
276
+ );
277
+ await this.fileSystem.writeFile(bindingsPath, bindingsJson);
278
+ }
279
+
280
+ // Create or copy bindings_v2.json (version 2.0)
281
+ const bindingsV2Path = Path.join(contentFolder, "bindings_v2.json");
282
+ const bindingsV2Exists = await this.fileSystem.exists(bindingsV2Path);
283
+
284
+ if (!bindingsV2Exists) {
285
+ const bindingsV2Json = JSON.stringify(
286
+ {
287
+ version: "2.0",
288
+ resources: [],
289
+ },
290
+ null,
291
+ 2,
292
+ );
293
+ await this.fileSystem.writeFile(bindingsV2Path, bindingsV2Json);
294
+ }
295
+
296
+ // Create or copy entry-points.json
297
+ const entryPointsPath = Path.join(contentFolder, "entry-points.json");
298
+ const projectEntryPointsPath = Path.join(
299
+ projectPath,
300
+ "entry-points.json",
301
+ );
302
+
303
+ let entryPointsContent: string;
304
+ const projectEntryPointsExists = await this.fileSystem.exists(
305
+ projectEntryPointsPath,
306
+ );
307
+
308
+ if (projectEntryPointsExists) {
309
+ const entryPointsData = await this.fileSystem.readFile(
310
+ projectEntryPointsPath,
311
+ );
312
+ if (entryPointsData) {
313
+ entryPointsContent = new TextDecoder().decode(entryPointsData);
314
+ } else {
315
+ entryPointsContent = this.createDefaultEntryPoints(mainFile);
316
+ }
317
+ } else {
318
+ entryPointsContent = this.createDefaultEntryPoints(mainFile);
319
+ }
320
+
321
+ await this.fileSystem.writeFile(entryPointsPath, entryPointsContent);
322
+
323
+ // Create package-descriptor.json in content folder
324
+ const packageDescriptorPath = Path.join(
325
+ contentFolder,
326
+ NugetConstants.PackageDescriptorFileName,
327
+ );
328
+ const packageDescriptor: PackageDescriptorFileModel = {
329
+ files: {
330
+ [NugetConstants.OperateFileName]: Path.join(
331
+ NugetConstants.ContentFolderName,
332
+ NugetConstants.OperateFileName,
333
+ ),
334
+ "entry-points.json": Path.join(
335
+ NugetConstants.ContentFolderName,
336
+ "entry-points.json",
337
+ ),
338
+ "bindings.json": Path.join(
339
+ NugetConstants.ContentFolderName,
340
+ "bindings_v2.json",
341
+ ),
342
+ },
343
+ };
344
+
345
+ const packageDescriptorJson = JSON.stringify(
346
+ packageDescriptor,
347
+ null,
348
+ 2,
349
+ );
350
+ await this.fileSystem.writeFile(
351
+ packageDescriptorPath,
352
+ packageDescriptorJson,
353
+ );
354
+ }
355
+
356
+ /**
357
+ * Create default entry-points.json structure
358
+ */
359
+ private createDefaultEntryPoints(mainFile: string): string {
360
+ const uniqueId = this.generateUniqueId();
361
+
362
+ const entryPoints = {
363
+ $schema: "https://cloud.uipath.com/draft/2024-12/entry-point",
364
+ $id: "entry-points-doc-001",
365
+ entryPoints: [
366
+ {
367
+ filePath: mainFile,
368
+ uniqueId: uniqueId,
369
+ type: DEFAULT_ENTRY_POINT_TYPE,
370
+ input: {
371
+ amount: { type: "integer" },
372
+ id: { type: "string" },
373
+ },
374
+ output: {
375
+ status: { type: "string" },
376
+ },
377
+ },
378
+ ],
379
+ };
380
+
381
+ return JSON.stringify(entryPoints, null, 2);
382
+ }
383
+
384
+ /**
385
+ * Generate a cryptographically secure unique ID (UUID v4-like format)
386
+ */
387
+ private generateUniqueId(): string {
388
+ const randomBytes = new Uint8Array(16);
389
+
390
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
391
+ crypto.getRandomValues(randomBytes);
392
+ } else {
393
+ throw new Error("crypto.getRandomValues is not available");
394
+ }
395
+
396
+ // Set version (4) and variant bits according to UUID v4 spec
397
+ randomBytes[6] = (randomBytes[6] & 0x0f) | 0x40; // Version 4
398
+ randomBytes[8] = (randomBytes[8] & 0x3f) | 0x80; // Variant 10
399
+
400
+ // Convert to UUID string format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
401
+ const hex = Array.from(randomBytes)
402
+ .map((b) => b.toString(16).padStart(2, "0"))
403
+ .join("");
404
+
405
+ return [
406
+ hex.substring(0, 8),
407
+ hex.substring(8, 12),
408
+ hex.substring(12, 16),
409
+ hex.substring(16, 20),
410
+ hex.substring(20, 32),
411
+ ].join("-");
412
+ }
413
+ }
@@ -0,0 +1,39 @@
1
+ import type { IFileSystem } from "@uipath/solutionpackager-tool-core";
2
+ import type { WebAppManifest } from "../models/webapp-manifest.js";
3
+ import { WebAppVariantType } from "../models/webapp-manifest.js";
4
+ import { CodedAppStrategy } from "./coded-app-strategy.js";
5
+ import type { VariantStrategy } from "./variant-strategy.js";
6
+
7
+ /**
8
+ * Factory for creating variant-specific packaging strategies
9
+ */
10
+ export class VariantStrategyFactory {
11
+ /**
12
+ * Create a strategy instance based on manifest type
13
+ * @param manifest - WebApp manifest
14
+ * @param fileSystem - File system instance
15
+ * @returns Strategy instance
16
+ * @throws Error if variant is not supported
17
+ */
18
+ static createStrategy(
19
+ manifest: WebAppManifest,
20
+ fileSystem: IFileSystem,
21
+ ): VariantStrategy {
22
+ const variant = manifest.type;
23
+
24
+ switch (variant) {
25
+ case WebAppVariantType.Coded:
26
+ return new CodedAppStrategy(fileSystem);
27
+
28
+ case WebAppVariantType.JS:
29
+ throw new Error(
30
+ "JS variant is not yet implemented. Only Coded is supported in v1.",
31
+ );
32
+
33
+ default:
34
+ throw new Error(
35
+ `Unknown WebApp variant: ${manifest.type}. Supported variants: ${Object.values(WebAppVariantType).join(", ")}`,
36
+ );
37
+ }
38
+ }
39
+ }