@adobe/aio-commerce-lib-app 0.3.1 → 1.0.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +1 -4
  3. package/bin/cli.mjs +24 -0
  4. package/dist/cjs/actions/app-config.cjs +27 -0
  5. package/dist/cjs/actions/app-config.d.cts +15 -0
  6. package/dist/cjs/actions/config.cjs +79 -0
  7. package/dist/cjs/actions/config.d.cts +15 -0
  8. package/dist/cjs/actions/installation.cjs +424 -0
  9. package/dist/cjs/actions/{index.d.cts → installation.d.cts} +4 -3
  10. package/dist/cjs/actions/scope-tree.cjs +97 -0
  11. package/dist/cjs/actions/scope-tree.d.cts +8 -0
  12. package/dist/cjs/{app-Dx0ca6oL.d.cts → app-PTKvEBea.d.cts} +6 -6
  13. package/dist/cjs/commands/generate/actions/templates/app-management/app-config.js.template +22 -0
  14. package/dist/cjs/commands/generate/actions/templates/app-management/installation.js.template +1 -1
  15. package/dist/cjs/commands/generate/actions/templates/business-configuration/config.js.template +22 -0
  16. package/dist/cjs/commands/generate/actions/templates/business-configuration/scope-tree.js.template +18 -0
  17. package/dist/cjs/commands/index.cjs +105 -113
  18. package/dist/cjs/config/index.cjs +21 -19
  19. package/dist/cjs/config/index.d.cts +32 -332
  20. package/dist/cjs/error-DJ2UAPH2.cjs +24 -0
  21. package/dist/cjs/installation-nwF2RC7F.cjs +241 -0
  22. package/dist/cjs/{logging-DYwr5WQk.cjs → logging-IDRQG0as.cjs} +2 -2
  23. package/dist/cjs/management/index.cjs +9 -8
  24. package/dist/cjs/management/index.d.cts +2 -2
  25. package/dist/cjs/parser-DIchX9SL.cjs +267 -0
  26. package/dist/cjs/router-DCw7oEQ9.cjs +417 -0
  27. package/dist/cjs/{management-Dm5h0E6l.cjs → runner-CUJ8RHzY.cjs} +24 -30
  28. package/dist/{es/index-Bxr3zvCT.d.mts → cjs/runner-Ds2m27Q4.d.cts} +49 -95
  29. package/dist/cjs/schemas-CZ6c8Id9.cjs +98 -0
  30. package/dist/cjs/validate-BegMfe-i.cjs +235 -0
  31. package/dist/es/actions/app-config.d.mts +15 -0
  32. package/dist/es/actions/app-config.mjs +25 -0
  33. package/dist/es/actions/config.d.mts +15 -0
  34. package/dist/es/actions/config.mjs +77 -0
  35. package/dist/es/actions/{index.d.mts → installation.d.mts} +4 -3
  36. package/dist/es/actions/{index.mjs → installation.mjs} +27 -427
  37. package/dist/es/actions/scope-tree.d.mts +8 -0
  38. package/dist/es/actions/scope-tree.mjs +95 -0
  39. package/dist/es/{app-Cx1-6dn0.d.mts → app-vKXaAr6f.d.mts} +6 -6
  40. package/dist/es/commands/generate/actions/templates/app-management/app-config.js.template +22 -0
  41. package/dist/es/commands/generate/actions/templates/app-management/installation.js.template +1 -1
  42. package/dist/es/commands/generate/actions/templates/business-configuration/config.js.template +22 -0
  43. package/dist/es/commands/generate/actions/templates/business-configuration/scope-tree.js.template +18 -0
  44. package/dist/es/commands/index.mjs +82 -91
  45. package/dist/es/config/index.d.mts +32 -332
  46. package/dist/es/config/index.mjs +3 -2
  47. package/dist/es/error-CMV3IjBz.mjs +18 -0
  48. package/dist/es/{error-P7JgUTds.mjs → installation-SWIwhpKT.mjs} +72 -124
  49. package/dist/es/management/index.d.mts +2 -3
  50. package/dist/es/management/index.mjs +1 -1
  51. package/dist/es/parser-CKQyrTB7.mjs +201 -0
  52. package/dist/es/router-CJ4VWoCt.mjs +404 -0
  53. package/dist/es/{management-Y7pwEbNI.mjs → runner-DB2tDBQS.mjs} +17 -24
  54. package/dist/{cjs/index-C5SutkJQ.d.cts → es/runner-Uk7263hG.d.mts} +49 -95
  55. package/dist/es/schemas-B8yIv0_b.mjs +41 -0
  56. package/dist/es/validate-DXI6gwZ2.mjs +187 -0
  57. package/package.json +39 -25
  58. package/dist/cjs/actions/index.cjs +0 -824
  59. package/dist/cjs/commands/generate/actions/templates/app-management/get-app-config.js.template +0 -62
  60. package/dist/cjs/commands/generate/actions/templates/business-configuration/get-config-schema.js.template +0 -63
  61. package/dist/cjs/commands/generate/actions/templates/business-configuration/get-configuration.js.template +0 -104
  62. package/dist/cjs/commands/generate/actions/templates/business-configuration/get-scope-tree.js.template +0 -69
  63. package/dist/cjs/commands/generate/actions/templates/business-configuration/set-configuration.js.template +0 -125
  64. package/dist/cjs/commands/generate/actions/templates/business-configuration/set-custom-scope-tree.js.template +0 -83
  65. package/dist/cjs/commands/generate/actions/templates/business-configuration/sync-commerce-scopes.js.template +0 -113
  66. package/dist/cjs/commands/generate/actions/templates/business-configuration/unsync-commerce-scopes.js.template +0 -56
  67. package/dist/cjs/config-JQ_n-5Nk.cjs +0 -565
  68. package/dist/cjs/error-Byj1DVHZ.cjs +0 -344
  69. package/dist/es/commands/generate/actions/templates/app-management/get-app-config.js.template +0 -62
  70. package/dist/es/commands/generate/actions/templates/business-configuration/get-config-schema.js.template +0 -63
  71. package/dist/es/commands/generate/actions/templates/business-configuration/get-configuration.js.template +0 -104
  72. package/dist/es/commands/generate/actions/templates/business-configuration/get-scope-tree.js.template +0 -69
  73. package/dist/es/commands/generate/actions/templates/business-configuration/set-configuration.js.template +0 -125
  74. package/dist/es/commands/generate/actions/templates/business-configuration/set-custom-scope-tree.js.template +0 -83
  75. package/dist/es/commands/generate/actions/templates/business-configuration/sync-commerce-scopes.js.template +0 -113
  76. package/dist/es/commands/generate/actions/templates/business-configuration/unsync-commerce-scopes.js.template +0 -56
  77. package/dist/es/config-BSGerqCG.mjs +0 -457
  78. /package/dist/es/{logging-VgerMhp6.mjs → logging-CzmXDzxI.mjs} +0 -0
@@ -1,824 +0,0 @@
1
- const require_error = require('../error-Byj1DVHZ.cjs');
2
- const require_management = require('../management-Dm5h0E6l.cjs');
3
- let _adobe_aio_commerce_lib_core_responses = require("@adobe/aio-commerce-lib-core/responses");
4
- let _adobe_aio_lib_core_logging = require("@adobe/aio-lib-core-logging");
5
- _adobe_aio_lib_core_logging = require_error.__toESM(_adobe_aio_lib_core_logging);
6
- let regexparam = require("regexparam");
7
- let _adobe_aio_lib_files = require("@adobe/aio-lib-files");
8
- let _adobe_aio_lib_state = require("@adobe/aio-lib-state");
9
- let openwhisk = require("openwhisk");
10
- openwhisk = require_error.__toESM(openwhisk);
11
- let valibot = require("valibot");
12
- valibot = require_error.__toESM(valibot);
13
-
14
- //#region ../../packages-private/common-utils/source/actions/http/middleware/logger.ts
15
- /**
16
- * Creates a logger middleware that adds logging capabilities to the context.
17
- *
18
- * @example
19
- * ```typescript
20
- * router.use(logger({ level: "debug", name: () => "my-logger-name" }));
21
- *
22
- * router.get("/test", {
23
- * handler: (req, ctx) => {
24
- * ctx.logger.info("Hello world");
25
- * return ok({ body: {} });
26
- * },
27
- * });
28
- * ```
29
- */
30
- function logger({ name, ...restOptions } = {}) {
31
- return (ctx) => {
32
- const params = ctx.rawParams;
33
- return { logger: (0, _adobe_aio_lib_core_logging.default)(`${params.__ow_method}-${name?.(ctx) ?? process.env.__OW_ACTION_NAME}`, {
34
- level: `${params.LOG_LEVEL ?? "info"}`,
35
- ...restOptions
36
- }) };
37
- };
38
- }
39
-
40
- //#endregion
41
- //#region ../../packages-private/common-utils/source/actions/http/utils.ts
42
- /**
43
- * Validates input against a Standard Schema and returns a result.
44
- *
45
- * @template TInput - The input type expected by the schema
46
- * @template TOutput - The output type produced by the schema
47
- * @param schema - A Standard Schema v1 compliant schema
48
- * @param input - The input data to validate
49
- * @returns A promise resolving to either success with validated data or failure with issues
50
- *
51
- * @example
52
- * ```typescript
53
- * const result = await validateSchema(mySchema, userInput);
54
- * if (result.success) {
55
- * console.log(result.data); // Typed as TOutput
56
- * } else {
57
- * console.error(result.issues); // Validation errors
58
- * }
59
- * ```
60
- */
61
- async function validateSchema(schema, input) {
62
- const result = await schema["~standard"].validate(input);
63
- if (result.issues) return {
64
- success: false,
65
- issues: result.issues.map((issue) => ({
66
- message: issue.message,
67
- path: issue.path?.map((segment) => typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment)
68
- }))
69
- };
70
- return {
71
- success: true,
72
- data: result.value
73
- };
74
- }
75
- /**
76
- * Parses a request body from OpenWhisk/Runtime.
77
- * Handles multiple formats:
78
- * - Base64-encoded strings (__ow_body)
79
- * - Already-parsed objects
80
- * - Body properties mixed into args (web actions with JSON content-type)
81
- *
82
- * @param owBody - Body from __ow_body (base64 string, JSON string, or object)
83
- * @param args - Full args object to extract body from if __ow_body is not present
84
- *
85
- * @example
86
- * ```typescript
87
- * const body = parseRequestBody(params.__ow_body, params);
88
- * ```
89
- */
90
- function parseRequestBody(owBody, args) {
91
- if (owBody) {
92
- if (typeof owBody === "object") return owBody;
93
- if (typeof owBody === "string") {
94
- try {
95
- return JSON.parse(owBody);
96
- } catch {}
97
- try {
98
- const decoded = Buffer.from(owBody, "base64").toString();
99
- return JSON.parse(decoded);
100
- } catch {}
101
- }
102
- }
103
- if (args && typeof args === "object") {
104
- const body = {};
105
- for (const [key, value] of Object.entries(args)) if (!key.startsWith("__ow_")) body[key] = value;
106
- return body;
107
- }
108
- return {};
109
- }
110
- /**
111
- * Parses query parameters from OpenWhisk/Runtime format.
112
- *
113
- * @param queryString - Query string from __ow_query
114
- * @param fallbackParams - Fallback params object (used when __ow_query is not present)
115
- * @returns Parsed query parameters as a record
116
- *
117
- * @example
118
- * ```typescript
119
- * const query = parseQueryParams(params.__ow_query, params);
120
- * ```
121
- */
122
- function parseQueryParams(queryString, fallbackParams) {
123
- if (queryString) return Object.fromEntries(new URLSearchParams(queryString));
124
- if (fallbackParams) {
125
- const { __ow_method, __ow_path, __ow_headers, __ow_body, __ow_query, ...rest } = fallbackParams;
126
- return rest;
127
- }
128
- return {};
129
- }
130
-
131
- //#endregion
132
- //#region ../../packages-private/common-utils/source/actions/http/router.ts
133
- /**
134
- * HTTP router for Adobe I/O Runtime actions.
135
- * Provides type-safe routing with schema validation and OpenWhisk integration.
136
- *
137
- * @example
138
- * ```typescript
139
- * const router = new HttpActionRouter();
140
- *
141
- * router.get("/users/:id", {
142
- * handler: (req) => ok({ id: req.params.id, context: req.context })
143
- * });
144
- *
145
- * // Add context builders
146
- * router.use(async (base) => ({
147
- * user: await getUser(base.rawParams.__ow_headers?.authorization),
148
- * }));
149
- *
150
- * export const main = router.handler();
151
- * ```
152
- */
153
- var HttpActionRouter = class {
154
- constructor() {
155
- this.routes = [];
156
- this.contextBuilders = [];
157
- }
158
- /**
159
- * Internal method to add a route to the router.
160
- */
161
- addRoute(method, path, config) {
162
- const { pattern, keys } = (0, regexparam.parse)(path);
163
- this.routes.push({
164
- method,
165
- pattern,
166
- keys,
167
- params: config.params,
168
- body: config.body,
169
- query: config.query,
170
- handler: config.handler
171
- });
172
- return this;
173
- }
174
- /**
175
- * Register a GET route.
176
- *
177
- * @example
178
- * ```typescript
179
- * router.get("/users/:id", {
180
- * handler: (req) => ok({ id: req.params.id })
181
- * });
182
- * ```
183
- */
184
- get(path, config) {
185
- return this.addRoute("GET", path, config);
186
- }
187
- /**
188
- * Register a POST route.
189
- *
190
- * @example
191
- * ```typescript
192
- * router.post("/users", {
193
- * body: userSchema,
194
- * handler: (req) => created(req.body)
195
- * });
196
- * ```
197
- */
198
- post(path, config) {
199
- return this.addRoute("POST", path, config);
200
- }
201
- /**
202
- * Register a PUT route.
203
- *
204
- * @example
205
- * ```typescript
206
- * router.put("/users/:id", {
207
- * body: userSchema,
208
- * handler: (req) => ok(req.body)
209
- * });
210
- * ```
211
- */
212
- put(path, config) {
213
- return this.addRoute("PUT", path, config);
214
- }
215
- /**
216
- * Register a PATCH route.
217
- *
218
- * @example
219
- * ```typescript
220
- * router.patch("/users/:id", {
221
- * body: partialUserSchema,
222
- * handler: (req) => ok(req.body)
223
- * });
224
- * ```
225
- */
226
- patch(path, config) {
227
- return this.addRoute("PATCH", path, config);
228
- }
229
- /**
230
- * Register a DELETE route.
231
- *
232
- * @example
233
- * ```typescript
234
- * router.delete("/users/:id", {
235
- * handler: (req) => noContent()
236
- * });
237
- * ```
238
- */
239
- delete(path, config) {
240
- return this.addRoute("DELETE", path, config);
241
- }
242
- /**
243
- * Register a context builder that runs before route handlers.
244
- * Context builders can add properties to the request context.
245
- * Multiple builders are executed in order and their results are merged.
246
- *
247
- * The returned router has an updated context type that includes the new properties,
248
- * enabling type-safe access in route handlers.
249
- *
250
- * @param builder - Function that receives base context and returns additional context
251
- * @returns The router instance with updated context type for chaining
252
- *
253
- * @example
254
- * ```typescript
255
- * const router = new HttpActionRouter()
256
- * .use(logger()) // HttpActionRouter<BaseContext & { logger: Logger }>
257
- * .use(auth()); // HttpActionRouter<BaseContext & { logger: Logger } & { user: User }>
258
- *
259
- * router.get("/me", {
260
- * handler: (req, ctx) => {
261
- * ctx.logger.info("Hello"); // ✅ typed
262
- * return ok({ body: ctx.user }); // ✅ typed
263
- * },
264
- * });
265
- * ```
266
- */
267
- use(builder) {
268
- this.contextBuilders.push(builder);
269
- return this;
270
- }
271
- /**
272
- * Builds the full context by running all context builders.
273
- */
274
- async buildContext(args) {
275
- let context = { rawParams: args };
276
- for (const builder of this.contextBuilders) {
277
- const result = await builder(context);
278
- if (result) context = {
279
- ...context,
280
- ...result
281
- };
282
- }
283
- return context;
284
- }
285
- /**
286
- * Validates and extracts route parameters.
287
- */
288
- async validateParams(route, params) {
289
- if (!route.params) return {
290
- success: true,
291
- data: params
292
- };
293
- const result = await validateSchema(route.params, params);
294
- if (!result.success) return {
295
- success: false,
296
- issues: result.issues
297
- };
298
- return {
299
- success: true,
300
- data: result.data
301
- };
302
- }
303
- /**
304
- * Validates request body.
305
- */
306
- async validateBody(route, body) {
307
- if (!route.body) return {
308
- success: true,
309
- data: body
310
- };
311
- const result = await validateSchema(route.body, body);
312
- if (!result.success) return {
313
- success: false,
314
- issues: result.issues
315
- };
316
- return {
317
- success: true,
318
- data: result.data
319
- };
320
- }
321
- /**
322
- * Validates query parameters.
323
- */
324
- async validateQuery(route, query) {
325
- if (!route.query) return {
326
- success: true,
327
- data: query
328
- };
329
- const result = await validateSchema(route.query, query);
330
- if (!result.success) return {
331
- success: false,
332
- issues: result.issues
333
- };
334
- return {
335
- success: true,
336
- data: result.data
337
- };
338
- }
339
- /** Handles a matched route by validating inputs and calling the handler. */
340
- async handleRoute(route, match, body, query, headers, method, path, context) {
341
- const params = {};
342
- route.keys.forEach((key, i) => {
343
- params[key] = decodeURIComponent(match[i + 1] || "");
344
- });
345
- const paramsResult = await this.validateParams(route, params);
346
- if (!paramsResult.success) return (0, _adobe_aio_commerce_lib_core_responses.badRequest)({ body: {
347
- message: "Invalid route parameters",
348
- issues: paramsResult.issues
349
- } });
350
- const bodyResult = await this.validateBody(route, body);
351
- if (!bodyResult.success) return (0, _adobe_aio_commerce_lib_core_responses.badRequest)({ body: {
352
- message: "Invalid request body",
353
- issues: bodyResult.issues
354
- } });
355
- const queryResult = await this.validateQuery(route, query);
356
- if (!queryResult.success) return (0, _adobe_aio_commerce_lib_core_responses.badRequest)({ body: {
357
- message: "Invalid query parameters",
358
- issues: queryResult.issues
359
- } });
360
- try {
361
- return await route.handler({
362
- params: paramsResult.data,
363
- body: bodyResult.data,
364
- query: queryResult.data,
365
- headers,
366
- method,
367
- path
368
- }, context);
369
- } catch (err) {
370
- console.error("Handler error:", err);
371
- return (0, _adobe_aio_commerce_lib_core_responses.internalServerError)({ body: {
372
- message: "Internal server error",
373
- error: err instanceof Error ? err.message : "Unknown error"
374
- } });
375
- }
376
- }
377
- /**
378
- * Creates an OpenWhisk/Runtime action handler from the registered routes.
379
- *
380
- * @example
381
- * ```typescript
382
- * const router = new HttpActionRouter();
383
- * router.get("/hello", { handler: () => ok({ message: "Hello!" }) });
384
- *
385
- * export const main = router.handler();
386
- * ```
387
- */
388
- handler() {
389
- return async (args) => {
390
- const method = (args.__ow_method ?? "get").toUpperCase();
391
- const rawPath = args.__ow_path ?? "/";
392
- const path = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
393
- const headers = args.__ow_headers ?? {};
394
- const body = parseRequestBody(args.__ow_body, args);
395
- const query = parseQueryParams(args.__ow_query, args);
396
- const context = await this.buildContext(args);
397
- const matchedMethods = [];
398
- for (const route of this.routes) {
399
- const match = route.pattern.exec(path);
400
- if (!match) continue;
401
- matchedMethods.push(route.method);
402
- if (route.method !== method) continue;
403
- const response = await this.handleRoute(route, match, body, query, headers, method, path, context);
404
- if (response) return response;
405
- }
406
- if (matchedMethods.length > 0) return (0, _adobe_aio_commerce_lib_core_responses.methodNotAllowed)(`Method ${method} not allowed`);
407
- return (0, _adobe_aio_commerce_lib_core_responses.notFound)(`No route matches ${path}`);
408
- };
409
- }
410
- };
411
-
412
- //#endregion
413
- //#region ../../packages-private/common-utils/source/storage/files-store.ts
414
- /** Default directory prefix. */
415
- const DEFAULT_DIR_PREFIX = "store";
416
- /**
417
- * Creates a generic key-value store backed by @adobe/aio-lib-files.
418
- * Provides persistent storage that survives beyond TTL.
419
- *
420
- * @typeParam T - The type of data to store.
421
- * @param options - Configuration options for the store.
422
- * @returns A KeyValueStore implementation.
423
- *
424
- * @example
425
- * ```typescript
426
- * interface UserProfile {
427
- * id: string;
428
- * name: string;
429
- * email: string;
430
- * }
431
- *
432
- * const store = await createFilesStore<UserProfile>({
433
- * dirPrefix: "profiles",
434
- * });
435
- *
436
- * await store.put("user-123", { id: "123", name: "John", email: "john@example.com" });
437
- * const profile = await store.get("user-123");
438
- * ```
439
- */
440
- async function createFilesStore(options = {}) {
441
- const { dirPrefix = DEFAULT_DIR_PREFIX } = options;
442
- return new FilesStore(await (0, _adobe_aio_lib_files.init)(), dirPrefix);
443
- }
444
- /**
445
- * Key-value store implementation using @adobe/aio-lib-files.
446
- */
447
- var FilesStore = class {
448
- constructor(files, dirPrefix) {
449
- this.files = files;
450
- this.dirPrefix = dirPrefix;
451
- }
452
- async get(key) {
453
- const filePath = this.buildFilePath(key);
454
- try {
455
- const content = await this.files.read(filePath);
456
- if (!content) return null;
457
- return JSON.parse(content.toString("utf8"));
458
- } catch {
459
- return null;
460
- }
461
- }
462
- async put(key, data) {
463
- const filePath = this.buildFilePath(key);
464
- await this.files.write(filePath, JSON.stringify(data));
465
- }
466
- async delete(key) {
467
- const filePath = this.buildFilePath(key);
468
- try {
469
- const result = await this.files.delete(filePath);
470
- return Array.isArray(result) && result.length > 0;
471
- } catch {
472
- return false;
473
- }
474
- }
475
- buildFilePath(key) {
476
- return `${this.dirPrefix}/${key}.json`;
477
- }
478
- };
479
-
480
- //#endregion
481
- //#region ../../packages-private/common-utils/source/storage/state-store.ts
482
- /** Default TTL for state entries (3 hours in seconds). */
483
- const DEFAULT_TTL_SECONDS = 10800;
484
- /** Default key prefix. */
485
- const DEFAULT_KEY_PREFIX = "store";
486
- /**
487
- * Creates a generic key-value store backed by @adobe/aio-lib-state.
488
- * Provides fast, TTL-based caching for temporary data.
489
- *
490
- * @typeParam T - The type of data to store.
491
- * @param options - Configuration options for the store.
492
- * @returns A KeyValueStore implementation.
493
- *
494
- * @example
495
- * ```typescript
496
- * interface UserSession {
497
- * userId: string;
498
- * token: string;
499
- * }
500
- *
501
- * const store = await createStateStore<UserSession>({
502
- * keyPrefix: "session",
503
- * ttlSeconds: 3600, // 1 hour
504
- * });
505
- *
506
- * await store.put("user-123", { userId: "123", token: "abc" });
507
- * const session = await store.get("user-123");
508
- * ```
509
- */
510
- async function createStateStore(options = {}) {
511
- const { keyPrefix = DEFAULT_KEY_PREFIX, ttlSeconds = DEFAULT_TTL_SECONDS } = options;
512
- return new StateStore(await (0, _adobe_aio_lib_state.init)(), keyPrefix, ttlSeconds);
513
- }
514
- /**
515
- * Key-value store implementation using @adobe/aio-lib-state.
516
- */
517
- var StateStore = class {
518
- constructor(state, keyPrefix, ttlSeconds) {
519
- this.state = state;
520
- this.keyPrefix = keyPrefix;
521
- this.ttlSeconds = ttlSeconds;
522
- }
523
- async get(key) {
524
- const fullKey = this.buildKey(key);
525
- const result = await this.state.get(fullKey);
526
- if (!result?.value) return null;
527
- try {
528
- return JSON.parse(result.value);
529
- } catch {
530
- return null;
531
- }
532
- }
533
- async put(key, data) {
534
- const fullKey = this.buildKey(key);
535
- const value = JSON.stringify(data);
536
- await this.state.put(fullKey, value, { ttl: this.ttlSeconds });
537
- }
538
- async delete(key) {
539
- const fullKey = this.buildKey(key);
540
- try {
541
- await this.state.delete(fullKey);
542
- return true;
543
- } catch {
544
- return false;
545
- }
546
- }
547
- buildKey(key) {
548
- return `${this.keyPrefix}-${key}`;
549
- }
550
- };
551
-
552
- //#endregion
553
- //#region ../../packages-private/common-utils/source/storage/combined-store.ts
554
- /** Default TTL for cache (10 minutes). */
555
- const DEFAULT_CACHE_TTL_SECONDS = 600;
556
- /**
557
- * Creates a combined key-value store that uses:
558
- * - lib-state for fast cache during active operations
559
- * - lib-files for persistent storage
560
- *
561
- * Read strategy: cache first, then persistent storage
562
- * Write strategy:
563
- * - Always write to cache for fast reads
564
- * - Write to persistent storage based on shouldPersist predicate
565
- *
566
- * @typeParam T - The type of data to store.
567
- * @param options - Configuration options for the stores.
568
- *
569
- * @example
570
- * ```typescript
571
- * interface Task {
572
- * id: string;
573
- * status: "pending" | "completed";
574
- * result?: unknown;
575
- * }
576
- *
577
- * const store = await createCombinedStore<Task>({
578
- * cache: { keyPrefix: "task", ttlSeconds: 600 },
579
- * persistent: {
580
- * dirPrefix: "tasks",
581
- * shouldPersist: (task) => task.status === "completed",
582
- * },
583
- * });
584
- *
585
- * // During processing: writes to cache only
586
- * await store.put("task-1", { id: "1", status: "pending" });
587
- *
588
- * // On completion (as shouldPersist indicates): writes to both cache and persistent
589
- * await store.put("task-1", { id: "1", status: "completed", result: {} });
590
- * ```
591
- */
592
- async function createCombinedStore(options = {}) {
593
- const { cache = {}, persistent = {} } = options;
594
- const { shouldPersist, ...filesOptions } = persistent;
595
- const cacheOptions = {
596
- ttlSeconds: DEFAULT_CACHE_TTL_SECONDS,
597
- ...cache
598
- };
599
- const [cacheStore, persistentStore] = await Promise.all([createStateStore(cacheOptions), createFilesStore(filesOptions)]);
600
- return new CombinedStore(cacheStore, persistentStore, shouldPersist);
601
- }
602
- /**
603
- * Combined key-value store implementation.
604
- * Uses lib-state for cache and lib-files for persistence.
605
- */
606
- var CombinedStore = class {
607
- constructor(cache, persistent, shouldPersist) {
608
- this.cache = cache;
609
- this.persistent = persistent;
610
- this.shouldPersist = shouldPersist;
611
- }
612
- async get(key) {
613
- const cached = await this.cache.get(key);
614
- if (cached !== null && cached !== void 0) return cached;
615
- const persisted = await this.persistent.get(key);
616
- if (persisted !== null && persisted !== void 0) {
617
- await this.cache.put(key, persisted).catch(() => {});
618
- return persisted;
619
- }
620
- return null;
621
- }
622
- async put(key, data) {
623
- await this.cache.put(key, data);
624
- if (!this.shouldPersist || this.shouldPersist(data)) await this.persistent.put(key, data);
625
- }
626
- async delete(key) {
627
- const [cacheDeleted, persistentDeleted] = await Promise.all([this.cache.delete?.(key) ?? Promise.resolve(false), this.persistent.delete?.(key) ?? Promise.resolve(false)]);
628
- return cacheDeleted || persistentDeleted;
629
- }
630
- };
631
-
632
- //#endregion
633
- //#region source/management/installation/schema.ts
634
- /** Schema for validating Adobe I/O app credentials required for installation. */
635
- const AppDataSchema = valibot.object({
636
- consumerOrgId: require_error.nonEmptyStringValueSchema("consumerOrgId"),
637
- orgName: require_error.nonEmptyStringValueSchema("orgName"),
638
- projectId: require_error.nonEmptyStringValueSchema("projectId"),
639
- projectName: require_error.nonEmptyStringValueSchema("projectName"),
640
- projectTitle: require_error.nonEmptyStringValueSchema("projectTitle"),
641
- workspaceId: require_error.nonEmptyStringValueSchema("workspaceId"),
642
- workspaceName: require_error.nonEmptyStringValueSchema("workspaceName"),
643
- workspaceTitle: require_error.nonEmptyStringValueSchema("workspaceTitle")
644
- });
645
-
646
- //#endregion
647
- //#region source/actions/installation.ts
648
- const DEFAULT_ACTION_NAME = "app-management/installation";
649
- /** Creates an installation state store using lib-core combined storage. */
650
- function createInstallationStore() {
651
- return createCombinedStore({
652
- cache: { keyPrefix: "installation" },
653
- persistent: {
654
- dirPrefix: "installation",
655
- shouldPersist: require_management.isCompletedState
656
- }
657
- });
658
- }
659
- /** Returns the storage key used to store the current installation ID. */
660
- function getStorageKey() {
661
- return "current";
662
- }
663
- /**
664
- * Creates hooks to sync installation state to storage.
665
- */
666
- function createInstallationHooks(store, logFn) {
667
- const logAndSave = async (message, data) => {
668
- logFn(message);
669
- await store.put(getStorageKey(), data);
670
- };
671
- return {
672
- onInstallationStart: (state) => logAndSave("Installation started", state),
673
- onInstallationFailure: (state) => logAndSave("Installation failed", state),
674
- onInstallationSuccess: (state) => logAndSave("Installation succeeded", state),
675
- onStepStart: (event, state) => logAndSave(`Step started: ${event.stepName}`, state),
676
- onStepSuccess: (event, state) => logAndSave(`Step succeeded: ${event.stepName}`, state),
677
- onStepFailure: (event, state) => logAndSave(`Step failed: ${event.stepName}`, state)
678
- };
679
- }
680
- /**
681
- * Installation action router.
682
- *
683
- * Routes:
684
- * - POST /installation - Start installation (creates plan, invokes execution async)
685
- * - GET /installation/execution - Get current execution status
686
- * - POST /installation/execution - Execute installation (internal, called async)
687
- */
688
- const router = new HttpActionRouter().use(logger({ name: () => "installation" }));
689
- /**
690
- * GET /installation/execution - Get current execution status
691
- *
692
- * Flow:
693
- * 1. Find execution in state store
694
- * 2. If found: return execution plan with step statuses
695
- * 3. If not found: return empty status
696
- */
697
- router.get("/", { handler: async (_req, { logger: logger$1 }) => {
698
- logger$1.debug("Getting installation execution status...");
699
- const state = await (await createInstallationStore()).get(getStorageKey());
700
- if (state) {
701
- logger$1.debug(`Found execution: ${state.status}`);
702
- return (0, _adobe_aio_commerce_lib_core_responses.ok)({ body: state });
703
- }
704
- logger$1.debug("No execution found");
705
- return (0, _adobe_aio_commerce_lib_core_responses.noContent)();
706
- } });
707
- /**
708
- * POST / - Start installation
709
- *
710
- * Flow:
711
- * 1. Find execution in state store
712
- * 2. If found and (pending/in-progress or succeeded): return 409 Conflict
713
- * 3. If not found or failed: create plan, invoke execution async, return 202 Accepted
714
- */
715
- router.post("/", {
716
- body: (0, valibot.object)({
717
- appData: AppDataSchema,
718
- commerceBaseUrl: (0, valibot.string)(),
719
- commerceEnv: (0, valibot.string)(),
720
- ioEventsUrl: (0, valibot.string)(),
721
- ioEventsEnv: (0, valibot.string)()
722
- }),
723
- handler: async (req, { logger: logger$1, rawParams }) => {
724
- logger$1.debug("Starting installation...");
725
- const store = await createInstallationStore();
726
- const existingState = await store.get(getStorageKey());
727
- if (existingState) {
728
- if (require_management.isInProgressState(existingState)) {
729
- logger$1.debug(`Installation already in progress: ${existingState.status}`);
730
- return (0, _adobe_aio_commerce_lib_core_responses.conflict)(`Installation is already ${existingState.status}. Wait for it to complete.`);
731
- }
732
- if (require_management.isSucceededState(existingState)) {
733
- logger$1.debug("Installation already succeeded");
734
- return (0, _adobe_aio_commerce_lib_core_responses.conflict)("Installation has already completed successfully.");
735
- }
736
- logger$1.debug("Previous installation failed, allowing retry");
737
- }
738
- const appConfig = rawParams.appConfig;
739
- if (!appConfig) return (0, _adobe_aio_commerce_lib_core_responses.internalServerError)("Could not find or parse the app.commerce.manifest.json file, is it present and valid?");
740
- const initialState = require_management.createInitialInstallationState({ config: appConfig });
741
- logger$1.debug(`Created initial state: ${initialState.id}`);
742
- await store.put(getStorageKey(), initialState);
743
- const activation = await (0, openwhisk.default)().actions.invoke({
744
- name: DEFAULT_ACTION_NAME,
745
- blocking: false,
746
- result: false,
747
- params: {
748
- ...rawParams,
749
- appData: req.body.appData,
750
- AIO_EVENTS_API_BASE_URL: req.body.ioEventsUrl,
751
- AIO_COMMERCE_AUTH_IMS_ENVIRONMENT: req.body.ioEventsEnv,
752
- AIO_COMMERCE_API_BASE_URL: req.body.commerceBaseUrl,
753
- AIO_COMMERCE_API_FLAVOR: req.body.commerceEnv,
754
- initialState,
755
- appConfig,
756
- __ow_path: "/execution",
757
- __ow_method: "post"
758
- }
759
- });
760
- logger$1.debug(`Async execution started: ${activation.activationId}`);
761
- return (0, _adobe_aio_commerce_lib_core_responses.accepted)({ body: {
762
- message: "Installation started",
763
- activationId: activation.activationId,
764
- ...initialState
765
- } });
766
- }
767
- });
768
- /**
769
- * POST /installation/execution - Execute installation (internal)
770
- *
771
- * This endpoint is called asynchronously by POST /installation.
772
- * It runs the actual installation workflow and saves state.
773
- */
774
- router.post("/execution", { handler: async (_req, { logger: logger$1, rawParams }) => {
775
- const { appData, ...params } = rawParams;
776
- const { initialState, appConfig } = params;
777
- if (!initialState) return (0, _adobe_aio_commerce_lib_core_responses.badRequest)("initialState is required for execution");
778
- if (!appConfig) return (0, _adobe_aio_commerce_lib_core_responses.badRequest)("appConfig is required for execution");
779
- const store = await createInstallationStore();
780
- const hooks = createInstallationHooks(store, (msg) => logger$1.debug(msg));
781
- const installationContext = {
782
- appData,
783
- params,
784
- logger: logger$1,
785
- customScripts: params.customScriptsLoader?.(appConfig, logger$1) || {}
786
- };
787
- logger$1.debug(`Executing installation: ${initialState.id}`);
788
- const result = await require_management.runInstallation({
789
- installationContext,
790
- config: appConfig,
791
- initialState,
792
- hooks
793
- });
794
- await store.put(getStorageKey(), result);
795
- logger$1.debug(`Installation completed: ${result.status}`);
796
- if (require_management.isFailedState(result)) return (0, _adobe_aio_commerce_lib_core_responses.internalServerError)({ body: {
797
- message: "Installation failed",
798
- error: result.error,
799
- state: result
800
- } });
801
- return (0, _adobe_aio_commerce_lib_core_responses.ok)({ body: result });
802
- } });
803
- /**
804
- * DELETE / - Clear installation state
805
- *
806
- * This endpoint allows clearing the installation state.
807
- */
808
- router.delete("/", { handler: async (_req, { logger: logger$1 }) => {
809
- logger$1.debug("Clearing installation state...");
810
- await (await createInstallationStore()).delete(getStorageKey());
811
- logger$1.debug("Installation state cleared");
812
- return (0, _adobe_aio_commerce_lib_core_responses.noContent)();
813
- } });
814
- /** The route handler for the runtime action. */
815
- const installationRuntimeAction = ({ appConfig, customScriptsLoader }) => async (params) => {
816
- return await router.handler()({
817
- ...params,
818
- appConfig,
819
- customScriptsLoader
820
- });
821
- };
822
-
823
- //#endregion
824
- exports.installationRuntimeAction = installationRuntimeAction;