@friggframework/core 2.0.0-next.6 → 2.0.0-next.61

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 (286) hide show
  1. package/CLAUDE.md +694 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +451 -0
  4. package/application/commands/credential-commands.js +245 -0
  5. package/application/commands/entity-commands.js +336 -0
  6. package/application/commands/integration-commands.js +210 -0
  7. package/application/commands/user-commands.js +283 -0
  8. package/application/index.js +69 -0
  9. package/core/CLAUDE.md +690 -0
  10. package/core/Worker.js +8 -21
  11. package/core/create-handler.js +14 -7
  12. package/credential/repositories/credential-repository-documentdb.js +304 -0
  13. package/credential/repositories/credential-repository-factory.js +54 -0
  14. package/credential/repositories/credential-repository-interface.js +98 -0
  15. package/credential/repositories/credential-repository-mongo.js +269 -0
  16. package/credential/repositories/credential-repository-postgres.js +291 -0
  17. package/credential/repositories/credential-repository.js +302 -0
  18. package/credential/use-cases/get-credential-for-user.js +25 -0
  19. package/credential/use-cases/update-authentication-status.js +15 -0
  20. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  21. package/database/adapters/lambda-invoker.js +97 -0
  22. package/database/config.js +154 -0
  23. package/database/documentdb-encryption-service.js +330 -0
  24. package/database/documentdb-utils.js +136 -0
  25. package/database/encryption/README.md +839 -0
  26. package/database/encryption/documentdb-encryption-service.md +3575 -0
  27. package/database/encryption/encryption-schema-registry.js +268 -0
  28. package/database/encryption/field-encryption-service.js +226 -0
  29. package/database/encryption/logger.js +79 -0
  30. package/database/encryption/prisma-encryption-extension.js +222 -0
  31. package/database/index.js +61 -21
  32. package/database/models/WebsocketConnection.js +16 -10
  33. package/database/models/readme.md +1 -0
  34. package/database/prisma.js +182 -0
  35. package/database/repositories/health-check-repository-documentdb.js +134 -0
  36. package/database/repositories/health-check-repository-factory.js +48 -0
  37. package/database/repositories/health-check-repository-interface.js +82 -0
  38. package/database/repositories/health-check-repository-mongodb.js +89 -0
  39. package/database/repositories/health-check-repository-postgres.js +82 -0
  40. package/database/repositories/health-check-repository.js +108 -0
  41. package/database/repositories/migration-status-repository-s3.js +137 -0
  42. package/database/use-cases/check-database-health-use-case.js +29 -0
  43. package/database/use-cases/check-database-state-use-case.js +81 -0
  44. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  45. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  46. package/database/use-cases/get-migration-status-use-case.js +93 -0
  47. package/database/use-cases/run-database-migration-use-case.js +139 -0
  48. package/database/use-cases/test-encryption-use-case.js +253 -0
  49. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  50. package/database/utils/mongodb-collection-utils.js +91 -0
  51. package/database/utils/mongodb-schema-init.js +106 -0
  52. package/database/utils/prisma-runner.js +477 -0
  53. package/database/utils/prisma-schema-parser.js +182 -0
  54. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  55. package/encrypt/Cryptor.js +34 -168
  56. package/encrypt/index.js +1 -2
  57. package/encrypt/test-encrypt.js +0 -2
  58. package/errors/client-safe-error.js +26 -0
  59. package/errors/fetch-error.js +2 -1
  60. package/errors/index.js +2 -0
  61. package/generated/prisma-mongodb/client.d.ts +1 -0
  62. package/generated/prisma-mongodb/client.js +4 -0
  63. package/generated/prisma-mongodb/default.d.ts +1 -0
  64. package/generated/prisma-mongodb/default.js +4 -0
  65. package/generated/prisma-mongodb/edge.d.ts +1 -0
  66. package/generated/prisma-mongodb/edge.js +334 -0
  67. package/generated/prisma-mongodb/index-browser.js +316 -0
  68. package/generated/prisma-mongodb/index.d.ts +22903 -0
  69. package/generated/prisma-mongodb/index.js +359 -0
  70. package/generated/prisma-mongodb/package.json +183 -0
  71. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  72. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  73. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  74. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  75. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  76. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  77. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  78. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  79. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  80. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  81. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  82. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  83. package/generated/prisma-mongodb/schema.prisma +360 -0
  84. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  85. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  86. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  87. package/generated/prisma-mongodb/wasm.js +341 -0
  88. package/generated/prisma-postgresql/client.d.ts +1 -0
  89. package/generated/prisma-postgresql/client.js +4 -0
  90. package/generated/prisma-postgresql/default.d.ts +1 -0
  91. package/generated/prisma-postgresql/default.js +4 -0
  92. package/generated/prisma-postgresql/edge.d.ts +1 -0
  93. package/generated/prisma-postgresql/edge.js +356 -0
  94. package/generated/prisma-postgresql/index-browser.js +338 -0
  95. package/generated/prisma-postgresql/index.d.ts +25077 -0
  96. package/generated/prisma-postgresql/index.js +381 -0
  97. package/generated/prisma-postgresql/package.json +183 -0
  98. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  99. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  100. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  101. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  102. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  103. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  104. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  105. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  106. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  107. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  108. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  109. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  110. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  111. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  112. package/generated/prisma-postgresql/schema.prisma +343 -0
  113. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  114. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  115. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  116. package/generated/prisma-postgresql/wasm.js +363 -0
  117. package/handlers/WEBHOOKS.md +653 -0
  118. package/handlers/app-definition-loader.js +38 -0
  119. package/handlers/app-handler-helpers.js +56 -0
  120. package/handlers/backend-utils.js +186 -0
  121. package/handlers/database-migration-handler.js +227 -0
  122. package/handlers/integration-event-dispatcher.js +54 -0
  123. package/handlers/routers/HEALTHCHECK.md +342 -0
  124. package/handlers/routers/auth.js +15 -0
  125. package/handlers/routers/db-migration.handler.js +29 -0
  126. package/handlers/routers/db-migration.js +326 -0
  127. package/handlers/routers/health.js +516 -0
  128. package/handlers/routers/integration-defined-routers.js +45 -0
  129. package/handlers/routers/integration-webhook-routers.js +67 -0
  130. package/handlers/routers/user.js +63 -0
  131. package/handlers/routers/websocket.js +57 -0
  132. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  133. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  134. package/handlers/workers/db-migration.js +352 -0
  135. package/handlers/workers/integration-defined-workers.js +27 -0
  136. package/index.js +77 -22
  137. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  138. package/integrations/index.js +12 -10
  139. package/integrations/integration-base.js +326 -55
  140. package/integrations/integration-router.js +374 -179
  141. package/integrations/options.js +1 -1
  142. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  143. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  144. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  145. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  146. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  147. package/integrations/repositories/integration-mapping-repository.js +156 -0
  148. package/integrations/repositories/integration-repository-documentdb.js +210 -0
  149. package/integrations/repositories/integration-repository-factory.js +51 -0
  150. package/integrations/repositories/integration-repository-interface.js +127 -0
  151. package/integrations/repositories/integration-repository-mongo.js +303 -0
  152. package/integrations/repositories/integration-repository-postgres.js +352 -0
  153. package/integrations/repositories/process-repository-documentdb.js +243 -0
  154. package/integrations/repositories/process-repository-factory.js +53 -0
  155. package/integrations/repositories/process-repository-interface.js +90 -0
  156. package/integrations/repositories/process-repository-mongo.js +190 -0
  157. package/integrations/repositories/process-repository-postgres.js +217 -0
  158. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  159. package/integrations/tests/doubles/dummy-integration-class.js +105 -0
  160. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  161. package/integrations/use-cases/create-integration.js +83 -0
  162. package/integrations/use-cases/create-process.js +128 -0
  163. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  164. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  165. package/integrations/use-cases/get-integration-for-user.js +78 -0
  166. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  167. package/integrations/use-cases/get-integration-instance.js +83 -0
  168. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  169. package/integrations/use-cases/get-possible-integrations.js +27 -0
  170. package/integrations/use-cases/get-process.js +87 -0
  171. package/integrations/use-cases/index.js +19 -0
  172. package/integrations/use-cases/load-integration-context.js +71 -0
  173. package/integrations/use-cases/update-integration-messages.js +44 -0
  174. package/integrations/use-cases/update-integration-status.js +32 -0
  175. package/integrations/use-cases/update-integration.js +92 -0
  176. package/integrations/use-cases/update-process-metrics.js +201 -0
  177. package/integrations/use-cases/update-process-state.js +119 -0
  178. package/integrations/utils/map-integration-dto.js +37 -0
  179. package/jest-global-setup-noop.js +3 -0
  180. package/jest-global-teardown-noop.js +3 -0
  181. package/logs/logger.js +0 -4
  182. package/{module-plugin → modules}/entity.js +1 -1
  183. package/{module-plugin → modules}/index.js +0 -8
  184. package/modules/module-factory.js +56 -0
  185. package/modules/module.js +221 -0
  186. package/modules/repositories/module-repository-documentdb.js +307 -0
  187. package/modules/repositories/module-repository-factory.js +40 -0
  188. package/modules/repositories/module-repository-interface.js +129 -0
  189. package/modules/repositories/module-repository-mongo.js +377 -0
  190. package/modules/repositories/module-repository-postgres.js +426 -0
  191. package/modules/repositories/module-repository.js +316 -0
  192. package/modules/requester/api-key.js +52 -0
  193. package/{module-plugin → modules}/requester/requester.js +1 -0
  194. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  195. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  196. package/modules/tests/doubles/test-module-factory.js +16 -0
  197. package/modules/tests/doubles/test-module-repository.js +39 -0
  198. package/modules/use-cases/get-entities-for-user.js +32 -0
  199. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  200. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  201. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  202. package/modules/use-cases/get-module.js +74 -0
  203. package/modules/use-cases/process-authorization-callback.js +133 -0
  204. package/modules/use-cases/refresh-entity-options.js +72 -0
  205. package/modules/use-cases/test-module-auth.js +72 -0
  206. package/modules/utils/map-module-dto.js +18 -0
  207. package/package.json +82 -50
  208. package/prisma-mongodb/schema.prisma +360 -0
  209. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  210. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  211. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  212. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  213. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  214. package/prisma-postgresql/schema.prisma +343 -0
  215. package/queues/queuer-util.js +27 -22
  216. package/syncs/manager.js +468 -443
  217. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  218. package/syncs/repositories/sync-repository-factory.js +43 -0
  219. package/syncs/repositories/sync-repository-interface.js +109 -0
  220. package/syncs/repositories/sync-repository-mongo.js +239 -0
  221. package/syncs/repositories/sync-repository-postgres.js +319 -0
  222. package/syncs/sync.js +0 -1
  223. package/token/repositories/token-repository-documentdb.js +137 -0
  224. package/token/repositories/token-repository-factory.js +40 -0
  225. package/token/repositories/token-repository-interface.js +131 -0
  226. package/token/repositories/token-repository-mongo.js +219 -0
  227. package/token/repositories/token-repository-postgres.js +264 -0
  228. package/token/repositories/token-repository.js +219 -0
  229. package/types/core/index.d.ts +2 -2
  230. package/types/integrations/index.d.ts +2 -6
  231. package/types/module-plugin/index.d.ts +5 -59
  232. package/types/syncs/index.d.ts +0 -2
  233. package/user/repositories/user-repository-documentdb.js +441 -0
  234. package/user/repositories/user-repository-factory.js +52 -0
  235. package/user/repositories/user-repository-interface.js +201 -0
  236. package/user/repositories/user-repository-mongo.js +308 -0
  237. package/user/repositories/user-repository-postgres.js +360 -0
  238. package/user/tests/doubles/test-user-repository.js +72 -0
  239. package/user/use-cases/authenticate-user.js +127 -0
  240. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  241. package/user/use-cases/create-individual-user.js +61 -0
  242. package/user/use-cases/create-organization-user.js +47 -0
  243. package/user/use-cases/create-token-for-user-id.js +30 -0
  244. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  245. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  246. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  247. package/user/use-cases/login-user.js +122 -0
  248. package/user/user.js +125 -0
  249. package/utils/backend-path.js +38 -0
  250. package/utils/index.js +6 -0
  251. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  252. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  253. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  254. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  255. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  256. package/websocket/repositories/websocket-connection-repository.js +161 -0
  257. package/database/models/State.js +0 -9
  258. package/database/models/Token.js +0 -70
  259. package/database/mongo.js +0 -45
  260. package/encrypt/Cryptor.test.js +0 -32
  261. package/encrypt/encrypt.js +0 -132
  262. package/encrypt/encrypt.test.js +0 -1069
  263. package/errors/base-error.test.js +0 -32
  264. package/errors/fetch-error.test.js +0 -79
  265. package/errors/halt-error.test.js +0 -11
  266. package/errors/validation-errors.test.js +0 -120
  267. package/integrations/create-frigg-backend.js +0 -31
  268. package/integrations/integration-factory.js +0 -251
  269. package/integrations/integration-mapping.js +0 -43
  270. package/integrations/integration-model.js +0 -46
  271. package/integrations/integration-user.js +0 -144
  272. package/integrations/test/integration-base.test.js +0 -144
  273. package/lambda/TimeoutCatcher.test.js +0 -68
  274. package/logs/logger.test.js +0 -76
  275. package/module-plugin/auther.js +0 -393
  276. package/module-plugin/credential.js +0 -22
  277. package/module-plugin/entity-manager.js +0 -70
  278. package/module-plugin/manager.js +0 -169
  279. package/module-plugin/module-factory.js +0 -61
  280. package/module-plugin/requester/api-key.js +0 -36
  281. package/module-plugin/requester/requester.test.js +0 -28
  282. package/module-plugin/test/auther.test.js +0 -97
  283. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  284. /package/{module-plugin → modules}/requester/basic.js +0 -0
  285. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  286. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -1,7 +1,6 @@
1
1
  declare module "@friggframework/integrations" {
2
2
  import { Delegate, IFriggDelegate } from "@friggframework/core";
3
3
  import { Model } from "mongoose";
4
- import { EntityManager } from "@friggframework/module-plugin";
5
4
 
6
5
  export class Integration extends Model {
7
6
  entities: any[];
@@ -19,8 +18,7 @@ declare module "@friggframework/integrations" {
19
18
 
20
19
  export class IntegrationManager
21
20
  extends Delegate
22
- implements IFriggIntegrationManager
23
- {
21
+ implements IFriggIntegrationManager {
24
22
  integration: Integration;
25
23
  primaryInstance: any;
26
24
  targetInstance: any;
@@ -56,7 +54,6 @@ declare module "@friggframework/integrations" {
56
54
  entities: { id: string; user: any },
57
55
  userId: string,
58
56
  config: any,
59
- EntityManager: EntityManager
60
57
  ): Promise<any>;
61
58
 
62
59
  static getFormattedIntegration(
@@ -116,8 +113,7 @@ declare module "@friggframework/integrations" {
116
113
  }
117
114
 
118
115
  export class IntegrationConfigManager
119
- implements IFriggIntegrationConfigManager
120
- {
116
+ implements IFriggIntegrationConfigManager {
121
117
  options: IntegrationOptions[];
122
118
  primary: any;
123
119
 
@@ -4,72 +4,21 @@ declare module "@friggframework/module-plugin" {
4
4
 
5
5
  export class Credential extends Model {
6
6
  userId: string;
7
- auth_is_valid: boolean;
8
- subType: string;
7
+ authIsValid: boolean;
9
8
  externalId: string;
10
9
  }
11
10
 
12
- export class EntityManager implements IFriggEntityManager {
13
- static primaryEntityClass: any;
14
- static entityManagerClasses: any[];
15
- static entityTypes: string[];
16
- static getEntitiesForUser(userId: string): Promise<any[]>;
17
- static checkIsValidType(entityType: string): boolean;
18
- static getEntityManagerClass(entityType?: string): any;
19
-
20
- static getEntityManagerInstanceFromEntityId(
21
- entityId: string,
22
- userId: string
23
- ): Promise<any>;
24
- }
25
-
26
- interface IFriggEntityManager {}
11
+ interface IFriggEntityManager { }
27
12
 
28
13
  export class Entity extends Model {
29
14
  credentialId: string;
30
- subType: string;
31
15
  userId: string;
32
16
  name: string;
33
17
  externalId: string;
34
18
  }
35
19
 
36
20
  export type MappedEntity = Entity & { id: string; type: any };
37
- export class ModuleManager extends Delegate implements IFriggModuleManager {
38
- static Entity: Entity;
39
- static Credential: Credential;
40
-
41
- constructor(params: { userId: string });
42
21
 
43
- static getName(): any;
44
- static getInstance(params: any): Promise<any>;
45
- static getEntitiesForUserId(userId: string): Promise<MappedEntity[]>;
46
-
47
- batchCreateSyncObjects(syncObjects: any, syncManager: any): Promise<any>;
48
- batchUpdateSyncObjects(syncObjects: any, syncManager: any): Promise<any>;
49
- findOrCreateEntity(params: any): Promise<any>;
50
- getAllSyncObjects(SyncClass: any): Promise<any>;
51
- getAuthorizationRequirements(params: any): Promise<any>;
52
- getEntityId(): Promise<string>;
53
- getEntityOptions(): Promise<any>;
54
- markCredentialsInvalid(): Promise<Credential>;
55
- processAuthorizationCallback(params: any): Promise<any>;
56
- testAuth(params: any): Promise<any>;
57
- validateAuthorizationRequirements(): Promise<boolean>;
58
- }
59
-
60
- interface IFriggModuleManager extends IFriggDelegate {
61
- getEntityId(): Promise<string>;
62
- validateAuthorizationRequirements(): Promise<boolean>;
63
- getAuthorizationRequirements(params: any): Promise<any>;
64
- testAuth(params: any): Promise<any>;
65
- processAuthorizationCallback(params: any): Promise<any>;
66
- getEntityOptions(): Promise<any>;
67
- findOrCreateEntity(params: any): Promise<any>;
68
- getAllSyncObjects(SyncClass: any): Promise<any>;
69
- batchCreateSyncObjects(syncObjects: any, syncManager: any): Promise<any>;
70
- batchUpdateSyncObjects(syncObjects: any, syncManager: any): Promise<any>;
71
- markCredentialsInvalid(): Promise<Credential>;
72
- }
73
22
 
74
23
  export class Requester implements IFriggRequester {
75
24
  DLGT_INVALID_AUTH: string;
@@ -138,8 +87,7 @@ declare module "@friggframework/module-plugin" {
138
87
 
139
88
  export class ApiKeyRequester
140
89
  extends Requester
141
- implements IFriggApiKeyRequester
142
- {
90
+ implements IFriggApiKeyRequester {
143
91
  API_KEY_NAME: string;
144
92
  API_KEY_VALUE: any;
145
93
 
@@ -160,8 +108,7 @@ declare module "@friggframework/module-plugin" {
160
108
 
161
109
  export class BasicAuthRequester
162
110
  extends Requester
163
- implements IFriggBasicAuthRequester
164
- {
111
+ implements IFriggBasicAuthRequester {
165
112
  password: string;
166
113
  username: string;
167
114
 
@@ -189,8 +136,7 @@ declare module "@friggframework/module-plugin" {
189
136
 
190
137
  export class OAuth2Requester
191
138
  extends Requester
192
- implements IFriggOAuth2Requester
193
- {
139
+ implements IFriggOAuth2Requester {
194
140
  DLGT_TOKEN_DEAUTHORIZED: string;
195
141
  DLGT_TOKEN_UPDATE: string;
196
142
  accessTokenExpire: any;
@@ -28,7 +28,6 @@ declare module "@friggframework/syncs/manager" {
28
28
  confirmCreate(
29
29
  syncObj: Sync,
30
30
  createdId: string,
31
- moduleManager: any
32
31
  ): Promise<any>;
33
32
  confirmUpdate(syncObj: Sync): Promise<any>;
34
33
  createSyncDBObject(objArr: any[], entities: any[]): Promise<any>;
@@ -50,7 +49,6 @@ declare module "@friggframework/syncs/manager" {
50
49
  confirmCreate(
51
50
  syncObj: Sync,
52
51
  createdId: string,
53
- moduleManager: any
54
52
  ): Promise<any>;
55
53
  confirmUpdate(syncObj: Sync): Promise<any>;
56
54
  }
@@ -0,0 +1,441 @@
1
+ const bcrypt = require('bcryptjs');
2
+ const { prisma } = require('../../database/prisma');
3
+ const {
4
+ toObjectId,
5
+ fromObjectId,
6
+ findOne,
7
+ insertOne,
8
+ updateOne,
9
+ deleteOne,
10
+ } = require('../../database/documentdb-utils');
11
+ const {
12
+ createTokenRepository,
13
+ } = require('../../token/repositories/token-repository-factory');
14
+ const { UserRepositoryInterface } = require('./user-repository-interface');
15
+ const { ClientSafeError } = require('../../errors');
16
+ const {
17
+ DocumentDBEncryptionService,
18
+ } = require('../../database/documentdb-encryption-service');
19
+
20
+ /**
21
+ * User repository for DocumentDB.
22
+ * Uses DocumentDBEncryptionService for field-level encryption.
23
+ *
24
+ * Encrypted fields: User.hashword
25
+ *
26
+ * @see DocumentDBEncryptionService
27
+ * @see encryption-schema-registry.js
28
+ */
29
+ class UserRepositoryDocumentDB extends UserRepositoryInterface {
30
+ constructor() {
31
+ super();
32
+ this.prisma = prisma;
33
+ this.tokenRepository = createTokenRepository();
34
+ this.encryptionService = new DocumentDBEncryptionService();
35
+ }
36
+
37
+ async getSessionToken(token) {
38
+ const jsonToken =
39
+ this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
40
+ const sessionToken = await this.tokenRepository.validateAndGetToken(
41
+ jsonToken
42
+ );
43
+ return sessionToken;
44
+ }
45
+
46
+ async findOrganizationUserById(userId) {
47
+ const doc = await findOne(this.prisma, 'User', {
48
+ _id: toObjectId(userId),
49
+ type: 'ORGANIZATION',
50
+ });
51
+ const decrypted = await this.encryptionService.decryptFields(
52
+ 'User',
53
+ doc
54
+ );
55
+ return this._mapUser(decrypted);
56
+ }
57
+
58
+ async findIndividualUserById(userId) {
59
+ const doc = await findOne(this.prisma, 'User', {
60
+ _id: toObjectId(userId),
61
+ type: 'INDIVIDUAL',
62
+ });
63
+ const decrypted = await this.encryptionService.decryptFields(
64
+ 'User',
65
+ doc
66
+ );
67
+ return this._mapUser(decrypted);
68
+ }
69
+
70
+ async createToken(userId, rawToken, minutes = 120) {
71
+ const createdToken = await this.tokenRepository.createTokenWithExpire(
72
+ fromObjectId(toObjectId(userId)),
73
+ rawToken,
74
+ minutes
75
+ );
76
+ return this.tokenRepository.createBase64BufferToken(
77
+ createdToken,
78
+ rawToken
79
+ );
80
+ }
81
+
82
+ async createIndividualUser(params) {
83
+ const now = new Date();
84
+ const document = {
85
+ type: 'INDIVIDUAL',
86
+ email: params.email ?? null,
87
+ username: params.username ?? null,
88
+ appUserId: params.appUserId ?? null,
89
+ organizationId: params.organization
90
+ ? toObjectId(params.organization)
91
+ : params.organizationId
92
+ ? toObjectId(params.organizationId)
93
+ : null,
94
+ createdAt: now,
95
+ updatedAt: now,
96
+ };
97
+
98
+ if (
99
+ params.hashword !== undefined &&
100
+ params.hashword !== null &&
101
+ params.hashword !== ''
102
+ ) {
103
+ if (typeof params.hashword !== 'string') {
104
+ throw new ClientSafeError('Password must be a string', 400);
105
+ }
106
+
107
+ if (params.hashword.startsWith('$2')) {
108
+ throw new Error(
109
+ 'Password appears to be already hashed. Pass plain text password only.'
110
+ );
111
+ }
112
+
113
+ // Bcrypt hash the password
114
+ document.hashword = await bcrypt.hash(params.hashword, 10);
115
+ }
116
+
117
+ // Encrypt sensitive fields before insert
118
+ const encryptedDocument = await this.encryptionService.encryptFields(
119
+ 'User',
120
+ document
121
+ );
122
+ const insertedId = await insertOne(
123
+ this.prisma,
124
+ 'User',
125
+ encryptedDocument
126
+ );
127
+ const created = await findOne(this.prisma, 'User', { _id: insertedId });
128
+
129
+ // Defensive check: verify document was found after insert
130
+ if (!created) {
131
+ console.error(
132
+ '[UserRepositoryDocumentDB] User not found after insert',
133
+ {
134
+ insertedId: fromObjectId(insertedId),
135
+ params: {
136
+ username: params.username,
137
+ appUserId: params.appUserId,
138
+ email: params.email,
139
+ },
140
+ }
141
+ );
142
+ throw new Error(
143
+ 'Failed to create individual user: Document not found after insert. ' +
144
+ 'This indicates a database consistency issue.'
145
+ );
146
+ }
147
+
148
+ // Decrypt sensitive fields after read
149
+ const decrypted = await this.encryptionService.decryptFields(
150
+ 'User',
151
+ created
152
+ );
153
+
154
+ return this._mapUser(decrypted);
155
+ }
156
+
157
+ async createOrganizationUser(params) {
158
+ const now = new Date();
159
+ const document = {
160
+ type: 'ORGANIZATION',
161
+ appOrgId: params.appOrgId ?? null,
162
+ name: params.name ?? null,
163
+ createdAt: now,
164
+ updatedAt: now,
165
+ };
166
+
167
+ // Encrypt sensitive fields before insert (consistency with individual user)
168
+ const encryptedDocument = await this.encryptionService.encryptFields(
169
+ 'User',
170
+ document
171
+ );
172
+ const insertedId = await insertOne(
173
+ this.prisma,
174
+ 'User',
175
+ encryptedDocument
176
+ );
177
+ const created = await findOne(this.prisma, 'User', { _id: insertedId });
178
+
179
+ // Defensive check: verify document was found after insert
180
+ if (!created) {
181
+ console.error(
182
+ '[UserRepositoryDocumentDB] Organization user not found after insert',
183
+ {
184
+ insertedId: fromObjectId(insertedId),
185
+ params: {
186
+ appOrgId: params.appOrgId,
187
+ name: params.name,
188
+ },
189
+ }
190
+ );
191
+ throw new Error(
192
+ 'Failed to create organization user: Document not found after insert. ' +
193
+ 'This indicates a database consistency issue.'
194
+ );
195
+ }
196
+
197
+ // Decrypt sensitive fields after read
198
+ const decrypted = await this.encryptionService.decryptFields(
199
+ 'User',
200
+ created
201
+ );
202
+ return this._mapUser(decrypted);
203
+ }
204
+
205
+ async findIndividualUserByUsername(username) {
206
+ const doc = await findOne(this.prisma, 'User', {
207
+ type: 'INDIVIDUAL',
208
+ username,
209
+ });
210
+ const decrypted = await this.encryptionService.decryptFields(
211
+ 'User',
212
+ doc
213
+ );
214
+ return this._mapUser(decrypted);
215
+ }
216
+
217
+ async findIndividualUserByAppUserId(appUserId) {
218
+ const doc = await findOne(this.prisma, 'User', {
219
+ type: 'INDIVIDUAL',
220
+ appUserId,
221
+ });
222
+ const decrypted = await this.encryptionService.decryptFields(
223
+ 'User',
224
+ doc
225
+ );
226
+ return this._mapUser(decrypted);
227
+ }
228
+
229
+ async findOrganizationUserByAppOrgId(appOrgId) {
230
+ const doc = await findOne(this.prisma, 'User', {
231
+ type: 'ORGANIZATION',
232
+ appOrgId,
233
+ });
234
+ const decrypted = await this.encryptionService.decryptFields(
235
+ 'User',
236
+ doc
237
+ );
238
+ return this._mapUser(decrypted);
239
+ }
240
+
241
+ async findIndividualUserByEmail(email) {
242
+ const doc = await findOne(this.prisma, 'User', {
243
+ type: 'INDIVIDUAL',
244
+ email,
245
+ });
246
+ const decrypted = await this.encryptionService.decryptFields(
247
+ 'User',
248
+ doc
249
+ );
250
+ return this._mapUser(decrypted);
251
+ }
252
+
253
+ async updateIndividualUser(userId, updates) {
254
+ const objectId = toObjectId(userId);
255
+ if (!objectId) return null;
256
+
257
+ const payload = await this._prepareUpdatePayload(updates);
258
+ payload.updatedAt = new Date();
259
+
260
+ // Encrypt sensitive fields before update
261
+ const encryptedPayload = await this.encryptionService.encryptFields(
262
+ 'User',
263
+ payload
264
+ );
265
+
266
+ await updateOne(
267
+ this.prisma,
268
+ 'User',
269
+ { _id: objectId, type: 'INDIVIDUAL' },
270
+ { $set: encryptedPayload }
271
+ );
272
+
273
+ const updated = await findOne(this.prisma, 'User', { _id: objectId });
274
+
275
+ // Defensive check: verify document was found after update
276
+ if (!updated) {
277
+ console.error(
278
+ '[UserRepositoryDocumentDB] Individual user not found after update',
279
+ {
280
+ userId: fromObjectId(objectId),
281
+ updates,
282
+ }
283
+ );
284
+ throw new Error(
285
+ 'Failed to update individual user: Document not found after update. ' +
286
+ 'This indicates a database consistency issue.'
287
+ );
288
+ }
289
+
290
+ const decrypted = await this.encryptionService.decryptFields(
291
+ 'User',
292
+ updated
293
+ );
294
+ return this._mapUser(decrypted);
295
+ }
296
+
297
+ async updateOrganizationUser(userId, updates) {
298
+ const objectId = toObjectId(userId);
299
+ if (!objectId) return null;
300
+
301
+ const payload = { ...updates, updatedAt: new Date() };
302
+
303
+ const encryptedPayload = await this.encryptionService.encryptFields(
304
+ 'User',
305
+ payload
306
+ );
307
+
308
+ await updateOne(
309
+ this.prisma,
310
+ 'User',
311
+ { _id: objectId, type: 'ORGANIZATION' },
312
+ { $set: encryptedPayload }
313
+ );
314
+
315
+ const updated = await findOne(this.prisma, 'User', { _id: objectId });
316
+
317
+ if (!updated) {
318
+ console.error(
319
+ '[UserRepositoryDocumentDB] Organization user not found after update',
320
+ {
321
+ userId: fromObjectId(objectId),
322
+ updates,
323
+ }
324
+ );
325
+ throw new Error(
326
+ 'Failed to update organization user: Document not found after update. ' +
327
+ 'This indicates a database consistency issue.'
328
+ );
329
+ }
330
+
331
+ const decrypted = await this.encryptionService.decryptFields(
332
+ 'User',
333
+ updated
334
+ );
335
+ return this._mapUser(decrypted);
336
+ }
337
+
338
+ async deleteUser(userId) {
339
+ const objectId = toObjectId(userId);
340
+ if (!objectId) return false;
341
+
342
+ const result = await deleteOne(this.prisma, 'User', { _id: objectId });
343
+ const deleted = result?.n ?? 0;
344
+ return deleted > 0;
345
+ }
346
+
347
+ _mapUser(doc) {
348
+ if (!doc) {
349
+ console.warn(
350
+ '[UserRepositoryDocumentDB] _mapUser received null/undefined document'
351
+ );
352
+ return null;
353
+ }
354
+
355
+ // Use optional chaining for robustness
356
+ return {
357
+ id: fromObjectId(doc?._id),
358
+ type: doc?.type ?? null,
359
+ email: doc?.email ?? null,
360
+ username: doc?.username ?? null,
361
+ hashword: doc?.hashword ?? null,
362
+ appUserId: doc?.appUserId ?? null,
363
+ organizationId: doc?.organizationId
364
+ ? fromObjectId(doc.organizationId)
365
+ : null,
366
+ appOrgId: doc?.appOrgId ?? null,
367
+ name: doc?.name ?? null,
368
+ createdAt: this._parseDate(doc?.createdAt),
369
+ updatedAt: this._parseDate(doc?.updatedAt),
370
+ };
371
+ }
372
+
373
+ async _prepareUpdatePayload(updates = {}) {
374
+ const payload = { ...updates };
375
+
376
+ if (
377
+ payload.hashword !== undefined &&
378
+ payload.hashword !== null &&
379
+ payload.hashword !== ''
380
+ ) {
381
+ if (typeof payload.hashword !== 'string') {
382
+ throw new ClientSafeError('Password must be a string', 400);
383
+ }
384
+
385
+ if (payload.hashword.startsWith('$2')) {
386
+ throw new Error(
387
+ 'Password appears to be already hashed. Pass plain text password only.'
388
+ );
389
+ }
390
+
391
+ payload.hashword = await bcrypt.hash(payload.hashword, 10);
392
+ }
393
+
394
+ if (payload.organization !== undefined) {
395
+ payload.organizationId = toObjectId(payload.organization);
396
+ delete payload.organization;
397
+ }
398
+
399
+ if (payload.organizationId !== undefined) {
400
+ payload.organizationId = payload.organizationId
401
+ ? toObjectId(payload.organizationId)
402
+ : null;
403
+ }
404
+
405
+ return payload;
406
+ }
407
+
408
+ /**
409
+ * Parse date value safely, returning undefined for invalid dates
410
+ * @private
411
+ * @param {*} value - Date value from database
412
+ * @returns {Date|undefined} Valid Date object or undefined
413
+ */
414
+ _parseDate(value) {
415
+ if (!value) return undefined;
416
+ const date = new Date(value);
417
+ return isNaN(date.getTime()) ? undefined : date;
418
+ }
419
+
420
+ /**
421
+ * Link an individual user to an organization user
422
+ * @param {string} individualUserId - Individual user ID (MongoDB ObjectId string)
423
+ * @param {string} organizationUserId - Organization user ID (MongoDB ObjectId string)
424
+ * @returns {Promise<Object>} Updated individual user object
425
+ */
426
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
427
+ const doc = await updateOne(
428
+ this.prisma,
429
+ 'User',
430
+ { _id: toObjectId(individualUserId), type: 'INDIVIDUAL' },
431
+ { $set: { organizationId: toObjectId(organizationUserId) } }
432
+ );
433
+ const decrypted = await this.encryptionService.decryptFields(
434
+ 'User',
435
+ doc
436
+ );
437
+ return this._mapUser(decrypted);
438
+ }
439
+ }
440
+
441
+ module.exports = { UserRepositoryDocumentDB };
@@ -0,0 +1,52 @@
1
+ const { UserRepositoryMongo } = require('./user-repository-mongo');
2
+ const { UserRepositoryPostgres } = require('./user-repository-postgres');
3
+ const { UserRepositoryDocumentDB } = require('./user-repository-documentdb');
4
+ const databaseConfig = require('../../database/config');
5
+
6
+ /**
7
+ * User Repository Factory
8
+ * Creates the appropriate repository adapter based on database type
9
+ *
10
+ * Database-specific implementations:
11
+ * - MongoDB: Uses String IDs (ObjectId), no conversion needed
12
+ * - PostgreSQL: Uses Int IDs, converts String ↔ Int
13
+ *
14
+ * All repository methods return String IDs regardless of database type,
15
+ * ensuring application layer consistency.
16
+ *
17
+ * Usage:
18
+ * ```javascript
19
+ * const repository = createUserRepository();
20
+ * const user = await repository.findIndividualUserById(id); // ID is string
21
+ * const orgUser = await repository.findOrganizationUserById(id); // ID is string
22
+ * ```
23
+ *
24
+ * @returns {UserRepositoryInterface} Configured repository adapter
25
+ */
26
+ function createUserRepository() {
27
+ const dbType = databaseConfig.DB_TYPE;
28
+
29
+ switch (dbType) {
30
+ case 'mongodb':
31
+ return new UserRepositoryMongo();
32
+
33
+ case 'postgresql':
34
+ return new UserRepositoryPostgres();
35
+
36
+ case 'documentdb':
37
+ return new UserRepositoryDocumentDB();
38
+
39
+ default:
40
+ throw new Error(
41
+ `Unsupported DB_TYPE: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
42
+ );
43
+ }
44
+ }
45
+
46
+ module.exports = {
47
+ createUserRepository,
48
+ // Export adapters for direct testing
49
+ UserRepositoryMongo,
50
+ UserRepositoryPostgres,
51
+ UserRepositoryDocumentDB,
52
+ };