@salesforce/storefront-next-dev 0.1.1 → 0.2.0-alpha.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 (50) hide show
  1. package/README.md +45 -36
  2. package/bin/run.js +12 -0
  3. package/dist/bundle.js +83 -0
  4. package/dist/cartridge-services/index.d.ts +2 -26
  5. package/dist/cartridge-services/index.d.ts.map +1 -1
  6. package/dist/cartridge-services/index.js +3 -336
  7. package/dist/cartridge-services/index.js.map +1 -1
  8. package/dist/commands/create-bundle.js +107 -0
  9. package/dist/commands/create-instructions.js +174 -0
  10. package/dist/commands/create-storefront.js +210 -0
  11. package/dist/commands/deploy-cartridge.js +52 -0
  12. package/dist/commands/dev.js +122 -0
  13. package/dist/commands/extensions/create.js +38 -0
  14. package/dist/commands/extensions/install.js +44 -0
  15. package/dist/commands/extensions/list.js +21 -0
  16. package/dist/commands/extensions/remove.js +38 -0
  17. package/dist/commands/generate-cartridge.js +35 -0
  18. package/dist/commands/prepare-local.js +30 -0
  19. package/dist/commands/preview.js +101 -0
  20. package/dist/commands/push.js +139 -0
  21. package/dist/config.js +87 -0
  22. package/dist/configs/react-router.config.js +3 -1
  23. package/dist/configs/react-router.config.js.map +1 -1
  24. package/dist/dependency-utils.js +314 -0
  25. package/dist/entry/client.d.ts +1 -0
  26. package/dist/entry/client.js +28 -0
  27. package/dist/entry/client.js.map +1 -0
  28. package/dist/entry/server.d.ts +15 -0
  29. package/dist/entry/server.d.ts.map +1 -0
  30. package/dist/entry/server.js +35 -0
  31. package/dist/entry/server.js.map +1 -0
  32. package/dist/flags.js +11 -0
  33. package/dist/generate-cartridge.js +620 -0
  34. package/dist/hooks/init.js +47 -0
  35. package/dist/index.d.ts +9 -29
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +413 -621
  38. package/dist/index.js.map +1 -1
  39. package/dist/local-dev-setup.js +176 -0
  40. package/dist/logger.js +105 -0
  41. package/dist/manage-extensions.js +329 -0
  42. package/dist/mrt/ssr.mjs +21 -21
  43. package/dist/mrt/ssr.mjs.map +1 -1
  44. package/dist/mrt/streamingHandler.mjs +28 -28
  45. package/dist/mrt/streamingHandler.mjs.map +1 -1
  46. package/dist/server.js +425 -0
  47. package/dist/utils.js +126 -0
  48. package/package.json +44 -9
  49. package/dist/cli.js +0 -3393
  50. /package/{LICENSE.txt → LICENSE} +0 -0
@@ -1,8 +1,5 @@
1
- import fs from "fs";
2
- import path, { extname } from "path";
3
- import archiver from "archiver";
4
1
  import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
- import { basename, extname as extname$1, join, resolve } from "node:path";
2
+ import { basename, extname, join, resolve } from "node:path";
6
3
  import { execSync } from "node:child_process";
7
4
  import { Node, Project } from "ts-morph";
8
5
  import { existsSync, readFileSync, unlinkSync } from "node:fs";
@@ -10,336 +7,6 @@ import { tmpdir } from "node:os";
10
7
  import { randomUUID } from "node:crypto";
11
8
  import { npmRunPathEnv } from "npm-run-path";
12
9
 
13
- //#region src/cartridge-services/types.ts
14
- const WEBDAV_BASE = "/on/demandware.servlet/webdav/Sites";
15
- const CARTRIDGES_PATH = "Cartridges";
16
- const HTTP_METHODS = {
17
- PUT: "PUT",
18
- POST: "POST",
19
- DELETE: "DELETE"
20
- };
21
- const CONTENT_TYPES = {
22
- APPLICATION_ZIP: "application/zip",
23
- APPLICATION_FORM_URLENCODED: "application/x-www-form-urlencoded",
24
- APPLICATION_JSON: "application/json"
25
- };
26
- const WEBDAV_OPERATIONS = {
27
- UNZIP: "UNZIP",
28
- TARGET_CARTRIDGES: "cartridges"
29
- };
30
-
31
- //#endregion
32
- //#region src/cartridge-services/sfcc-client.ts
33
- /**
34
- * Create HTTP request options for WebDAV operations (file upload/download)
35
- *
36
- * @param instance - The Commerce Cloud instance hostname
37
- * @param path - The WebDAV path (e.g., '/cartridges')
38
- * @param basicAuth - Base64 encoded basic authentication credentials (required)
39
- * @param method - HTTP method (PUT, DELETE, UNZIP, etc.)
40
- * @param formData - Optional form data for the request
41
- * @returns Configured HTTP request options for WebDAV operations
42
- */
43
- function getWebdavOptions(instance, path$1, basicAuth, method, formData) {
44
- const endpoint = `${WEBDAV_BASE}/${path$1}`;
45
- return {
46
- baseUrl: `https://${instance}`,
47
- uri: endpoint,
48
- auth: { basic: basicAuth },
49
- method,
50
- ...formData && { form: formData }
51
- };
52
- }
53
- /**
54
- * Check if an HTTP response indicates an authentication error and throw if so
55
- *
56
- * @param response - The HTTP response to check
57
- * @throws Error with authentication message if status code is 401
58
- */
59
- function checkAuthenticationError(response) {
60
- if (response.statusCode === 401) throw new Error("Authentication failed. Please login again.");
61
- }
62
- /**
63
- * Execute an HTTP request using the native fetch API with default SSL validation
64
- *
65
- * This function handles general HTTP requests and does not automatically set Content-Type headers.
66
- * Callers must set the appropriate Content-Type header in opts.headers based on their body type
67
- *
68
- * @param opts - HTTP request configuration including URL, method, headers, and body
69
- * @returns Promise resolving to an object containing the HTTP response and parsed body
70
- * @throws Error if the HTTP request fails or cannot be completed
71
- */
72
- async function makeRequest(opts) {
73
- const url = opts.uri;
74
- const fetchOptions = {
75
- ...opts,
76
- headers: {
77
- Authorization: `Basic ${opts.auth.basic}`,
78
- ...opts.headers
79
- }
80
- };
81
- if (opts.form) {
82
- const formData = new URLSearchParams();
83
- Object.entries(opts.form).forEach(([key, value]) => {
84
- formData.append(key, String(value));
85
- });
86
- fetchOptions.body = formData;
87
- fetchOptions.headers = {
88
- ...fetchOptions.headers,
89
- "Content-Type": CONTENT_TYPES.APPLICATION_FORM_URLENCODED
90
- };
91
- }
92
- try {
93
- const response = await fetch(url, fetchOptions);
94
- const body = response.headers.get("content-type")?.includes(CONTENT_TYPES.APPLICATION_JSON) ? await response.json() : await response.text();
95
- const headers = {};
96
- response.headers.forEach((value, key) => {
97
- headers[key] = value;
98
- });
99
- return {
100
- response: {
101
- statusCode: response.status,
102
- statusMessage: response.statusText,
103
- headers
104
- },
105
- body
106
- };
107
- } catch (error) {
108
- throw new Error(`HTTP request failed: ${error instanceof Error ? error.message : String(error)}`);
109
- }
110
- }
111
-
112
- //#endregion
113
- //#region src/cartridge-services/validation.ts
114
- /**
115
- * Validation error class for cartridge service parameter validation
116
- */
117
- var ValidationError = class extends Error {
118
- constructor(message) {
119
- super(message);
120
- this.name = "ValidationError";
121
- }
122
- };
123
- /**
124
- * Validate Commerce Cloud instance hostname
125
- *
126
- * @param instance - The instance hostname to validate
127
- * @throws ValidationError if instance is invalid
128
- */
129
- function validateInstance(instance) {
130
- if (!instance || typeof instance !== "string") throw new ValidationError("Instance parameter is required and must be a string");
131
- if (instance.trim().length === 0) throw new ValidationError("Instance parameter cannot be empty");
132
- if (!instance.includes(".")) throw new ValidationError("Parameter instance must be a valid domain name");
133
- }
134
- /**
135
- * Validate cartridge file (must be a ZIP file)
136
- *
137
- * @param cartridgePath - The cartridge file path to validate
138
- * @throws ValidationError if cartridge is invalid
139
- */
140
- function validateCartridgePath(cartridgePath) {
141
- if (!cartridgePath || typeof cartridgePath !== "string") throw new ValidationError("cartridge parameter is required and must be a string");
142
- if (cartridgePath.trim().length === 0) throw new ValidationError("cartridge parameter cannot be empty");
143
- const ext = extname(cartridgePath).toLowerCase();
144
- if (ext !== "") throw new ValidationError(`cartridge must be a directory, got: ${ext}`);
145
- }
146
- /**
147
- * Validate Basic Auth credentials
148
- *
149
- * @param basicAuth - The base64 encoded basic auth credentials to validate
150
- * @throws ValidationError if credentials are invalid
151
- */
152
- function validateBasicAuth(basicAuth) {
153
- if (!basicAuth || typeof basicAuth !== "string") throw new ValidationError("Basic auth credentials parameter is required and must be a string");
154
- if (basicAuth.trim().length === 0) throw new ValidationError("Basic auth credentials parameter cannot be empty");
155
- if (basicAuth.length < 10) throw new ValidationError("Basic auth credentials appear to be too short to be valid");
156
- }
157
- /**
158
- * Validate code version name
159
- *
160
- * @param version - The code version name to validate
161
- * @throws ValidationError if version is invalid
162
- */
163
- function validateVersion(version) {
164
- if (!version || typeof version !== "string") throw new ValidationError("Version parameter is required and must be a string");
165
- if (version.trim().length === 0) throw new ValidationError("Version parameter cannot be empty");
166
- if (!/^[a-zA-Z0-9._-]+$/.test(version)) throw new ValidationError("Version parameter contains invalid characters. Only alphanumeric, dots, hyphens, and underscores are allowed");
167
- }
168
- /**
169
- * Validate WebDAV path
170
- *
171
- * @param webdavPath - The WebDAV path to validate
172
- * @throws ValidationError if path is invalid
173
- */
174
- function validateWebdavPath(webdavPath) {
175
- if (!webdavPath || typeof webdavPath !== "string") throw new ValidationError("WebDAV path parameter is required and must be a string");
176
- if (!webdavPath.startsWith("/")) throw new ValidationError("WebDAV path must start with a forward slash");
177
- }
178
- /**
179
- * Validate all parameters for deployCode function
180
- *
181
- * @param instance - Commerce Cloud instance hostname
182
- * @param codeVersionName - Target code version name
183
- * @param cartridgeDirectoryPath - Path to the source directory
184
- * @param basicAuth - Base64 encoded basic auth credentials
185
- * @param cartridgeWebDevPath - WebDAV path for cartridge deployment
186
- * @throws ValidationError if any parameter is invalid
187
- */
188
- function validateDeployCodeParams(instance, codeVersionName, cartridgeDirectoryPath, basicAuth, cartridgeWebDevPath) {
189
- validateInstance(instance);
190
- validateVersion(codeVersionName);
191
- validateCartridgePath(cartridgeDirectoryPath);
192
- validateBasicAuth(basicAuth);
193
- validateWebdavPath(cartridgeWebDevPath);
194
- }
195
-
196
- //#endregion
197
- //#region src/cartridge-services/deploy-cartridge.ts
198
- /**
199
- * Extract the filename (including extension) from a file path
200
- *
201
- * @param filePath - The full path to the file
202
- * @returns The filename portion of the path (e.g., 'archive.zip' from '/path/to/archive.zip')
203
- */
204
- function getFilename(filePath) {
205
- return path.basename(filePath);
206
- }
207
- /**
208
- * Create a ZIP cartridge from a directory
209
- *
210
- * @param sourceDir - The directory to zip
211
- * @param outputPath - The output ZIP file path (can be same as sourceDir)
212
- * @returns Promise resolving when the ZIP file is created
213
- */
214
- async function zipCartridge(sourceDir, outputPath) {
215
- const archive = archiver("zip", { zlib: { level: 9 } });
216
- const output = fs.createWriteStream(outputPath);
217
- archive.pipe(output);
218
- archive.directory(sourceDir, false);
219
- await archive.finalize();
220
- }
221
- /**
222
- * Build the WebDAV endpoint URL for a file
223
- *
224
- * @param instance - The Commerce Cloud instance hostname
225
- * @param path - The WebDAV path (e.g., 'Cartridges/local_metadata')
226
- * @param file - The local file path (filename will be extracted)
227
- * @returns The complete WebDAV endpoint URL
228
- */
229
- function buildWebdavEndpoint(instance, webdavPath, file) {
230
- return `https://${instance}${WEBDAV_BASE}/${webdavPath}/${getFilename(file)}`;
231
- }
232
- /**
233
- * Unzip an uploaded archive file on Commerce Cloud via WebDAV
234
- *
235
- * @param instance - The Commerce Cloud instance hostname
236
- * @param path - The WebDAV path where the file was uploaded
237
- * @param file - The local file path (used to determine the remote filename)
238
- * @param basicAuth - Base64 encoded basic authentication credentials
239
- * @returns Promise resolving to HTTP response and body from the unzip operation
240
- */
241
- async function unzip(instance, webdavPath, file, basicAuth) {
242
- const endpoint = buildWebdavEndpoint(instance, webdavPath, file);
243
- const opts = getWebdavOptions(instance, webdavPath, basicAuth, HTTP_METHODS.POST, {
244
- method: WEBDAV_OPERATIONS.UNZIP,
245
- target: WEBDAV_OPERATIONS.TARGET_CARTRIDGES
246
- });
247
- opts.uri = endpoint;
248
- const result = await makeRequest(opts);
249
- checkAuthenticationError(result.response);
250
- return result;
251
- }
252
- /**
253
- * Delete a file from Commerce Cloud via WebDAV
254
- *
255
- * @param instance - The Commerce Cloud instance hostname
256
- * @param path - The WebDAV path where the file is located
257
- * @param file - The local file path (used to determine the remote filename)
258
- * @param basicAuth - Base64 encoded basic authentication credentials
259
- * @returns Promise resolving to HTTP response and body from the delete operation
260
- */
261
- async function deleteFile(instance, webdavPath, file, basicAuth) {
262
- const endpoint = buildWebdavEndpoint(instance, webdavPath, file);
263
- const opts = getWebdavOptions(instance, webdavPath, basicAuth, HTTP_METHODS.DELETE);
264
- opts.uri = endpoint;
265
- const result = await makeRequest(opts);
266
- checkAuthenticationError(result.response);
267
- return result;
268
- }
269
- /**
270
- * Upload a file to a specific cartridge version on Commerce Cloud via WebDAV (internal function)
271
- *
272
- * @param instance - The Commerce Cloud instance hostname
273
- * @param codeVersionName - The target code version name
274
- * @param filePath - The local file path to upload
275
- * @param basicAuth - Base64 encoded basic authentication credentials
276
- * @returns Promise resolving to HTTP response and body from the upload operation
277
- */
278
- async function postFile(instance, codeVersionName, filePath, basicAuth) {
279
- const targetPath = `${CARTRIDGES_PATH}/${codeVersionName}`;
280
- try {
281
- const endpoint = buildWebdavEndpoint(instance, targetPath, filePath);
282
- const opts = getWebdavOptions(instance, targetPath, basicAuth, HTTP_METHODS.PUT);
283
- opts.uri = endpoint;
284
- opts.body = fs.createReadStream(filePath);
285
- opts.duplex = "half";
286
- opts.headers = {
287
- ...opts.headers,
288
- "Content-Type": CONTENT_TYPES.APPLICATION_ZIP
289
- };
290
- const result = await makeRequest(opts);
291
- checkAuthenticationError(result.response);
292
- if (![
293
- 200,
294
- 201,
295
- 204
296
- ].includes(result.response.statusCode)) throw new Error(`Post file "${filePath}" failed: ${result.response.statusCode} (${result.response.statusMessage})`);
297
- return result;
298
- } catch (error) {
299
- throw new Error(`Post file "${filePath}" failed: ${error instanceof Error ? error.message : String(error)}`);
300
- }
301
- }
302
- /**
303
- * Deploy code to Commerce Cloud by uploading, unzipping, and cleaning up
304
- *
305
- * This function performs a complete code deployment workflow:
306
- * 1. Uploads the archive file via WebDAV to the specified cartridge version
307
- * 2. Unzips the archive on the server
308
- * 3. Deletes the uploaded archive file
309
- * 4. Returns the deployed version name
310
- *
311
- * @param instance - The Commerce Cloud instance hostname
312
- * @param codeVersionName - The target code version name
313
- * @param sourceDir - The local directory containing the source files to deploy
314
- * @param basicAuth - Base64 encoded basic authentication credentials
315
- * @returns Promise resolving to deployment result with the version name
316
- * @throws Error if any step of the deployment process fails
317
- */
318
- async function deployCode(instance, codeVersionName, sourceDir, basicAuth) {
319
- validateDeployCodeParams(instance, codeVersionName, sourceDir, basicAuth, `/${CARTRIDGES_PATH}/${codeVersionName}/cartridges`);
320
- const tempZipPath = path.join(path.dirname(sourceDir), `metadata-${Date.now()}.zip`);
321
- try {
322
- await zipCartridge(sourceDir, tempZipPath);
323
- const file = path.basename(tempZipPath);
324
- await postFile(instance, codeVersionName, tempZipPath, basicAuth);
325
- const unzipResult = await unzip(instance, `${CARTRIDGES_PATH}/${codeVersionName}`, file, basicAuth);
326
- if (![
327
- 200,
328
- 201,
329
- 202
330
- ].includes(unzipResult.response.statusCode)) throw new Error(`Deploy code ${file} failed (unzip step): ${unzipResult.response.statusCode} (${unzipResult.response.statusMessage})`);
331
- const deleteResult = await deleteFile(instance, `${CARTRIDGES_PATH}/${codeVersionName}`, file, basicAuth);
332
- if (![200, 204].includes(deleteResult.response.statusCode)) throw new Error(`Delete ZIP file ${file} after deployment failed (deleteFile step): ${deleteResult.response.statusCode} (${deleteResult.response.statusMessage})`);
333
- return { version: getFilename(file).replace(".zip", "") };
334
- } catch (error) {
335
- if (error instanceof Error) throw error;
336
- throw new Error(`Deploy code ${sourceDir} failed: ${String(error)}`);
337
- } finally {
338
- if (fs.existsSync(tempZipPath)) fs.unlinkSync(tempZipPath);
339
- }
340
- }
341
-
342
- //#endregion
343
10
  //#region src/cartridge-services/react-router-config.ts
344
11
  let isCliAvailable = null;
345
12
  function checkReactRouterCli(projectDirectory) {
@@ -892,7 +559,7 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
892
559
  const fullPath = join(dir, entry.name);
893
560
  if (entry.isDirectory()) {
894
561
  if (!SKIP_DIRECTORIES.includes(entry.name)) await scanDirectory(fullPath);
895
- } else if (entry.isFile() && (extname$1(entry.name) === ".ts" || extname$1(entry.name) === ".tsx" || extname$1(entry.name) === ".json")) files.push(fullPath);
562
+ } else if (entry.isFile() && (extname(entry.name) === ".ts" || extname(entry.name) === ".tsx" || extname(entry.name) === ".json")) files.push(fullPath);
896
563
  }
897
564
  };
898
565
  await scanDirectory(srcDir);
@@ -950,5 +617,5 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
950
617
  }
951
618
 
952
619
  //#endregion
953
- export { deployCode, generateMetadata };
620
+ export { generateMetadata };
954
621
  //# sourceMappingURL=index.js.map