@friggframework/core 2.0.0-next.1 → 2.0.0-next.100

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 (342) hide show
  1. package/CLAUDE.md +702 -0
  2. package/README.md +959 -50
  3. package/admin-scripts/index.js +52 -0
  4. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +166 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +228 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +268 -0
  9. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  10. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  11. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  12. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  13. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  14. package/application/commands/README.md +451 -0
  15. package/application/commands/admin-script-commands.js +358 -0
  16. package/application/commands/credential-commands.js +245 -0
  17. package/application/commands/entity-commands.js +336 -0
  18. package/application/commands/integration-commands.js +343 -0
  19. package/application/commands/process-commands.js +135 -0
  20. package/application/commands/scheduler-commands.js +263 -0
  21. package/application/commands/user-commands.js +312 -0
  22. package/application/index.js +82 -0
  23. package/assertions/index.js +0 -3
  24. package/core/CLAUDE.md +690 -0
  25. package/core/Worker.js +60 -24
  26. package/core/create-handler.js +84 -6
  27. package/credential/repositories/credential-repository-documentdb.js +304 -0
  28. package/credential/repositories/credential-repository-factory.js +54 -0
  29. package/credential/repositories/credential-repository-interface.js +98 -0
  30. package/credential/repositories/credential-repository-mongo.js +269 -0
  31. package/credential/repositories/credential-repository-postgres.js +287 -0
  32. package/credential/repositories/credential-repository.js +300 -0
  33. package/credential/use-cases/get-credential-for-user.js +25 -0
  34. package/credential/use-cases/update-authentication-status.js +15 -0
  35. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  36. package/database/adapters/lambda-invoker.js +97 -0
  37. package/database/config.js +154 -0
  38. package/database/documentdb-encryption-service.js +330 -0
  39. package/database/documentdb-utils.js +136 -0
  40. package/database/encryption/README.md +839 -0
  41. package/database/encryption/documentdb-encryption-service.md +3575 -0
  42. package/database/encryption/encryption-schema-registry.js +401 -0
  43. package/database/encryption/field-encryption-service.js +254 -0
  44. package/database/encryption/logger.js +79 -0
  45. package/database/encryption/prisma-encryption-extension.js +230 -0
  46. package/database/index.js +21 -21
  47. package/database/prisma.js +182 -0
  48. package/database/repositories/health-check-repository-documentdb.js +138 -0
  49. package/database/repositories/health-check-repository-factory.js +48 -0
  50. package/database/repositories/health-check-repository-interface.js +82 -0
  51. package/database/repositories/health-check-repository-mongodb.js +89 -0
  52. package/database/repositories/health-check-repository-postgres.js +82 -0
  53. package/database/repositories/migration-status-repository-s3.js +137 -0
  54. package/database/use-cases/check-database-health-use-case.js +29 -0
  55. package/database/use-cases/check-database-state-use-case.js +81 -0
  56. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  57. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  58. package/database/use-cases/get-migration-status-use-case.js +93 -0
  59. package/database/use-cases/run-database-migration-use-case.js +139 -0
  60. package/database/use-cases/test-encryption-use-case.js +253 -0
  61. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  62. package/database/utils/mongodb-collection-utils.js +94 -0
  63. package/database/utils/mongodb-schema-init.js +108 -0
  64. package/database/utils/prisma-runner.js +477 -0
  65. package/database/utils/prisma-schema-parser.js +182 -0
  66. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  67. package/encrypt/Cryptor.js +34 -168
  68. package/encrypt/index.js +1 -2
  69. package/errors/client-safe-error.js +26 -0
  70. package/errors/fetch-error.js +15 -7
  71. package/errors/index.js +2 -0
  72. package/generated/prisma-mongodb/client.d.ts +1 -0
  73. package/generated/prisma-mongodb/client.js +5 -0
  74. package/generated/prisma-mongodb/default.d.ts +1 -0
  75. package/generated/prisma-mongodb/default.js +5 -0
  76. package/generated/prisma-mongodb/edge.d.ts +1 -0
  77. package/generated/prisma-mongodb/edge.js +374 -0
  78. package/generated/prisma-mongodb/index-browser.js +356 -0
  79. package/generated/prisma-mongodb/index.d.ts +25667 -0
  80. package/generated/prisma-mongodb/index.js +399 -0
  81. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  82. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  83. package/generated/prisma-mongodb/package.json +183 -0
  84. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  85. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  86. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  87. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  88. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  89. package/generated/prisma-mongodb/runtime/library.js +147 -0
  90. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  91. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  92. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  93. package/generated/prisma-mongodb/schema.prisma +432 -0
  94. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  95. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  96. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  97. package/generated/prisma-mongodb/wasm.js +381 -0
  98. package/generated/prisma-postgresql/client.d.ts +1 -0
  99. package/generated/prisma-postgresql/client.js +5 -0
  100. package/generated/prisma-postgresql/default.d.ts +1 -0
  101. package/generated/prisma-postgresql/default.js +5 -0
  102. package/generated/prisma-postgresql/edge.d.ts +1 -0
  103. package/generated/prisma-postgresql/edge.js +395 -0
  104. package/generated/prisma-postgresql/index-browser.js +377 -0
  105. package/generated/prisma-postgresql/index.d.ts +28455 -0
  106. package/generated/prisma-postgresql/index.js +420 -0
  107. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  108. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  109. package/generated/prisma-postgresql/package.json +183 -0
  110. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  111. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  112. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  113. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  114. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  115. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  116. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  117. package/generated/prisma-postgresql/runtime/library.js +147 -0
  118. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  119. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  120. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  121. package/generated/prisma-postgresql/schema.prisma +414 -0
  122. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  123. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  124. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  125. package/generated/prisma-postgresql/wasm.js +402 -0
  126. package/handlers/WEBHOOKS.md +653 -0
  127. package/handlers/app-definition-loader.js +43 -0
  128. package/handlers/app-handler-helpers.js +57 -0
  129. package/handlers/backend-utils.js +316 -0
  130. package/handlers/database-migration-handler.js +227 -0
  131. package/handlers/integration-event-dispatcher.js +54 -0
  132. package/handlers/middleware/admin-auth.js +73 -0
  133. package/handlers/routers/HEALTHCHECK.md +342 -0
  134. package/handlers/routers/auth.js +15 -0
  135. package/handlers/routers/db-migration.handler.js +29 -0
  136. package/handlers/routers/db-migration.js +311 -0
  137. package/handlers/routers/health.js +518 -0
  138. package/handlers/routers/integration-defined-routers.js +117 -0
  139. package/handlers/routers/integration-webhook-routers.js +67 -0
  140. package/handlers/routers/reporting.js +9 -0
  141. package/handlers/routers/user.js +63 -0
  142. package/handlers/routers/websocket.js +57 -0
  143. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  144. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  145. package/handlers/workers/db-migration.js +352 -0
  146. package/handlers/workers/dlq-processor.js +63 -0
  147. package/handlers/workers/integration-defined-workers.js +30 -0
  148. package/index.js +111 -50
  149. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  150. package/infrastructure/scheduler/index.js +33 -0
  151. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  152. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  153. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  154. package/integrations/EXTENSIONS.md +240 -0
  155. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  156. package/integrations/extension.js +254 -0
  157. package/integrations/index.js +20 -10
  158. package/integrations/integration-base.js +619 -72
  159. package/integrations/integration-router.js +460 -218
  160. package/integrations/options.js +3 -4
  161. package/integrations/repositories/config-patch-shared.js +43 -0
  162. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  163. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  164. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  165. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  166. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  167. package/integrations/repositories/integration-mapping-repository.js +156 -0
  168. package/integrations/repositories/integration-repository-documentdb.js +284 -0
  169. package/integrations/repositories/integration-repository-factory.js +51 -0
  170. package/integrations/repositories/integration-repository-interface.js +173 -0
  171. package/integrations/repositories/integration-repository-mongo.js +414 -0
  172. package/integrations/repositories/integration-repository-postgres.js +452 -0
  173. package/integrations/repositories/process-repository-documentdb.js +311 -0
  174. package/integrations/repositories/process-repository-factory.js +53 -0
  175. package/integrations/repositories/process-repository-interface.js +136 -0
  176. package/integrations/repositories/process-repository-mongo.js +262 -0
  177. package/integrations/repositories/process-repository-postgres.js +380 -0
  178. package/integrations/repositories/process-update-ops-shared.js +112 -0
  179. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  180. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  181. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  182. package/integrations/use-cases/create-integration.js +215 -0
  183. package/integrations/use-cases/create-process.js +130 -0
  184. package/integrations/use-cases/delete-integration-for-user.js +119 -0
  185. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  186. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  187. package/integrations/use-cases/get-integration-for-user.js +78 -0
  188. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  189. package/integrations/use-cases/get-integration-instance.js +83 -0
  190. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  191. package/integrations/use-cases/get-possible-integrations.js +27 -0
  192. package/integrations/use-cases/get-process.js +89 -0
  193. package/integrations/use-cases/index.js +19 -0
  194. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  195. package/integrations/use-cases/load-integration-context.js +71 -0
  196. package/integrations/use-cases/patch-integration-config.js +39 -0
  197. package/integrations/use-cases/process-errors.js +28 -0
  198. package/integrations/use-cases/update-integration-config.js +32 -0
  199. package/integrations/use-cases/update-integration-messages.js +44 -0
  200. package/integrations/use-cases/update-integration-status.js +32 -0
  201. package/integrations/use-cases/update-integration.js +92 -0
  202. package/integrations/use-cases/update-process-metrics.js +216 -0
  203. package/integrations/use-cases/update-process-state.js +163 -0
  204. package/integrations/utils/map-integration-dto.js +37 -0
  205. package/jest-global-setup-noop.js +3 -0
  206. package/jest-global-teardown-noop.js +3 -0
  207. package/logs/logger.js +0 -4
  208. package/{module-plugin → modules}/index.js +0 -10
  209. package/modules/module-factory.js +56 -0
  210. package/modules/module.js +274 -0
  211. package/modules/repositories/module-repository-documentdb.js +350 -0
  212. package/modules/repositories/module-repository-factory.js +40 -0
  213. package/modules/repositories/module-repository-interface.js +145 -0
  214. package/modules/repositories/module-repository-mongo.js +436 -0
  215. package/modules/repositories/module-repository-postgres.js +481 -0
  216. package/modules/repositories/module-repository.js +369 -0
  217. package/modules/requester/api-key.js +52 -0
  218. package/modules/requester/oauth-2.js +396 -0
  219. package/modules/requester/requester.js +285 -0
  220. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  221. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  222. package/modules/tests/doubles/test-module-factory.js +16 -0
  223. package/modules/tests/doubles/test-module-repository.js +39 -0
  224. package/modules/use-cases/get-entities-for-user.js +32 -0
  225. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  226. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  227. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  228. package/modules/use-cases/get-module.js +74 -0
  229. package/modules/use-cases/process-authorization-callback.js +243 -0
  230. package/modules/use-cases/refresh-entity-options.js +72 -0
  231. package/modules/use-cases/test-module-auth.js +72 -0
  232. package/modules/utils/map-module-dto.js +18 -0
  233. package/package.json +82 -50
  234. package/prisma-mongodb/schema.prisma +432 -0
  235. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  236. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  237. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  238. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  239. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  240. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  241. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  242. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  243. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  244. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  245. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  246. package/prisma-postgresql/schema.prisma +414 -0
  247. package/queues/queuer-util.js +103 -21
  248. package/reporting/README.md +86 -0
  249. package/reporting/index.js +17 -0
  250. package/reporting/reporting-router.js +77 -0
  251. package/reporting/repositories/reporting-repository-documentdb.js +127 -0
  252. package/reporting/repositories/reporting-repository-factory.js +35 -0
  253. package/reporting/repositories/reporting-repository-interface.js +16 -0
  254. package/reporting/repositories/reporting-repository-mongo.js +54 -0
  255. package/reporting/repositories/reporting-repository-postgres.js +70 -0
  256. package/reporting/use-cases/index.js +6 -0
  257. package/reporting/use-cases/list-integrations-report.js +142 -0
  258. package/syncs/manager.js +468 -443
  259. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  260. package/syncs/repositories/sync-repository-factory.js +43 -0
  261. package/syncs/repositories/sync-repository-interface.js +109 -0
  262. package/syncs/repositories/sync-repository-mongo.js +239 -0
  263. package/syncs/repositories/sync-repository-postgres.js +319 -0
  264. package/syncs/sync.js +0 -1
  265. package/token/repositories/token-repository-documentdb.js +137 -0
  266. package/token/repositories/token-repository-factory.js +40 -0
  267. package/token/repositories/token-repository-interface.js +131 -0
  268. package/token/repositories/token-repository-mongo.js +219 -0
  269. package/token/repositories/token-repository-postgres.js +264 -0
  270. package/token/repositories/token-repository.js +219 -0
  271. package/types/associations/index.d.ts +0 -17
  272. package/types/core/index.d.ts +12 -4
  273. package/types/database/index.d.ts +10 -2
  274. package/types/encrypt/index.d.ts +5 -3
  275. package/types/integrations/index.d.ts +3 -8
  276. package/types/module-plugin/index.d.ts +17 -69
  277. package/types/syncs/index.d.ts +0 -17
  278. package/user/repositories/user-repository-documentdb.js +458 -0
  279. package/user/repositories/user-repository-factory.js +52 -0
  280. package/user/repositories/user-repository-interface.js +214 -0
  281. package/user/repositories/user-repository-mongo.js +323 -0
  282. package/user/repositories/user-repository-postgres.js +377 -0
  283. package/user/tests/doubles/test-user-repository.js +72 -0
  284. package/user/use-cases/authenticate-user.js +127 -0
  285. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  286. package/user/use-cases/create-individual-user.js +61 -0
  287. package/user/use-cases/create-organization-user.js +47 -0
  288. package/user/use-cases/create-token-for-user-id.js +30 -0
  289. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  290. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  291. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  292. package/user/use-cases/login-user.js +122 -0
  293. package/user/user.js +125 -0
  294. package/utils/backend-path.js +38 -0
  295. package/utils/index.js +6 -0
  296. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  297. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  298. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  299. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  300. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  301. package/websocket/repositories/websocket-connection-repository.js +161 -0
  302. package/assertions/is-equal.js +0 -17
  303. package/associations/model.js +0 -54
  304. package/database/models/IndividualUser.js +0 -76
  305. package/database/models/OrganizationUser.js +0 -29
  306. package/database/models/State.js +0 -9
  307. package/database/models/Token.js +0 -70
  308. package/database/models/UserModel.js +0 -7
  309. package/database/models/WebsocketConnection.js +0 -49
  310. package/database/mongo.js +0 -45
  311. package/database/mongoose.js +0 -5
  312. package/encrypt/Cryptor.test.js +0 -32
  313. package/encrypt/encrypt.js +0 -132
  314. package/encrypt/encrypt.test.js +0 -1069
  315. package/encrypt/test-encrypt.js +0 -107
  316. package/errors/base-error.test.js +0 -32
  317. package/errors/fetch-error.test.js +0 -79
  318. package/errors/halt-error.test.js +0 -11
  319. package/errors/validation-errors.test.js +0 -120
  320. package/integrations/create-frigg-backend.js +0 -31
  321. package/integrations/integration-factory.js +0 -251
  322. package/integrations/integration-mapping.js +0 -43
  323. package/integrations/integration-model.js +0 -46
  324. package/integrations/integration-user.js +0 -144
  325. package/integrations/test/integration-base.test.js +0 -144
  326. package/lambda/TimeoutCatcher.test.js +0 -68
  327. package/logs/logger.test.js +0 -76
  328. package/module-plugin/auther.js +0 -350
  329. package/module-plugin/credential.js +0 -22
  330. package/module-plugin/entity-manager.js +0 -70
  331. package/module-plugin/entity.js +0 -46
  332. package/module-plugin/manager.js +0 -169
  333. package/module-plugin/module-factory.js +0 -61
  334. package/module-plugin/requester/api-key.js +0 -36
  335. package/module-plugin/requester/oauth-2.js +0 -219
  336. package/module-plugin/requester/requester.js +0 -165
  337. package/module-plugin/requester/requester.test.js +0 -28
  338. package/module-plugin/test/auther.test.js +0 -97
  339. package/syncs/model.js +0 -62
  340. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  341. /package/{module-plugin → modules}/requester/basic.js +0 -0
  342. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,268 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ AdminScriptExecutionRepositoryInterface,
4
+ } = require('./admin-script-execution-repository-interface');
5
+
6
+ /**
7
+ * PostgreSQL Admin Process Repository Adapter
8
+ * Handles admin process persistence using Prisma with PostgreSQL
9
+ *
10
+ * PostgreSQL-specific characteristics:
11
+ * - Uses Int IDs with autoincrement
12
+ * - Requires ID conversion: String (app layer) ↔ Int (database)
13
+ * - All returned IDs are converted to strings for application layer consistency
14
+ * - context and results are Json objects
15
+ */
16
+ class AdminScriptExecutionRepositoryPostgres extends AdminScriptExecutionRepositoryInterface {
17
+ constructor() {
18
+ super();
19
+ this.prisma = prisma;
20
+ }
21
+
22
+ /**
23
+ * Convert string ID to integer for PostgreSQL queries
24
+ * @private
25
+ * @param {string|number|null|undefined} id - ID to convert
26
+ * @returns {number|null|undefined} Integer ID or null/undefined
27
+ * @throws {Error} If ID cannot be converted to integer
28
+ */
29
+ _convertId(id) {
30
+ if (id === null || id === undefined) return id;
31
+ const parsed = Number.parseInt(id, 10);
32
+ if (Number.isNaN(parsed)) {
33
+ throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
34
+ }
35
+ return parsed;
36
+ }
37
+
38
+ /**
39
+ * Convert process object IDs to strings
40
+ * @private
41
+ * @param {Object|null} process - Process object from database
42
+ * @returns {Object|null} Process with string IDs
43
+ */
44
+ _convertExecutionIds(process) {
45
+ if (!process) return process;
46
+ return {
47
+ ...process,
48
+ id: process.id?.toString(),
49
+ parentExecutionId: process.parentExecutionId?.toString(),
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Create a new admin process record
55
+ *
56
+ * @param {Object} params - Process creation parameters
57
+ * @param {string} params.name - Name of the process
58
+ * @param {string} params.type - Type of process (e.g., 'ADMIN_SCRIPT', 'DB_MIGRATION')
59
+ * @param {Object} [params.context] - Context data
60
+ * @returns {Promise<Object>} The created process record with string ID
61
+ */
62
+ async createExecution({ name, type, context = {}, parentExecutionId }) {
63
+ const data = {
64
+ name,
65
+ type,
66
+ context,
67
+ results: { logs: [] },
68
+ ...(parentExecutionId != null && {
69
+ parentExecutionId: this._convertId(parentExecutionId),
70
+ }),
71
+ };
72
+
73
+ const process = await this.prisma.adminScriptExecution.create({
74
+ data,
75
+ });
76
+
77
+ return this._convertExecutionIds(process);
78
+ }
79
+
80
+ /**
81
+ * Find a process by its ID
82
+ *
83
+ * @param {string|number} id - The process ID
84
+ * @returns {Promise<Object|null>} The process record with string ID or null if not found
85
+ */
86
+ async findExecutionById(id) {
87
+ const intId = this._convertId(id);
88
+ const process = await this.prisma.adminScriptExecution.findUnique({
89
+ where: { id: intId },
90
+ });
91
+
92
+ return this._convertExecutionIds(process);
93
+ }
94
+
95
+ /**
96
+ * Find all processes with a specific name
97
+ *
98
+ * @param {string} name - The process name to filter by
99
+ * @param {Object} [options] - Query options
100
+ * @param {number} [options.limit] - Maximum number of results
101
+ * @param {number} [options.offset] - Number of results to skip
102
+ * @param {string} [options.sortBy] - Field to sort by
103
+ * @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
104
+ * @returns {Promise<Array>} Array of process records with string IDs
105
+ */
106
+ async findExecutionsByName(name, options = {}) {
107
+ const {
108
+ limit,
109
+ offset,
110
+ sortBy = 'createdAt',
111
+ sortOrder = 'desc',
112
+ state,
113
+ } = options;
114
+
115
+ const where = { name };
116
+ if (state) where.state = state;
117
+
118
+ const processes = await this.prisma.adminScriptExecution.findMany({
119
+ where,
120
+ orderBy: { [sortBy]: sortOrder },
121
+ take: limit,
122
+ skip: offset,
123
+ });
124
+
125
+ return processes.map((process) => this._convertExecutionIds(process));
126
+ }
127
+
128
+ /**
129
+ * Find all processes with a specific state
130
+ *
131
+ * @param {string} state - State to filter by
132
+ * @param {Object} [options] - Query options
133
+ * @param {number} [options.limit] - Maximum number of results
134
+ * @param {number} [options.offset] - Number of results to skip
135
+ * @param {string} [options.sortBy] - Field to sort by
136
+ * @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
137
+ * @returns {Promise<Array>} Array of process records with string IDs
138
+ */
139
+ async findExecutionsByState(state, options = {}) {
140
+ const {
141
+ limit,
142
+ offset,
143
+ sortBy = 'createdAt',
144
+ sortOrder = 'desc',
145
+ } = options;
146
+
147
+ const processes = await this.prisma.adminScriptExecution.findMany({
148
+ where: { state },
149
+ orderBy: { [sortBy]: sortOrder },
150
+ take: limit,
151
+ skip: offset,
152
+ });
153
+
154
+ return processes.map((process) => this._convertExecutionIds(process));
155
+ }
156
+
157
+ /**
158
+ * Update the state of a process
159
+ *
160
+ * @param {string|number} id - The process ID
161
+ * @param {string} state - New state value
162
+ * @returns {Promise<Object>} Updated process record with string ID
163
+ */
164
+ async updateExecutionState(id, state) {
165
+ const intId = this._convertId(id);
166
+ const process = await this.prisma.adminScriptExecution.update({
167
+ where: { id: intId },
168
+ data: { state },
169
+ });
170
+
171
+ return this._convertExecutionIds(process);
172
+ }
173
+
174
+ /**
175
+ * Update the results of a process
176
+ * Merges new results with existing results
177
+ *
178
+ * @param {string|number} id - The process ID
179
+ * @param {Object} results - Results data to merge
180
+ * @returns {Promise<Object>} Updated process record with string ID
181
+ */
182
+ async updateExecutionResults(id, results) {
183
+ const intId = this._convertId(id);
184
+
185
+ // Get current process to merge results
186
+ const currentProcess = await this.prisma.adminScriptExecution.findUnique({
187
+ where: { id: intId },
188
+ });
189
+
190
+ if (!currentProcess) {
191
+ throw new Error(`AdminScriptExecution ${id} not found`);
192
+ }
193
+
194
+ // Merge new results with existing results
195
+ const mergedResults = {
196
+ ...(currentProcess.results || {}),
197
+ ...results,
198
+ };
199
+
200
+ const process = await this.prisma.adminScriptExecution.update({
201
+ where: { id: intId },
202
+ data: { results: mergedResults },
203
+ });
204
+
205
+ return this._convertExecutionIds(process);
206
+ }
207
+
208
+ /**
209
+ * Append a log entry to a process's log array in results
210
+ *
211
+ * @param {string|number} id - The process ID
212
+ * @param {Object} logEntry - Log entry to append
213
+ * @param {string} logEntry.level - Log level ('debug', 'info', 'warn', 'error')
214
+ * @param {string} logEntry.message - Log message
215
+ * @param {Object} [logEntry.data] - Additional log data
216
+ * @param {string} logEntry.timestamp - ISO timestamp
217
+ * @returns {Promise<Object>} Updated process record with string ID
218
+ */
219
+ async appendExecutionLog(id, logEntry) {
220
+ const intId = this._convertId(id);
221
+
222
+ // Get current process
223
+ const process = await this.prisma.adminScriptExecution.findUnique({
224
+ where: { id: intId },
225
+ });
226
+
227
+ if (!process) {
228
+ throw new Error(`AdminScriptExecution ${id} not found`);
229
+ }
230
+
231
+ // Get current results and logs
232
+ const results = process.results || {};
233
+ const logs = Array.isArray(results.logs) ? [...results.logs] : [];
234
+ logs.push(logEntry);
235
+
236
+ // Update with new logs array in results
237
+ const updated = await this.prisma.adminScriptExecution.update({
238
+ where: { id: intId },
239
+ data: { results: { ...results, logs } },
240
+ });
241
+
242
+ return this._convertExecutionIds(updated);
243
+ }
244
+
245
+ /**
246
+ * Delete all processes older than a specific date
247
+ * Used for cleanup and retention policies
248
+ *
249
+ * @param {Date} date - Delete processes older than this date
250
+ * @returns {Promise<Object>} Deletion result with count
251
+ */
252
+ async deleteExecutionsOlderThan(date) {
253
+ const result = await this.prisma.adminScriptExecution.deleteMany({
254
+ where: {
255
+ createdAt: {
256
+ lt: date,
257
+ },
258
+ },
259
+ });
260
+
261
+ return {
262
+ acknowledged: true,
263
+ deletedCount: result.count,
264
+ };
265
+ }
266
+ }
267
+
268
+ module.exports = { AdminScriptExecutionRepositoryPostgres };
@@ -0,0 +1,21 @@
1
+ const {
2
+ ScriptScheduleRepositoryMongo,
3
+ } = require('./script-schedule-repository-mongo');
4
+
5
+ /**
6
+ * DocumentDB Script Schedule Repository Adapter
7
+ * Handles script schedule persistence using Prisma with AWS DocumentDB
8
+ *
9
+ * DocumentDB is MongoDB-compatible with some limitations:
10
+ * - Uses MongoDB wire protocol
11
+ * - Same Prisma schema as MongoDB
12
+ * - Inherits all MongoDB repository methods
13
+ *
14
+ * For schedule operations, DocumentDB and MongoDB behavior is identical.
15
+ */
16
+ class ScriptScheduleRepositoryDocumentDB extends ScriptScheduleRepositoryMongo {
17
+ // Inherits all methods from MongoDB implementation
18
+ // DocumentDB is MongoDB-compatible for these operations
19
+ }
20
+
21
+ module.exports = { ScriptScheduleRepositoryDocumentDB };
@@ -0,0 +1,51 @@
1
+ const { ScriptScheduleRepositoryMongo } = require('./script-schedule-repository-mongo');
2
+ const { ScriptScheduleRepositoryPostgres } = require('./script-schedule-repository-postgres');
3
+ const {
4
+ ScriptScheduleRepositoryDocumentDB,
5
+ } = require('./script-schedule-repository-documentdb');
6
+ const config = require('../../database/config');
7
+
8
+ /**
9
+ * Script Schedule Repository Factory
10
+ * Creates the appropriate repository adapter based on database type
11
+ *
12
+ * This implements the Factory pattern for Hexagonal Architecture:
13
+ * - Reads database type from app definition (backend/index.js)
14
+ * - Returns correct adapter (MongoDB, DocumentDB, or PostgreSQL)
15
+ * - Provides clear error for unsupported databases
16
+ *
17
+ * Usage:
18
+ * ```javascript
19
+ * const repository = createScriptScheduleRepository();
20
+ * ```
21
+ *
22
+ * @returns {ScriptScheduleRepositoryInterface} Configured repository adapter
23
+ * @throws {Error} If database type is not supported
24
+ */
25
+ function createScriptScheduleRepository() {
26
+ const dbType = config.DB_TYPE;
27
+
28
+ switch (dbType) {
29
+ case 'mongodb':
30
+ return new ScriptScheduleRepositoryMongo();
31
+
32
+ case 'postgresql':
33
+ return new ScriptScheduleRepositoryPostgres();
34
+
35
+ case 'documentdb':
36
+ return new ScriptScheduleRepositoryDocumentDB();
37
+
38
+ default:
39
+ throw new Error(
40
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
41
+ );
42
+ }
43
+ }
44
+
45
+ module.exports = {
46
+ createScriptScheduleRepository,
47
+ // Export adapters for direct testing
48
+ ScriptScheduleRepositoryMongo,
49
+ ScriptScheduleRepositoryPostgres,
50
+ ScriptScheduleRepositoryDocumentDB,
51
+ };
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Script Schedule Repository Interface
3
+ * Abstract base class defining the contract for script schedule persistence adapters
4
+ *
5
+ * This follows the Port in Hexagonal Architecture:
6
+ * - Domain layer depends on this abstraction
7
+ * - Concrete adapters implement this interface
8
+ * - Use cases receive repositories via dependency injection
9
+ *
10
+ * Script schedules support Phase 2 hybrid scheduling:
11
+ * - Database overrides take precedence over Definition defaults
12
+ * - EventBridge rules provisioned for enabled schedules
13
+ * - lastTriggeredAt and nextTriggerAt for monitoring
14
+ *
15
+ * @abstract
16
+ */
17
+ class ScriptScheduleRepositoryInterface {
18
+ /**
19
+ * Find a schedule by script name
20
+ *
21
+ * @param {string} scriptName - The script name
22
+ * @returns {Promise<Object|null>} Schedule record or null if not found
23
+ * @abstract
24
+ */
25
+ async findScheduleByScriptName(scriptName) {
26
+ throw new Error('Method findScheduleByScriptName must be implemented by subclass');
27
+ }
28
+
29
+ /**
30
+ * Create or update a schedule (upsert)
31
+ *
32
+ * @param {Object} params - Schedule parameters
33
+ * @param {string} params.scriptName - Name of the script
34
+ * @param {boolean} params.enabled - Whether schedule is enabled
35
+ * @param {string} params.cronExpression - Cron expression
36
+ * @param {string} [params.timezone] - Timezone (default 'UTC')
37
+ * @param {string} [params.externalScheduleId] - External scheduler ID (e.g., AWS ARN)
38
+ * @param {string} [params.externalScheduleName] - External scheduler name
39
+ * @returns {Promise<Object>} Created or updated schedule record
40
+ * @abstract
41
+ */
42
+ async upsertSchedule({ scriptName, enabled, cronExpression, timezone, externalScheduleId, externalScheduleName }) {
43
+ throw new Error('Method upsertSchedule must be implemented by subclass');
44
+ }
45
+
46
+ /**
47
+ * Delete a schedule by script name
48
+ *
49
+ * @param {string} scriptName - The script name
50
+ * @returns {Promise<Object>} Deletion result
51
+ * @abstract
52
+ */
53
+ async deleteSchedule(scriptName) {
54
+ throw new Error('Method deleteSchedule must be implemented by subclass');
55
+ }
56
+
57
+ /**
58
+ * Update external scheduler information
59
+ *
60
+ * @param {string} scriptName - The script name
61
+ * @param {Object} externalInfo - External schedule information
62
+ * @param {string} [externalInfo.externalScheduleId] - External scheduler ID (e.g., AWS ARN)
63
+ * @param {string} [externalInfo.externalScheduleName] - External scheduler name
64
+ * @returns {Promise<Object>} Updated schedule record
65
+ * @abstract
66
+ */
67
+ async updateScheduleExternalInfo(scriptName, { externalScheduleId, externalScheduleName }) {
68
+ throw new Error('Method updateScheduleExternalInfo must be implemented by subclass');
69
+ }
70
+
71
+ /**
72
+ * Update last triggered timestamp
73
+ *
74
+ * @param {string} scriptName - The script name
75
+ * @param {Date} [timestamp] - Trigger timestamp (default: now)
76
+ * @returns {Promise<Object>} Updated schedule record
77
+ * @abstract
78
+ */
79
+ async updateScheduleLastTriggered(scriptName, timestamp) {
80
+ throw new Error('Method updateScheduleLastTriggered must be implemented by subclass');
81
+ }
82
+
83
+ /**
84
+ * Update next trigger timestamp
85
+ *
86
+ * @param {string} scriptName - The script name
87
+ * @param {Date} timestamp - Next trigger timestamp
88
+ * @returns {Promise<Object>} Updated schedule record
89
+ * @abstract
90
+ */
91
+ async updateScheduleNextTrigger(scriptName, timestamp) {
92
+ throw new Error('Method updateScheduleNextTrigger must be implemented by subclass');
93
+ }
94
+
95
+ /**
96
+ * List all schedules
97
+ *
98
+ * @param {Object} [options] - Query options
99
+ * @param {boolean} [options.enabledOnly] - Only return enabled schedules
100
+ * @returns {Promise<Array>} Array of schedule records
101
+ * @abstract
102
+ */
103
+ async listSchedules(options = {}) {
104
+ throw new Error('Method listSchedules must be implemented by subclass');
105
+ }
106
+ }
107
+
108
+ module.exports = { ScriptScheduleRepositoryInterface };
@@ -0,0 +1,179 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ ScriptScheduleRepositoryInterface,
4
+ } = require('./script-schedule-repository-interface');
5
+
6
+ /**
7
+ * MongoDB Script Schedule Repository Adapter
8
+ * Handles script schedule persistence using Prisma with MongoDB
9
+ *
10
+ * MongoDB-specific characteristics:
11
+ * - IDs are strings with @db.ObjectId
12
+ * - scriptName has unique index
13
+ * - Supports upsert operations natively
14
+ */
15
+ class ScriptScheduleRepositoryMongo extends ScriptScheduleRepositoryInterface {
16
+ constructor() {
17
+ super();
18
+ this.prisma = prisma;
19
+ }
20
+
21
+ /**
22
+ * Find a schedule by script name
23
+ *
24
+ * @param {string} scriptName - The script name
25
+ * @returns {Promise<Object|null>} Schedule record or null if not found
26
+ */
27
+ async findScheduleByScriptName(scriptName) {
28
+ const schedule = await this.prisma.scriptSchedule.findUnique({
29
+ where: { scriptName },
30
+ });
31
+
32
+ return schedule;
33
+ }
34
+
35
+ /**
36
+ * Create or update a schedule (upsert)
37
+ *
38
+ * @param {Object} params - Schedule parameters
39
+ * @param {string} params.scriptName - Name of the script
40
+ * @param {boolean} params.enabled - Whether schedule is enabled
41
+ * @param {string} params.cronExpression - Cron expression
42
+ * @param {string} [params.timezone] - Timezone (default 'UTC')
43
+ * @param {string} [params.externalScheduleId] - External scheduler ID (e.g., AWS ARN)
44
+ * @param {string} [params.externalScheduleName] - External scheduler name
45
+ * @returns {Promise<Object>} Created or updated schedule record
46
+ */
47
+ async upsertSchedule({ scriptName, enabled, cronExpression, timezone, externalScheduleId, externalScheduleName }) {
48
+ const data = {
49
+ enabled,
50
+ cronExpression,
51
+ timezone: timezone || 'UTC',
52
+ };
53
+
54
+ // Only set external scheduler fields if provided
55
+ if (externalScheduleId !== undefined) data.externalScheduleId = externalScheduleId;
56
+ if (externalScheduleName !== undefined) data.externalScheduleName = externalScheduleName;
57
+
58
+ const schedule = await this.prisma.scriptSchedule.upsert({
59
+ where: { scriptName },
60
+ update: data,
61
+ create: {
62
+ scriptName,
63
+ ...data,
64
+ },
65
+ });
66
+
67
+ return schedule;
68
+ }
69
+
70
+ /**
71
+ * Delete a schedule by script name
72
+ *
73
+ * @param {string} scriptName - The script name
74
+ * @returns {Promise<Object>} Deletion result
75
+ */
76
+ async deleteSchedule(scriptName) {
77
+ try {
78
+ const schedule = await this.prisma.scriptSchedule.delete({
79
+ where: { scriptName },
80
+ });
81
+
82
+ return {
83
+ acknowledged: true,
84
+ deletedCount: 1,
85
+ deleted: schedule,
86
+ };
87
+ } catch (error) {
88
+ // Return 0 count if not found
89
+ if (error.code === 'P2025') {
90
+ return {
91
+ acknowledged: true,
92
+ deletedCount: 0,
93
+ };
94
+ }
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Update external scheduler information
101
+ *
102
+ * @param {string} scriptName - The script name
103
+ * @param {Object} externalInfo - External schedule information
104
+ * @param {string} [externalInfo.externalScheduleId] - External scheduler ID (e.g., AWS ARN)
105
+ * @param {string} [externalInfo.externalScheduleName] - External scheduler name
106
+ * @returns {Promise<Object>} Updated schedule record
107
+ */
108
+ async updateScheduleExternalInfo(scriptName, { externalScheduleId, externalScheduleName }) {
109
+ const data = {};
110
+ if (externalScheduleId !== undefined) data.externalScheduleId = externalScheduleId;
111
+ if (externalScheduleName !== undefined) data.externalScheduleName = externalScheduleName;
112
+
113
+ const schedule = await this.prisma.scriptSchedule.update({
114
+ where: { scriptName },
115
+ data,
116
+ });
117
+
118
+ return schedule;
119
+ }
120
+
121
+ /**
122
+ * Update last triggered timestamp
123
+ *
124
+ * @param {string} scriptName - The script name
125
+ * @param {Date} [timestamp] - Trigger timestamp (default: now)
126
+ * @returns {Promise<Object>} Updated schedule record
127
+ */
128
+ async updateScheduleLastTriggered(scriptName, timestamp) {
129
+ const schedule = await this.prisma.scriptSchedule.update({
130
+ where: { scriptName },
131
+ data: {
132
+ lastTriggeredAt: timestamp || new Date(),
133
+ },
134
+ });
135
+
136
+ return schedule;
137
+ }
138
+
139
+ /**
140
+ * Update next trigger timestamp
141
+ *
142
+ * @param {string} scriptName - The script name
143
+ * @param {Date} timestamp - Next trigger timestamp
144
+ * @returns {Promise<Object>} Updated schedule record
145
+ */
146
+ async updateScheduleNextTrigger(scriptName, timestamp) {
147
+ const schedule = await this.prisma.scriptSchedule.update({
148
+ where: { scriptName },
149
+ data: {
150
+ nextTriggerAt: timestamp,
151
+ },
152
+ });
153
+
154
+ return schedule;
155
+ }
156
+
157
+ /**
158
+ * List all schedules
159
+ *
160
+ * @param {Object} [options] - Query options
161
+ * @param {boolean} [options.enabledOnly] - Only return enabled schedules
162
+ * @returns {Promise<Array>} Array of schedule records
163
+ */
164
+ async listSchedules(options = {}) {
165
+ const where = {};
166
+ if (options.enabledOnly) {
167
+ where.enabled = true;
168
+ }
169
+
170
+ const schedules = await this.prisma.scriptSchedule.findMany({
171
+ where,
172
+ orderBy: { scriptName: 'asc' },
173
+ });
174
+
175
+ return schedules;
176
+ }
177
+ }
178
+
179
+ module.exports = { ScriptScheduleRepositoryMongo };