@ductape/sdk 0.1.20 → 0.1.21

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 (79) hide show
  1. package/dist/agents/agents.service.d.ts +2 -0
  2. package/dist/agents/agents.service.js +26 -3
  3. package/dist/agents/agents.service.js.map +1 -1
  4. package/dist/api/services/logsApi.service.js +5 -1
  5. package/dist/api/services/logsApi.service.js.map +1 -1
  6. package/dist/api/services/productsApi.service.d.ts +111 -0
  7. package/dist/api/services/productsApi.service.js +90 -0
  8. package/dist/api/services/productsApi.service.js.map +1 -1
  9. package/dist/api/urls.d.ts +7 -0
  10. package/dist/api/urls.js +14 -7
  11. package/dist/api/urls.js.map +1 -1
  12. package/dist/apps/services/app.service.js +7 -2
  13. package/dist/apps/services/app.service.js.map +1 -1
  14. package/dist/brokers/brokers.service.js +20 -12
  15. package/dist/brokers/brokers.service.js.map +1 -1
  16. package/dist/database/adapters/mysql.adapter.js +8 -3
  17. package/dist/database/adapters/mysql.adapter.js.map +1 -1
  18. package/dist/database/adapters/postgresql.adapter.js +6 -2
  19. package/dist/database/adapters/postgresql.adapter.js.map +1 -1
  20. package/dist/database/databases.service.d.ts +10 -1
  21. package/dist/database/databases.service.js +84 -27
  22. package/dist/database/databases.service.js.map +1 -1
  23. package/dist/graph/graphs.service.d.ts +7 -3
  24. package/dist/graph/graphs.service.js +67 -31
  25. package/dist/graph/graphs.service.js.map +1 -1
  26. package/dist/index.d.ts +9 -2
  27. package/dist/index.js +32 -93
  28. package/dist/index.js.map +1 -1
  29. package/dist/logs/logs.service.d.ts +3 -1
  30. package/dist/logs/logs.service.js +12 -2
  31. package/dist/logs/logs.service.js.map +1 -1
  32. package/dist/logs/logs.types.d.ts +4 -0
  33. package/dist/logs/logs.types.js.map +1 -1
  34. package/dist/notifications/notifications.service.d.ts +6 -0
  35. package/dist/notifications/notifications.service.js +54 -17
  36. package/dist/notifications/notifications.service.js.map +1 -1
  37. package/dist/processor/services/processor.service.d.ts +0 -14
  38. package/dist/processor/services/processor.service.js +6 -72
  39. package/dist/processor/services/processor.service.js.map +1 -1
  40. package/dist/products/bootstrap-cache.d.ts +22 -0
  41. package/dist/products/bootstrap-cache.js +78 -0
  42. package/dist/products/bootstrap-cache.js.map +1 -0
  43. package/dist/products/services/products.service.d.ts +46 -3
  44. package/dist/products/services/products.service.js +304 -125
  45. package/dist/products/services/products.service.js.map +1 -1
  46. package/dist/products/validators/joi-validators/create.productFallback.validator.js +1 -1
  47. package/dist/products/validators/joi-validators/create.productGraph.validator.js +7 -0
  48. package/dist/products/validators/joi-validators/create.productGraph.validator.js.map +1 -1
  49. package/dist/products/validators/joi-validators/create.productQuota.validator.js +1 -1
  50. package/dist/products/validators/joi-validators/create.productVector.validator.js +6 -0
  51. package/dist/products/validators/joi-validators/create.productVector.validator.js.map +1 -1
  52. package/dist/products/validators/joi-validators/update.productFallback.validator.js +1 -1
  53. package/dist/products/validators/joi-validators/update.productGraph.validator.js +9 -0
  54. package/dist/products/validators/joi-validators/update.productGraph.validator.js.map +1 -1
  55. package/dist/products/validators/joi-validators/update.productQuota.validator.js +1 -1
  56. package/dist/resilience/fallback.service.d.ts +3 -0
  57. package/dist/resilience/fallback.service.js +49 -14
  58. package/dist/resilience/fallback.service.js.map +1 -1
  59. package/dist/resilience/healthcheck.service.d.ts +3 -0
  60. package/dist/resilience/healthcheck.service.js +57 -7
  61. package/dist/resilience/healthcheck.service.js.map +1 -1
  62. package/dist/resilience/quota.service.d.ts +3 -0
  63. package/dist/resilience/quota.service.js +49 -14
  64. package/dist/resilience/quota.service.js.map +1 -1
  65. package/dist/resilience/resilience-execution.utils.d.ts +22 -0
  66. package/dist/resilience/resilience-execution.utils.js +81 -0
  67. package/dist/resilience/resilience-execution.utils.js.map +1 -0
  68. package/dist/sessions/sessions.service.d.ts +9 -1
  69. package/dist/sessions/sessions.service.js +125 -49
  70. package/dist/sessions/sessions.service.js.map +1 -1
  71. package/dist/types/index.types.d.ts +8 -0
  72. package/dist/types/index.types.js.map +1 -1
  73. package/dist/types/processor.types.d.ts +3 -0
  74. package/dist/types/processor.types.js.map +1 -1
  75. package/dist/types/requests.types.d.ts +4 -0
  76. package/dist/vector/vector-database.service.d.ts +6 -0
  77. package/dist/vector/vector-database.service.js +66 -8
  78. package/dist/vector/vector-database.service.js.map +1 -1
  79. package/package.json +3 -1
@@ -32,6 +32,7 @@ const productsBuilder_types_1 = require("../../types/productsBuilder.types");
32
32
  const validators_1 = require("../validators");
33
33
  const objects_utils_1 = require("../utils/objects.utils");
34
34
  const processor_utils_1 = require("../../processor/utils/processor.utils");
35
+ const bootstrap_cache_1 = require("../bootstrap-cache");
35
36
  const create_productDatabaseAction_validator_1 = require("../validators/joi-validators/create.productDatabaseAction.validator");
36
37
  const update_productDatabaseAction_validator_1 = require("../validators/joi-validators/update.productDatabaseAction.validator");
37
38
  const create_productStorage_validator_1 = require("../validators/joi-validators/create.productStorage.validator");
@@ -82,6 +83,34 @@ class ProductsBuilderService {
82
83
  if (queues) {
83
84
  this.queues = queues;
84
85
  }
86
+ this.bootstrapCache = new bootstrap_cache_1.BootstrapCache(this.workspace_id, this.redisClient, this.workspace_private_key);
87
+ }
88
+ applyBootstrapProductContext(productTag, boot) {
89
+ var _a, _b;
90
+ if (boot.product_id) {
91
+ this.product_id = boot.product_id;
92
+ }
93
+ const hasPrivateKey = boot.private_key != null && String(boot.private_key).trim() !== '';
94
+ if (hasPrivateKey) {
95
+ this.product = {
96
+ _id: boot.product_id,
97
+ workspace_id: boot.workspace_id,
98
+ private_key: boot.private_key,
99
+ tag: productTag !== null && productTag !== void 0 ? productTag : ((_b = (_a = this.product) === null || _a === void 0 ? void 0 : _a.tag) !== null && _b !== void 0 ? _b : ''),
100
+ };
101
+ }
102
+ }
103
+ async cachedBootstrap(domain, keyParts, fetch, apply) {
104
+ const key = (0, bootstrap_cache_1.buildBootstrapCacheKey)(domain, this.workspace_id, keyParts);
105
+ const cached = await this.bootstrapCache.get(key);
106
+ if (cached) {
107
+ apply === null || apply === void 0 ? void 0 : apply(cached);
108
+ return cached;
109
+ }
110
+ const result = await fetch();
111
+ apply === null || apply === void 0 ? void 0 : apply(result);
112
+ this.bootstrapCache.set(key, result);
113
+ return result;
85
114
  }
86
115
  fetchPrivateKey() {
87
116
  var _a;
@@ -236,6 +265,32 @@ class ProductsBuilderService {
236
265
  option.check_interval = 10000;
237
266
  }
238
267
  }
268
+ /** Validate a quota/fallback action provider (app action or product workflow). */
269
+ async validateQuotaFallbackProvider(option) {
270
+ var _a;
271
+ if (option.type !== productsBuilder_types_1.FeatureEventTypes.ACTION)
272
+ return;
273
+ if ((_a = option.app) === null || _a === void 0 ? void 0 : _a.trim()) {
274
+ const app = await this.fetchThirdPartyAppByAccessTag(option.app);
275
+ if (!app) {
276
+ throw new Error(`App with access tag ${option.app} not found`);
277
+ }
278
+ const version = app.versions.find((v) => v.tag === app.version);
279
+ const action = version === null || version === void 0 ? void 0 : version.actions.find((a) => a.tag === option.event);
280
+ if (!action) {
281
+ throw new Error(`Cannot find action ${option.event} on app ${app.tag} version ${app.version}`);
282
+ }
283
+ }
284
+ else {
285
+ const workflow = await this.fetchWorkflow(option.event);
286
+ if (!workflow) {
287
+ throw new Error(`Workflow ${option.event} not found on product`);
288
+ }
289
+ }
290
+ if (option.healthcheck) {
291
+ await this.validateProviderHealthcheckTag(option);
292
+ }
293
+ }
239
294
  /** Keep in-memory product component arrays in sync after component CRUD (components API can lag). */
240
295
  mergeProductComponent(kind, item) {
241
296
  if (!this.product || !(item === null || item === void 0 ? void 0 : item.tag))
@@ -278,6 +333,27 @@ class ProductsBuilderService {
278
333
  event,
279
334
  envs, action: enums_1.RequestAction.CREATE, component: enums_1.ProductComponents.HEALTHCHECK }), this.getUserAccess());
280
335
  }
336
+ async updateHealthcheckFromSchema(schema) {
337
+ var _a, _b, _c;
338
+ const probe = schema.probe;
339
+ const probeInput = (probe === null || probe === void 0 ? void 0 : probe.input) != null ? probe.input : undefined;
340
+ const app = (_a = probe === null || probe === void 0 ? void 0 : probe.app) !== null && _a !== void 0 ? _a : schema.app;
341
+ const event = (_b = probe === null || probe === void 0 ? void 0 : probe.event) !== null && _b !== void 0 ? _b : schema.event;
342
+ if (probeInput != null && !((_c = this.product) === null || _c === void 0 ? void 0 : _c.private_key)) {
343
+ throw new Error('Healthcheck inputs must be encrypted with the product private key; product not initialized or missing private_key');
344
+ }
345
+ const envs = await Promise.all((schema.envs || []).map(async (env) => {
346
+ var _a;
347
+ const input = env.input != null ? env.input : probeInput;
348
+ const encrypted = input != null && ((_a = this.product) === null || _a === void 0 ? void 0 : _a.private_key)
349
+ ? (0, processor_utils_1.encrypt)(JSON.stringify(input), this.product.private_key)
350
+ : input;
351
+ return Object.assign(Object.assign({}, env), { input: encrypted });
352
+ }));
353
+ await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, schema), { app,
354
+ event,
355
+ envs, action: enums_1.RequestAction.UPDATE, component: enums_1.ProductComponents.HEALTHCHECK }), this.getUserAccess());
356
+ }
281
357
  async updateDataValidation(selector, update) {
282
358
  if (!selector.startsWith('$Data{') && !selector.startsWith('$Filter')) {
283
359
  throw new Error(`selector ${selector} is expected to start with $Data or $Filter`);
@@ -416,22 +492,9 @@ class ProductsBuilderService {
416
492
  * Returns only the minimal action data needed to execute
417
493
  */
418
494
  async bootstrapAction(params) {
495
+ var _a;
419
496
  try {
420
- const result = await this.productApi.bootstrapAction(params, this.getUserAccess());
421
- // Initialize minimal product data so fetchPrivateKey/fetchProductId/fetchWorkspaceId work
422
- if (result.product_id) {
423
- this.product_id = result.product_id;
424
- }
425
- const hasPrivateKey = result.private_key != null && String(result.private_key).trim() !== '';
426
- if (hasPrivateKey) {
427
- this.product = {
428
- _id: result.product_id,
429
- workspace_id: result.workspace_id,
430
- private_key: result.private_key,
431
- tag: params.product_tag,
432
- };
433
- }
434
- return result;
497
+ return await this.cachedBootstrap('action', [(_a = params.product_tag) !== null && _a !== void 0 ? _a : '', params.access_tag, params.env_slug, params.action_tag], () => this.productApi.bootstrapAction(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
435
498
  }
436
499
  catch (e) {
437
500
  throw e;
@@ -458,14 +521,17 @@ class ProductsBuilderService {
458
521
  }
459
522
  async bootstrapNotification(params) {
460
523
  try {
461
- const result = await this.productApi.bootstrapNotification(params, this.getUserAccess());
462
- if (result.product_id) {
463
- this.product_id = result.product_id;
464
- }
465
- if (result.notification && result.private_key) {
466
- result.notification = this.decryptNotificationChannelConfigs(result.notification, result.private_key);
467
- }
468
- return result;
524
+ return await this.cachedBootstrap('notification', [params.product_tag, params.env_slug, params.notification_tag, params.message_tag], async () => {
525
+ const result = await this.productApi.bootstrapNotification(params, this.getUserAccess());
526
+ if (result.notification && result.private_key) {
527
+ result.notification = this.decryptNotificationChannelConfigs(result.notification, result.private_key);
528
+ }
529
+ return result;
530
+ }, (result) => {
531
+ if (result.product_id) {
532
+ this.product_id = result.product_id;
533
+ }
534
+ });
469
535
  }
470
536
  catch (e) {
471
537
  throw e;
@@ -473,11 +539,68 @@ class ProductsBuilderService {
473
539
  }
474
540
  async bootstrapStorage(params) {
475
541
  try {
476
- const result = await this.productApi.bootstrapStorage(params, this.getUserAccess());
477
- if (result.product_id) {
478
- this.product_id = result.product_id;
479
- }
480
- return result;
542
+ return await this.cachedBootstrap('storage', [params.product_tag, params.env_slug, params.storage_tag], () => this.productApi.bootstrapStorage(params, this.getUserAccess()), (result) => {
543
+ if (result.product_id) {
544
+ this.product_id = result.product_id;
545
+ }
546
+ });
547
+ }
548
+ catch (e) {
549
+ throw e;
550
+ }
551
+ }
552
+ async bootstrapSession(params) {
553
+ try {
554
+ return await this.cachedBootstrap('session', [params.product_tag, params.env_slug, params.session_tag], () => this.productApi.bootstrapSession(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
555
+ }
556
+ catch (e) {
557
+ throw e;
558
+ }
559
+ }
560
+ async bootstrapDatabase(params) {
561
+ try {
562
+ return await this.cachedBootstrap('database', [params.product_tag, params.env_slug, params.database_tag], () => this.productApi.bootstrapDatabase(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
563
+ }
564
+ catch (e) {
565
+ throw e;
566
+ }
567
+ }
568
+ async bootstrapBroker(params) {
569
+ var _a;
570
+ try {
571
+ return await this.cachedBootstrap('broker', [params.product_tag, params.env_slug, params.broker_tag, (_a = params.topic_tag) !== null && _a !== void 0 ? _a : ''], () => this.productApi.bootstrapBroker(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
572
+ }
573
+ catch (e) {
574
+ throw e;
575
+ }
576
+ }
577
+ async bootstrapGraph(params) {
578
+ try {
579
+ return await this.cachedBootstrap('graph', [params.product_tag, params.env_slug, params.graph_tag], () => this.productApi.bootstrapGraph(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
580
+ }
581
+ catch (e) {
582
+ throw e;
583
+ }
584
+ }
585
+ async bootstrapVector(params) {
586
+ try {
587
+ return await this.cachedBootstrap('vector', [params.product_tag, params.env_slug, params.vector_tag], () => this.productApi.bootstrapVector(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
588
+ }
589
+ catch (e) {
590
+ throw e;
591
+ }
592
+ }
593
+ async bootstrapResilience(params) {
594
+ try {
595
+ return await this.cachedBootstrap('resilience', [params.product_tag, params.env_slug, params.component_type, params.component_tag], () => this.productApi.bootstrapResilience(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
596
+ }
597
+ catch (e) {
598
+ throw e;
599
+ }
600
+ }
601
+ async bootstrapIntelligence(params) {
602
+ try {
603
+ return await this.cachedBootstrap('intelligence', [params.product_tag, params.env_slug, params.agent_tag], () => this.productApi.bootstrapIntelligence(params, this.getUserAccess()), (result) => this.applyBootstrapProductContext(params.product_tag, result));
481
604
  }
482
605
  catch (e) {
483
606
  throw e;
@@ -489,19 +612,21 @@ class ProductsBuilderService {
489
612
  async bootstrapWorkflow(params) {
490
613
  try {
491
614
  const auth = this.getUserAccess();
492
- const result = await this.productApi.bootstrapWorkflow(Object.assign(Object.assign({}, params), { workspace_id: auth.workspace_id }), auth);
493
- if (result && typeof result === 'object') {
494
- for (const stepTag of Object.keys(result)) {
495
- const data = result[stepTag];
496
- if (data === null || data === void 0 ? void 0 : data.product_id) {
497
- this.product_id = data.product_id;
498
- }
499
- if ((data === null || data === void 0 ? void 0 : data.notification) && (data === null || data === void 0 ? void 0 : data.private_key)) {
500
- result[stepTag] = Object.assign(Object.assign({}, data), { notification: this.decryptNotificationChannelConfigs(data.notification, data.private_key) });
615
+ return await this.cachedBootstrap('workflow', [params.product_tag, params.env_slug, (0, bootstrap_cache_1.hashWorkflowSteps)(params.steps)], async () => {
616
+ const result = await this.productApi.bootstrapWorkflow(Object.assign(Object.assign({}, params), { workspace_id: auth.workspace_id }), auth);
617
+ if (result && typeof result === 'object') {
618
+ for (const stepTag of Object.keys(result)) {
619
+ const data = result[stepTag];
620
+ if (data === null || data === void 0 ? void 0 : data.product_id) {
621
+ this.product_id = data.product_id;
622
+ }
623
+ if ((data === null || data === void 0 ? void 0 : data.notification) && (data === null || data === void 0 ? void 0 : data.private_key)) {
624
+ result[stepTag] = Object.assign(Object.assign({}, data), { notification: this.decryptNotificationChannelConfigs(data.notification, data.private_key) });
625
+ }
501
626
  }
502
627
  }
503
- }
504
- return result !== null && result !== void 0 ? result : {};
628
+ return result !== null && result !== void 0 ? result : {};
629
+ });
505
630
  }
506
631
  catch (e) {
507
632
  throw e;
@@ -1070,20 +1195,7 @@ class ProductsBuilderService {
1070
1195
  data.total_init = 0;
1071
1196
  await Promise.all(data.options.map(async (d) => {
1072
1197
  data.total_quota += d.quota;
1073
- if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
1074
- const app = await this.fetchThirdPartyAppByAccessTag(d.app);
1075
- if (!app) {
1076
- throw new Error(`App with access tag ${d.app} not found`);
1077
- }
1078
- const version = app.versions.find((version) => version.tag === app.version);
1079
- const action = version.actions.find((action) => action.tag === d.event);
1080
- if (!action) {
1081
- throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1082
- }
1083
- if (d.healthcheck) {
1084
- await this.validateProviderHealthcheckTag(d);
1085
- }
1086
- }
1198
+ await this.validateQuotaFallbackProvider(d);
1087
1199
  d.last_checked = new Date();
1088
1200
  d.provider_status = productsBuilder_types_1.ProviderStatus.AVAILABLE;
1089
1201
  d.last_available = true;
@@ -1108,20 +1220,7 @@ class ProductsBuilderService {
1108
1220
  data.tag = tag;
1109
1221
  await Promise.all(data.options.map(async (d) => {
1110
1222
  data.total_quota += d.quota;
1111
- if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
1112
- const app = await this.fetchThirdPartyAppByAccessTag(d.app);
1113
- if (!app) {
1114
- throw new Error(`App with access tag ${d.app} not found`);
1115
- }
1116
- const version = app.versions.find((version) => version.tag === app.version);
1117
- const action = version.actions.find((action) => action.tag === d.event);
1118
- if (!action) {
1119
- throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1120
- }
1121
- if (d.healthcheck) {
1122
- await this.validateProviderHealthcheckTag(d);
1123
- }
1124
- }
1223
+ await this.validateQuotaFallbackProvider(d);
1125
1224
  d.last_checked = new Date();
1126
1225
  d.provider_status = productsBuilder_types_1.ProviderStatus.AVAILABLE;
1127
1226
  d.last_available = true;
@@ -1177,20 +1276,7 @@ class ProductsBuilderService {
1177
1276
  await validators_1.CreateProductFallbackSchema.validateAsync(data);
1178
1277
  await this.validateQuotaFallbackInputAndOutput(data, 'fallback');
1179
1278
  await Promise.all(data.options.map(async (d) => {
1180
- if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
1181
- const app = await this.fetchThirdPartyAppByAccessTag(d.app);
1182
- if (!app) {
1183
- throw new Error(`App with access tag ${d.app} not found`);
1184
- }
1185
- const version = app.versions.find((version) => version.tag === app.version);
1186
- const action = version.actions.find((action) => action.tag === d.event);
1187
- if (!action) {
1188
- throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1189
- }
1190
- if (d.healthcheck) {
1191
- await this.validateProviderHealthcheckTag(d);
1192
- }
1193
- }
1279
+ await this.validateQuotaFallbackProvider(d);
1194
1280
  d.last_checked = new Date();
1195
1281
  d.provider_status = productsBuilder_types_1.ProviderStatus.AVAILABLE;
1196
1282
  d.last_available = true;
@@ -1212,20 +1298,7 @@ class ProductsBuilderService {
1212
1298
  await this.validateQuotaFallbackInputAndOutput(data, 'fallback');
1213
1299
  data.tag = tag;
1214
1300
  await Promise.all(data.options.map(async (d) => {
1215
- if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
1216
- const app = await this.fetchThirdPartyAppByAccessTag(d.app);
1217
- if (!app) {
1218
- throw new Error(`App with access tag ${d.app} not found`);
1219
- }
1220
- const version = app.versions.find((version) => version.tag === app.version);
1221
- const action = version.actions.find((action) => action.tag === d.event);
1222
- if (!action) {
1223
- throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1224
- }
1225
- if (d.healthcheck) {
1226
- await this.validateProviderHealthcheckTag(d);
1227
- }
1228
- }
1301
+ await this.validateQuotaFallbackProvider(d);
1229
1302
  d.last_checked = new Date();
1230
1303
  d.provider_status = productsBuilder_types_1.ProviderStatus.AVAILABLE;
1231
1304
  d.last_available = true;
@@ -2403,9 +2476,13 @@ class ProductsBuilderService {
2403
2476
  }
2404
2477
  async validateAppData(appData, app) {
2405
2478
  // TODO:
2479
+ var _a;
2406
2480
  const { envs } = app;
2407
2481
  // const { envs: appEnvs, auths: appAuths } = appData
2408
- const version = appData.versions.find((data) => (data.latest = true));
2482
+ const version = (_a = appData.versions.find((data) => data.latest === true)) !== null && _a !== void 0 ? _a : appData.versions[0];
2483
+ if (!version) {
2484
+ throw new Error(`App ${appData.tag} has no versions`);
2485
+ }
2409
2486
  app.envs = this.validateAppEnvs(envs, version);
2410
2487
  // await this.validateAppAuth(auth, appData);
2411
2488
  return app;
@@ -2538,31 +2615,114 @@ class ProductsBuilderService {
2538
2615
  }
2539
2616
  return { data: updateData, values: updateValues };
2540
2617
  }
2541
- async createAppAccessTag(app_tag) {
2618
+ async createAppAccessTag(app_tag, known_app_id) {
2542
2619
  await this.initializeWorkspace();
2543
2620
  const { name: workspace_name } = this.workspace;
2544
- const app = await this.appApi.fetchAppByTag(app_tag, this.getUserAccess());
2545
- if (!app) {
2546
- throw new Error(`App ${app_tag} not found`);
2621
+ let app_id = known_app_id === null || known_app_id === void 0 ? void 0 : known_app_id.trim();
2622
+ if (!app_id) {
2623
+ const app = await this.appApi.fetchAppByTag(app_tag, this.getUserAccess());
2624
+ if (!app) {
2625
+ throw new Error(`App ${app_tag} not found`);
2626
+ }
2627
+ app_id = String(app._id);
2547
2628
  }
2548
- const { _id: app_id } = app;
2549
2629
  const accessTag = `${app_tag}:${(0, string_utils_2.tagify)(workspace_name)}`;
2550
- const { access, access_tag } = await this.appApi.createAppAccess(app_id, this.product_id, accessTag, this.getUserAccess());
2551
- return {
2552
- access_tag,
2553
- access,
2554
- };
2630
+ try {
2631
+ const existing = await this.appApi.fetchAccessByTag(accessTag, this.getUserAccess());
2632
+ if ((existing === null || existing === void 0 ? void 0 : existing.access_tag) && (await this.accessAppIsLive(existing))) {
2633
+ return {
2634
+ access_tag: existing.access_tag,
2635
+ access: existing.access,
2636
+ };
2637
+ }
2638
+ }
2639
+ catch (_a) {
2640
+ // no existing access row — create below
2641
+ }
2642
+ try {
2643
+ const { access, access_tag } = await this.appApi.createAppAccess(app_id, this.product_id, accessTag, this.getUserAccess());
2644
+ return {
2645
+ access_tag,
2646
+ access,
2647
+ };
2648
+ }
2649
+ catch (e) {
2650
+ try {
2651
+ const existing = await this.appApi.fetchAccessByTag(accessTag, this.getUserAccess());
2652
+ if ((existing === null || existing === void 0 ? void 0 : existing.access_tag) && (await this.accessAppIsLive(existing))) {
2653
+ return {
2654
+ access_tag: existing.access_tag,
2655
+ access: existing.access,
2656
+ };
2657
+ }
2658
+ }
2659
+ catch (_b) {
2660
+ // fall through to original error
2661
+ }
2662
+ throw e;
2663
+ }
2664
+ }
2665
+ async accessAppIsLive(access) {
2666
+ const appId = (access === null || access === void 0 ? void 0 : access.app_id) == null ? '' : String(access.app_id).trim();
2667
+ if (!appId)
2668
+ return false;
2669
+ try {
2670
+ const appData = await this.appApi.fetchApp(appId, this.getUserAccess());
2671
+ return appData != null;
2672
+ }
2673
+ catch (_a) {
2674
+ return false;
2675
+ }
2555
2676
  }
2556
2677
  async addApp(app, throwErrorIfExists = false) {
2678
+ var _a, _b, _c, _d, _e, _f;
2557
2679
  try {
2558
2680
  await validators_1.CreateProductAppSchema.validateAsync(app);
2559
2681
  // TODO: figure out a way to check if this has run before, halt if it has
2560
2682
  if (!await this.fetchApp(app.access_tag, throwErrorIfExists)) {
2561
- const _a = await this.appApi.fetchAccessByTag(app.access_tag, this.getUserAccess()), { version } = _a, access = __rest(_a, ["version"]);
2562
- if (!access) {
2563
- throw new Error(`Access to app ${app.access_tag} not found`);
2683
+ let access = null;
2684
+ try {
2685
+ const fetched = await this.appApi.fetchAccessByTag(app.access_tag, this.getUserAccess());
2686
+ access = fetched ? Object.assign({}, fetched) : null;
2687
+ }
2688
+ catch (_g) {
2689
+ access = null;
2690
+ }
2691
+ if (access === null || access === void 0 ? void 0 : access.app_id) {
2692
+ const liveApp = await this.appApi.fetchApp(access.app_id, this.getUserAccess());
2693
+ if (!liveApp) {
2694
+ access = null;
2695
+ }
2696
+ }
2697
+ if (!(access === null || access === void 0 ? void 0 : access.app_id)) {
2698
+ const wsSuffix = `:${(0, string_utils_2.tagify)(this.workspace.name)}`;
2699
+ const appTag = app.access_tag.endsWith(wsSuffix)
2700
+ ? app.access_tag.slice(0, -wsSuffix.length)
2701
+ : '';
2702
+ if (!appTag) {
2703
+ throw new Error(`Access to app ${app.access_tag} not found`);
2704
+ }
2705
+ const appRow = await this.appApi.fetchAppByTag(appTag, this.getUserAccess());
2706
+ if (!(appRow === null || appRow === void 0 ? void 0 : appRow._id)) {
2707
+ throw new Error(`App not found for tag ${appTag}`);
2708
+ }
2709
+ const created = await this.appApi.createAppAccess(String(appRow._id), this.product_id, app.access_tag, this.getUserAccess());
2710
+ const auth = this.getUserAccess();
2711
+ const latestVersion = (_f = (_c = (_b = (_a = appRow.versions) === null || _a === void 0 ? void 0 : _a.find((v) => v.latest)) === null || _b === void 0 ? void 0 : _b.tag) !== null && _c !== void 0 ? _c : (_e = (_d = appRow.versions) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.tag) !== null && _f !== void 0 ? _f : '';
2712
+ access = {
2713
+ workspace_id: auth.workspace_id,
2714
+ user_id: auth.user_id,
2715
+ public_key: auth.public_key,
2716
+ token: auth.token,
2717
+ product_id: this.product_id,
2718
+ app_id: String(appRow._id),
2719
+ access_tag: created.access_tag,
2720
+ access: created.access,
2721
+ version: latestVersion,
2722
+ };
2564
2723
  }
2565
- const appData = await this.appApi.fetchApp(access.app_id, this.getUserAccess());
2724
+ const _h = access, { version } = _h, accessFields = __rest(_h, ["version"]);
2725
+ const appData = await this.appApi.fetchApp(accessFields.app_id, this.getUserAccess());
2566
2726
  if (!appData) {
2567
2727
  throw new Error(`App ${app.access_tag} not found`);
2568
2728
  }
@@ -4347,6 +4507,24 @@ class ProductsBuilderService {
4347
4507
  }
4348
4508
  return component;
4349
4509
  }
4510
+ /** Resolve database config from in-memory product cache, falling back to API fetch. */
4511
+ async resolveDatabaseConfig(databaseTag) {
4512
+ var _a;
4513
+ const cached = ((_a = this.product.databases) !== null && _a !== void 0 ? _a : []).find((data) => data.tag === databaseTag);
4514
+ if (cached)
4515
+ return cached;
4516
+ return this.fetchDatabase(databaseTag);
4517
+ }
4518
+ normalizeDatabaseTriggers(database) {
4519
+ const raw = database.triggers;
4520
+ if (!raw)
4521
+ return [];
4522
+ if (Array.isArray(raw))
4523
+ return raw;
4524
+ if (typeof raw === 'object')
4525
+ return Object.values(raw);
4526
+ return [];
4527
+ }
4350
4528
  async deleteDatabase(tag) {
4351
4529
  try {
4352
4530
  const database = await this.fetchDatabase(tag);
@@ -4730,14 +4908,15 @@ class ProductsBuilderService {
4730
4908
  }
4731
4909
  }
4732
4910
  async fetchDatabaseAction(tag, throwErrorIfExists = false) {
4911
+ var _a;
4733
4912
  const [databaseTag, actionTag] = tag.split(':');
4734
4913
  if (!databaseTag || !actionTag) {
4735
4914
  throw new Error(`tag is expected to be defined as "database_tag:action_tag"`);
4736
4915
  }
4737
- const database = this.product.databases.find((data) => data.tag === databaseTag);
4916
+ const database = await this.resolveDatabaseConfig(databaseTag);
4738
4917
  if (!database)
4739
4918
  throw new Error(`Database ${databaseTag} not found`);
4740
- const action = database.actions.find((data) => data.tag === actionTag);
4919
+ const action = ((_a = database.actions) !== null && _a !== void 0 ? _a : []).find((data) => data.tag === actionTag);
4741
4920
  if (!action && throwErrorIfExists)
4742
4921
  throw new Error(`Database action ${tag} not found`);
4743
4922
  if (database.type === productsBuilder_types_1.DatabaseTypes.MONGODB && action) {
@@ -4790,10 +4969,11 @@ class ProductsBuilderService {
4790
4969
  }
4791
4970
  }
4792
4971
  async fetchDatabaseActions(databaseTag) {
4793
- const database = this.product.databases.find((data) => data.tag === databaseTag);
4972
+ var _a;
4973
+ const database = await this.resolveDatabaseConfig(databaseTag);
4794
4974
  if (!database)
4795
4975
  throw new Error(`Database ${databaseTag} not found`);
4796
- const actions = database.actions;
4976
+ const actions = (_a = database.actions) !== null && _a !== void 0 ? _a : [];
4797
4977
  if (database.type === productsBuilder_types_1.DatabaseTypes.MONGODB && actions.length) {
4798
4978
  /*return actions.map((data)=>{
4799
4979
  return{ ...data,template: JSON.parse(String(data.template))}
@@ -4897,24 +5077,25 @@ class ProductsBuilderService {
4897
5077
  await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign(Object.assign({}, existing), data), { value: processedValue || existing.value, databaseTag, component: enums_1.ProductComponents.DATABASE_MIGRATION }), this.getUserAccess());
4898
5078
  }
4899
5079
  async fetchDatabaseMigration(tag, throwError = false) {
5080
+ var _a;
4900
5081
  const [databaseTag, migrationTag] = tag.split(':');
4901
5082
  if (!databaseTag || !migrationTag) {
4902
5083
  throw new Error(`tag is expected to be defined as "database_tag:action_tag"`);
4903
5084
  }
4904
- const database = this.product.databases.find((data) => data.tag === databaseTag);
5085
+ const database = await this.resolveDatabaseConfig(databaseTag);
4905
5086
  if (!database)
4906
5087
  throw new Error(`Database ${databaseTag} not found`);
4907
- const migration = database.migrations.find((data) => data.tag === migrationTag);
5088
+ const migration = ((_a = database.migrations) !== null && _a !== void 0 ? _a : []).find((data) => data.tag === migrationTag);
4908
5089
  if (!migration && throwError)
4909
5090
  throw new Error(`Database migration ${tag} not found`);
4910
5091
  return migration;
4911
5092
  }
4912
5093
  async fetchDatabaseMigrations(databaseTag) {
4913
- const database = this.product.databases.find((data) => data.tag === databaseTag);
5094
+ var _a;
5095
+ const database = await this.resolveDatabaseConfig(databaseTag);
4914
5096
  if (!database)
4915
5097
  throw new Error(`Database ${databaseTag} not found`);
4916
- const migrations = database.migrations;
4917
- return migrations;
5098
+ return (_a = database.migrations) !== null && _a !== void 0 ? _a : [];
4918
5099
  }
4919
5100
  async deleteDatabaseMigration(tag) {
4920
5101
  try {
@@ -4992,21 +5173,19 @@ class ProductsBuilderService {
4992
5173
  if (!databaseTag || !triggerTag) {
4993
5174
  throw new Error(`tag is expected to be defined as "database_tag:trigger_tag"`);
4994
5175
  }
4995
- const database = this.product.databases.find((data) => data.tag === databaseTag);
5176
+ const database = await this.resolveDatabaseConfig(databaseTag);
4996
5177
  if (!database)
4997
5178
  throw new Error(`Database ${databaseTag} not found`);
4998
- const triggers = database.triggers || [];
4999
- const trigger = triggers.find((data) => data.tag === triggerTag);
5179
+ const trigger = this.normalizeDatabaseTriggers(database).find((data) => data.tag === triggerTag);
5000
5180
  if (!trigger && throwError)
5001
5181
  throw new Error(`Database trigger ${tag} not found`);
5002
5182
  return trigger || null;
5003
5183
  }
5004
5184
  async fetchDatabaseTriggers(databaseTag) {
5005
- const database = this.product.databases.find((data) => data.tag === databaseTag);
5185
+ const database = await this.resolveDatabaseConfig(databaseTag);
5006
5186
  if (!database)
5007
5187
  throw new Error(`Database ${databaseTag} not found`);
5008
- const triggers = database.triggers || [];
5009
- return triggers;
5188
+ return this.normalizeDatabaseTriggers(database);
5010
5189
  }
5011
5190
  async deleteDatabaseTrigger(tag) {
5012
5191
  try {