@friggframework/core 2.0.0--canary.682.451c957.0 → 2.0.0--canary.702.d5e6de1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CLAUDE.md +0 -2
  2. package/README.md +0 -105
  3. package/application/commands/integration-commands.js +10 -20
  4. package/application/commands/user-commands.js +5 -17
  5. package/application/index.js +2 -6
  6. package/credential/repositories/credential-repository-documentdb.js +0 -34
  7. package/credential/repositories/credential-repository-interface.js +0 -15
  8. package/credential/repositories/credential-repository-mongo.js +0 -23
  9. package/credential/repositories/credential-repository-postgres.js +0 -28
  10. package/database/documentdb-utils.js +0 -2
  11. package/database/encryption/encryption-schema-registry.js +0 -13
  12. package/errors/index.js +4 -0
  13. package/errors/mapping-already-exists-error.js +14 -0
  14. package/generated/prisma-mongodb/edge.js +4 -31
  15. package/generated/prisma-mongodb/index-browser.js +1 -28
  16. package/generated/prisma-mongodb/index.d.ts +74 -3463
  17. package/generated/prisma-mongodb/index.js +4 -31
  18. package/generated/prisma-mongodb/package.json +1 -1
  19. package/generated/prisma-mongodb/schema.prisma +0 -74
  20. package/generated/prisma-mongodb/wasm.js +4 -31
  21. package/generated/prisma-postgresql/edge.js +4 -31
  22. package/generated/prisma-postgresql/index-browser.js +1 -28
  23. package/generated/prisma-postgresql/index.d.ts +51 -3631
  24. package/generated/prisma-postgresql/index.js +4 -31
  25. package/generated/prisma-postgresql/package.json +1 -1
  26. package/generated/prisma-postgresql/schema.prisma +0 -70
  27. package/generated/prisma-postgresql/wasm.js +4 -31
  28. package/handlers/WEBHOOKS.md +29 -0
  29. package/handlers/app-definition-loader.js +16 -34
  30. package/handlers/app-handler-helpers.js +3 -25
  31. package/handlers/backend-utils.js +25 -31
  32. package/handlers/middleware/admin-auth.js +1 -14
  33. package/handlers/routers/auth.js +3 -11
  34. package/handlers/routers/health.js +4 -11
  35. package/handlers/workers/integration-defined-workers.js +2 -5
  36. package/index.js +2 -23
  37. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +67 -13
  38. package/integrations/index.js +0 -1
  39. package/integrations/integration-base.js +29 -0
  40. package/integrations/integration-router.js +98 -136
  41. package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
  42. package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
  43. package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
  44. package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
  45. package/integrations/repositories/integration-mapping-repository.js +29 -0
  46. package/integrations/repositories/integration-repository-documentdb.js +3 -35
  47. package/integrations/repositories/integration-repository-interface.js +1 -17
  48. package/integrations/repositories/integration-repository-mongo.js +77 -60
  49. package/integrations/repositories/integration-repository-postgres.js +76 -62
  50. package/integrations/repositories/process-repository-mongo.js +1 -1
  51. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  52. package/integrations/use-cases/create-integration.js +20 -55
  53. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  54. package/integrations/use-cases/index.js +1 -5
  55. package/integrations/utils/map-integration-dto.js +15 -47
  56. package/modules/index.js +0 -14
  57. package/modules/repositories/module-repository-documentdb.js +4 -48
  58. package/modules/repositories/module-repository-interface.js +2 -19
  59. package/modules/repositories/module-repository-mongo.js +73 -65
  60. package/modules/repositories/module-repository-postgres.js +73 -64
  61. package/modules/requester/requester.js +2 -27
  62. package/modules/use-cases/get-entities-for-user.js +5 -16
  63. package/modules/use-cases/get-entity-options-by-id.js +8 -16
  64. package/modules/use-cases/get-module.js +11 -27
  65. package/modules/use-cases/process-authorization-callback.js +18 -52
  66. package/modules/use-cases/refresh-entity-options.js +8 -16
  67. package/modules/use-cases/test-module-auth.js +8 -16
  68. package/modules/utils/map-module-dto.js +1 -7
  69. package/package.json +8 -7
  70. package/prisma-mongodb/schema.prisma +0 -74
  71. package/prisma-postgresql/schema.prisma +0 -70
  72. package/types/index.d.ts +0 -2
  73. package/user/repositories/user-repository-documentdb.js +2 -27
  74. package/user/repositories/user-repository-interface.js +0 -18
  75. package/user/repositories/user-repository-mongo.js +0 -24
  76. package/user/repositories/user-repository-postgres.js +0 -18
  77. package/user/use-cases/get-user-from-adopter-jwt.js +125 -86
  78. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  79. package/user/user.js +0 -27
  80. package/credential/use-cases/credential-operations-for-user.js +0 -117
  81. package/credential/use-cases/reauthorize-credential-for-user.js +0 -66
  82. package/database/organization-where.js +0 -18
  83. package/database/unique-violation.js +0 -25
  84. package/integrations/repositories/integration-not-found.js +0 -14
  85. package/integrations/use-cases/integration-operations-for-user.js +0 -246
  86. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  87. package/management-api/create-management-api-router.js +0 -49
  88. package/management-api/error-response.js +0 -116
  89. package/management-api/management-api-config.js +0 -85
  90. package/management-api/meta-router.js +0 -57
  91. package/management-api/openapi/build-openapi.js +0 -144
  92. package/management-api/openapi/components.js +0 -407
  93. package/management-api/openapi/v1-operations.js +0 -83
  94. package/management-api/openapi/v2-operations.js +0 -261
  95. package/management-api/route-registry.js +0 -294
  96. package/management-api/test-support/http-client.js +0 -39
  97. package/management-api/use-cases/get-management-api-meta.js +0 -57
  98. package/management-api/v1-deprecation.js +0 -120
  99. package/management-api/v2/create-v2-router.js +0 -85
  100. package/management-api/v2/dependencies.js +0 -279
  101. package/management-api/v2/dto.js +0 -143
  102. package/management-api/v2/handlers/authorization.js +0 -94
  103. package/management-api/v2/handlers/credentials.js +0 -29
  104. package/management-api/v2/handlers/entities.js +0 -77
  105. package/management-api/v2/handlers/integrations.js +0 -128
  106. package/management-api/v2/handlers/proxy.js +0 -34
  107. package/management-api/v2/request.js +0 -85
  108. package/management-api/v2/test-support/fake-dependencies.js +0 -103
  109. package/modules/domain/authorization-session.js +0 -144
  110. package/modules/entity-scope.js +0 -15
  111. package/modules/entity-type-registry.js +0 -311
  112. package/modules/proxy/proxy-errors.js +0 -20
  113. package/modules/proxy/proxy-path.js +0 -179
  114. package/modules/proxy/proxy-policy.js +0 -248
  115. package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
  116. package/modules/repositories/authorization-session-repository-factory.js +0 -31
  117. package/modules/repositories/authorization-session-repository-interface.js +0 -47
  118. package/modules/repositories/authorization-session-repository-prisma.js +0 -87
  119. package/modules/requester/proxy-request.js +0 -244
  120. package/modules/scope-config.js +0 -155
  121. package/modules/scope-policy.js +0 -80
  122. package/modules/scope-resolution.js +0 -277
  123. package/modules/scope-visibility.js +0 -165
  124. package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
  125. package/modules/tests/doubles/ownership-matching.js +0 -18
  126. package/modules/use-cases/authorize-entity-for-user.js +0 -59
  127. package/modules/use-cases/create-entity-from-credential.js +0 -113
  128. package/modules/use-cases/entity-operations-for-user.js +0 -157
  129. package/modules/use-cases/entity-types.js +0 -139
  130. package/modules/use-cases/execute-entity-proxy-request.js +0 -254
  131. package/modules/use-cases/get-authorization-requirements.js +0 -100
  132. package/modules/use-cases/get-entity-module-for-user.js +0 -106
  133. package/modules/use-cases/multi-step-authorization.js +0 -326
  134. package/modules/utils/find-module-definition.js +0 -23
  135. package/modules/utils/public-entity.js +0 -59
  136. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
  137. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  138. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  139. package/types/modules/index.d.ts +0 -155
  140. package/types/user/index.d.ts +0 -117
  141. package/user/adopter-jwt-config.js +0 -177
  142. package/user/adopter-jwt-errors.js +0 -59
  143. package/user/adopter-token-verifier.js +0 -122
  144. package/user/app-user-helpers.js +0 -97
  145. package/user/create-user-authentication.js +0 -64
  146. package/user/membership.js +0 -51
  147. package/user/repositories/organization-membership-record.js +0 -19
  148. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  149. package/user/repositories/organization-membership-repository-factory.js +0 -42
  150. package/user/repositories/organization-membership-repository-interface.js +0 -57
  151. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  152. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  153. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  154. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  155. package/user/use-cases/ensure-organization-membership.js +0 -92
  156. package/user/use-cases/find-individual-users-by-organization.js +0 -54
package/CLAUDE.md CHANGED
@@ -332,14 +332,12 @@ class MyIntegration extends IntegrationBase {
332
332
  - Password-based (bcrypt hashed)
333
333
  - Token-based (Bearer tokens)
334
334
  - App-based (external app user IDs)
335
- - Adopter JWT (`authModes.adopterJwt`, `jwtConfig`; `jose`, JWKS or HMAC; see `README.md`)
336
335
 
337
336
  **Use Cases**:
338
337
  - `login-user.js` - User authentication
339
338
  - `create-individual-user.js` - Create personal account
340
339
  - `create-organization-user.js` - Create business account
341
340
  - `get-user-from-bearer-token.js` - Token authentication
342
- - `get-user-from-adopter-jwt.js` - Adopter JWT verification, find-or-create (ADR-056 §2); `adopter-jwt-config.js` validates `jwtConfig` at load
343
341
 
344
342
  **Repositories**:
345
343
  - `user-repository-factory.js` - Creates database-specific repositories
package/README.md CHANGED
@@ -305,7 +305,6 @@ Comprehensive user authentication and authorization system supporting both indiv
305
305
  - **Password-based**: Traditional username/password authentication
306
306
  - **Token-based**: Bearer token authentication with session management
307
307
  - **App-based**: External app user ID authentication (passwordless)
308
- - **Adopter token (JWT)**: A token issued by your own identity system, verified by Frigg and safe to send from a browser (see below)
309
308
 
310
309
  **Usage:**
311
310
  ```javascript
@@ -353,110 +352,6 @@ console.log('Individual user:', user.getIndividualUser());
353
352
  console.log('Organization user:', user.getOrganizationUser());
354
353
  ```
355
354
 
356
- #### Adopter token (JWT) authentication
357
-
358
- For a multi-tenant product (ADR-056 §2), let the browser send the token your identity system
359
- already issues. Frigg verifies the signature and claims, then finds or creates the individual
360
- user and the organization user the token names. Unlike `sharedSecret` (`x-frigg-*` headers), a
361
- token cannot name a user other than the one it was issued for, so it is safe in a browser.
362
-
363
- ```javascript
364
- // backend/index.js
365
- const appDefinition = {
366
- user: {
367
- primary: 'organization',
368
- individualUserRequired: true,
369
- organizationUserRequired: true,
370
- authModes: { adopterJwt: true, friggToken: false, sharedSecret: false },
371
- jwtConfig: {
372
- jwksUri: 'https://login.example.com/.well-known/jwks.json',
373
- issuer: 'https://login.example.com/',
374
- audience: 'my-frigg-api', // required with jwksUri
375
- },
376
- },
377
- };
378
- ```
379
-
380
- The client sends `Authorization: Bearer <token>`. `x-frigg-appuserid` / `x-frigg-apporgid`, if
381
- also sent, must match the token or the request is refused with 403. That match rule lives in
382
- `user/use-cases/authenticate-user.js` (`validateUserMatch`), not in the token verifier. A bearer
383
- value that is not a three-part JWT is not offered to the verifier at all; it falls through to
384
- `friggToken` (or is a 401 when that mode is off).
385
-
386
- | `jwtConfig` field | Default | Meaning |
387
- |---|---|---|
388
- | `jwksUri` | | Remote JWKS (rotating keys, one per verifier, cached by jose). Must be `https`; `http` only for `localhost`, `127.0.0.0/8` or `::1`. Set exactly one of this and `secret`. |
389
- | `secret` | | Shared HMAC secret. Read it from an environment variable. |
390
- | `issuer` | not checked | Required `iss`. With `jwksUri`, a startup warning is logged when it is unset. |
391
- | `audience` | not checked with `secret`; **required with `jwksUri`** | Accepted `aud` (a string or a list). |
392
- | `algorithms` | `['ES256','RS256']` with `jwksUri`, `['HS256']` with `secret` | Allow-list. HS* is only accepted with `secret`, never with `jwksUri`. The older single `algorithm` is a deprecated alias; setting both is an error. |
393
- | `clockToleranceSec` | `30` | Skew allowed on `exp` and `nbf`. |
394
- | `userIdClaim` | `sub` | App user id (required in the token). |
395
- | `orgIdClaim` | `org_id` | App organization id (optional). |
396
- | `roleClaim` | `org_role` | Exposed as `user.getOrgRole()` for the request, and stored as the `orgRole` of a person's membership the first time they join that organization (`MEMBER` when the token names none). |
397
-
398
- Rules worth knowing:
399
-
400
- - Tokens must carry `exp`. `alg: none` and any algorithm outside the allow-list are refused.
401
- - Startup fails (`loadAppDefinition` throws) when `adopterJwt` is on and the config is unusable:
402
- neither or both of `jwksUri` and `secret`, a `jwksUri` that is not https (or loopback http),
403
- no `audience` with `jwksUri`, or an algorithm list that does not fit the key type.
404
- - Failures are `401` with a stable `data.reason`: `token_expired`, `token_not_yet_valid`,
405
- `token_invalid_signature`, `token_wrong_issuer`, `token_wrong_audience`,
406
- `token_missing_user_claim`, `token_malformed`. The message never contains the token or its claims.
407
- Any other verification failure, such as an unreachable or unresolvable JWKS, is `503`
408
- `jwks_unavailable`, not a 401, so clients do not drop good sessions.
409
- - **The token's organization is the request's active organization**, and a person can belong to
410
- several (ADR-056 §3.6). The first time a person presents a token for an organization they are
411
- not a member of, Frigg adds an `OrganizationMembership` and the request runs as that
412
- organization (`user.getActiveOrganizationId()`; `user.getId()` under `primary: 'organization'`).
413
- Nothing is moved: `IndividualUser.organizationId` is a legacy pointer, set only on a person's
414
- first link and read as an implicit membership. The same applies to `x-frigg-apporgid` with
415
- `sharedSecret`. With `user.strictMembership: true` those headers cannot add anyone to an organization
416
- that already existed (400 for a non-member, a brand-new `appUserId` included); only an organization
417
- the request itself creates is allowed. `strictUserValidation` is a deprecated alias with that same
418
- meaning. See
419
- [Organization memberships](../../docs/guides/organization-memberships.md). A token naming a second
420
- organization is no longer a `403` (`multi_org_unsupported` is gone).
421
- - Two first logins for the same new person or organization, even in different organizations, both
422
- succeed: a create that loses to a unique constraint finds the winner's row.
423
-
424
- #### Entity scopes and ownership (ADR-056 §3)
425
-
426
- Who owns an entity (a connected account) or an integration follows its **scope**, not `user.primary`:
427
-
428
- | Scope | Owned by | Created with |
429
- |---|---|---|
430
- | `individual` | the person (`individualUser`) | the active organization (`organizationId`), or none under `individualAcross: 'all'` |
431
- | `organization` | the active organization's user | that organization |
432
- | `global` | nobody (the app owner) | none; refused by the tenant API (admin routes only) |
433
-
434
- The scope of a new connection is the first of: the request (`scope` on `POST /api/v2/authorize`, only from
435
- the deciding level's `allow` list), the integration binding (`Definition.modules[key].scope` or
436
- `Definition.entities[key].scope`), `Definition.ownership.scope`, the entity type
437
- (`appDefinition.entities[name].scope`), `appDefinition.ownership.scope`, then `organization` for an
438
- organization-primary app and `individual` otherwise. A setting is `'organization'` or
439
- `{ default: 'individual', allow: ['individual', 'organization'] }`.
440
-
441
- ```javascript
442
- const appDefinition = {
443
- user: { primary: 'individual', authModes: { adopterJwt: true } },
444
- ownership: { scope: 'individual', individualAcross: 'organization' }, // the defaults
445
- entities: {
446
- google: { definition: google, scope: 'individual' },
447
- xero: { definition: xero, scope: { default: 'organization', allow: ['organization', 'individual'] } },
448
- },
449
- integrations: [Sync], // Sync.Definition.ownership = { owner: 'derive' }
450
- };
451
- ```
452
-
453
- `GET /api/v2/entities` and `/integrations` return, for the active organization, its own
454
- rows and the caller's, each with `scope`, `owner`, `organizationId` and `access`, and take `?scope=` and
455
- `?owner=me|org`. An individual entity is only visible and usable in the organization it was connected in
456
- (or in all of them, with `individualAcross: 'all'`). Rows written before this existed keep working: see
457
- [Entity scopes](../../docs/guides/entity-scopes.md), which also covers the schema (`organizationId` on
458
- `Entity` and `Integration`, no `scope` column) and what is deferred.
459
-
460
355
  ### 8. Lambda Utilities (`/lambda`)
461
356
 
462
357
  AWS Lambda-specific utilities and helpers.
@@ -18,7 +18,7 @@ const {
18
18
  ListIntegrationsByEntityExternalIdUseCase,
19
19
  } = require('../../integrations/use-cases/list-integrations-by-entity-external-id');
20
20
  const {
21
- GetIntegrationsByUserId,
21
+ GetIntegrationsForUser,
22
22
  } = require('../../integrations/use-cases/get-integrations-for-user');
23
23
  const {
24
24
  CreateIntegration,
@@ -30,9 +30,8 @@ const {
30
30
  PatchIntegrationConfig,
31
31
  } = require('../../integrations/use-cases/patch-integration-config');
32
32
  const {
33
- getEntityTypeRegistry,
34
- getEntityTypeDefinitions,
35
- } = require('../../modules/entity-type-registry');
33
+ getModulesDefinitionFromIntegrationClasses,
34
+ } = require('../../integrations/utils/map-integration-dto');
36
35
 
37
36
  const ERROR_CODE_MAP = {
38
37
  ENTITY_NOT_FOUND: 401,
@@ -52,16 +51,7 @@ function mapErrorToResponse(error) {
52
51
  };
53
52
  }
54
53
 
55
- /**
56
- * @param {Object} [params]
57
- * @param {Function} [params.integrationClass]
58
- * @param {Array<object>} [params.moduleDefinitions] - the app's offered module
59
- * definitions (`loadAppDefinition().moduleDefinitions`). Integration code
60
- * calls this from a constructor with no app definition in hand, so when it
61
- * is omitted the commands hydrate with this integration's own modules only
62
- * (a registry built from `{ integrations: [integrationClass] }`).
63
- */
64
- function createIntegrationCommands({ integrationClass, moduleDefinitions } = {}) {
54
+ function createIntegrationCommands({ integrationClass } = {}) {
65
55
  // Always use Frigg's default repositories and use cases
66
56
  const integrationRepository = createIntegrationRepository();
67
57
 
@@ -118,13 +108,13 @@ function createIntegrationCommands({ integrationClass, moduleDefinitions } = {})
118
108
 
119
109
  const moduleRepository = createModuleRepository();
120
110
 
111
+ const moduleDefinitions = getModulesDefinitionFromIntegrationClasses([
112
+ integrationClass,
113
+ ]);
114
+
121
115
  const moduleFactory = new ModuleFactory({
122
116
  moduleRepository,
123
- moduleDefinitions:
124
- moduleDefinitions ??
125
- getEntityTypeDefinitions(
126
- getEntityTypeRegistry({ integrations: [integrationClass] })
127
- ),
117
+ moduleDefinitions,
128
118
  });
129
119
 
130
120
  const loadIntegrationContextUseCase = new LoadIntegrationContextUseCase({
@@ -152,7 +142,7 @@ function createIntegrationCommands({ integrationClass, moduleDefinitions } = {})
152
142
  moduleRepository,
153
143
  });
154
144
 
155
- const getIntegrationsForUserUseCase = new GetIntegrationsByUserId({
145
+ const getIntegrationsForUserUseCase = new GetIntegrationsForUser({
156
146
  integrationRepository,
157
147
  integrationClasses: [integrationClass],
158
148
  moduleFactory,
@@ -1,12 +1,6 @@
1
1
  const {
2
2
  createUserRepository,
3
3
  } = require('../../user/repositories/user-repository-factory');
4
- const {
5
- createOrganizationMembershipRepository,
6
- } = require('../../user/repositories/organization-membership-repository-factory');
7
- const {
8
- FindIndividualUsersByOrganization,
9
- } = require('../../user/use-cases/find-individual-users-by-organization');
10
4
 
11
5
  const ERROR_CODE_MAP = {
12
6
  USER_NOT_FOUND: 404,
@@ -32,12 +26,6 @@ function mapErrorToResponse(error) {
32
26
  */
33
27
  function createUserCommands() {
34
28
  const userRepository = createUserRepository();
35
- const findIndividualUsersByOrganization =
36
- new FindIndividualUsersByOrganization({
37
- userRepository,
38
- organizationMembershipRepository:
39
- createOrganizationMembershipRepository(),
40
- });
41
29
 
42
30
  return {
43
31
  /**
@@ -182,8 +170,7 @@ function createUserCommands() {
182
170
  },
183
171
 
184
172
  /**
185
- * Find all individual users who belong to an organization user: those with
186
- * a membership and those the legacy organizationId pointer places there.
173
+ * Find all individual users linked to an organization user
187
174
  * @param {string} organizationUserId - Organization user ID to search for
188
175
  * @returns {Promise<Object[]>} Array of individual user objects (empty if none)
189
176
  */
@@ -195,9 +182,10 @@ function createUserCommands() {
195
182
  throw error;
196
183
  }
197
184
 
198
- const users = await findIndividualUsersByOrganization.execute(
199
- organizationUserId
200
- );
185
+ const users =
186
+ await userRepository.findIndividualUsersByOrganizationId(
187
+ organizationUserId
188
+ );
201
189
 
202
190
  return (users || []).map((user) => ({
203
191
  id: user._id?.toString() || user.id,
@@ -17,7 +17,6 @@ const { createUsageCommands } = require('./commands/usage-commands');
17
17
  *
18
18
  * @param {Object} params
19
19
  * @param {Object} params.integrationClass - Integration class (required)
20
- * @param {Array<object>} [params.moduleDefinitions] - the app's offered module definitions; defaults to the integration's own modules
21
20
  * @returns {Object} Unified commands object with all CRUD operations
22
21
  *
23
22
  * @example
@@ -25,12 +24,9 @@ const { createUsageCommands } = require('./commands/usage-commands');
25
24
  * const user = await commands.createUser({ username: 'user@example.com' });
26
25
  * const credential = await commands.createCredential({ userId: user.id, ... });
27
26
  */
28
- function createFriggCommands({ integrationClass, moduleDefinitions }) {
27
+ function createFriggCommands({ integrationClass }) {
29
28
  // All commands use Frigg's default repositories and use cases
30
- const integrationCommands = createIntegrationCommands({
31
- integrationClass,
32
- moduleDefinitions,
33
- });
29
+ const integrationCommands = createIntegrationCommands({ integrationClass });
34
30
 
35
31
  const userCommands = createUserCommands();
36
32
 
@@ -186,40 +186,6 @@ class CredentialRepositoryDocumentDB extends CredentialRepositoryInterface {
186
186
  return this._mapCredential(decryptedCredential);
187
187
  }
188
188
 
189
- /**
190
- * All credentials owned by a user, newest first.
191
- *
192
- * @param {string} userId - User ID
193
- * @returns {Promise<Array<Object>>} Credentials with string IDs
194
- */
195
- async findCredentialsByUserId(userId) {
196
- const userObjectId = toObjectId(userId);
197
- if (!userObjectId) return [];
198
- const docs = await findManyDrained(
199
- this.prisma,
200
- 'Credential',
201
- { userId: userObjectId },
202
- { sort: { createdAt: -1 } }
203
- );
204
- const credentials = [];
205
- for (const doc of docs) {
206
- const decrypted = await this.encryptionService.decryptFields(
207
- 'Credential',
208
- doc
209
- );
210
- credentials.push({
211
- ...this._mapCredential(decrypted),
212
- id: fromObjectId(doc._id),
213
- userId: fromObjectId(doc.userId) ?? doc.userId,
214
- externalId: doc.externalId ?? null,
215
- authIsValid: doc.authIsValid ?? null,
216
- createdAt: doc.createdAt ?? null,
217
- updatedAt: doc.updatedAt ?? null,
218
- });
219
- }
220
- return credentials;
221
- }
222
-
223
189
  async updateCredential(credentialId, updates) {
224
190
  const objectId = toObjectId(credentialId);
225
191
  if (!objectId) return null;
@@ -80,21 +80,6 @@ class CredentialRepositoryInterface {
80
80
  );
81
81
  }
82
82
 
83
- /**
84
- * All credentials owned by a user, newest first.
85
- *
86
- * @param {string|number} userId - User ID (string from application layer)
87
- * @returns {Promise<Array<Object>>} Credentials with string IDs, `createdAt`
88
- * and `updatedAt`, and the (decrypted) data fields spread in, as
89
- * findCredentialById returns them. Empty when the user has none.
90
- * @abstract
91
- */
92
- async findCredentialsByUserId(userId) {
93
- throw new Error(
94
- 'Method findCredentialsByUserId must be implemented by subclass'
95
- );
96
- }
97
-
98
83
  /**
99
84
  * Update a credential by ID
100
85
  *
@@ -188,29 +188,6 @@ class CredentialRepositoryMongo extends CredentialRepositoryInterface {
188
188
  };
189
189
  }
190
190
 
191
- /**
192
- * All credentials owned by a user, newest first.
193
- *
194
- * @param {string} userId - User ID
195
- * @returns {Promise<Array<Object>>} Credentials with string IDs
196
- */
197
- async findCredentialsByUserId(userId) {
198
- if (!userId) return [];
199
- const credentials = await this.prisma.credential.findMany({
200
- where: { userId: String(userId) },
201
- orderBy: { createdAt: 'desc' },
202
- });
203
- return credentials.map((credential) => ({
204
- ...(credential.data || {}),
205
- id: credential.id,
206
- userId: credential.userId,
207
- externalId: credential.externalId,
208
- authIsValid: credential.authIsValid,
209
- createdAt: credential.createdAt,
210
- updatedAt: credential.updatedAt,
211
- }));
212
- }
213
-
214
191
  /**
215
192
  * Update a credential by ID
216
193
  * Replaces: Credential.findByIdAndUpdate(credentialId, { $set: updates })
@@ -205,34 +205,6 @@ class CredentialRepositoryPostgres extends CredentialRepositoryInterface {
205
205
  };
206
206
  }
207
207
 
208
- /**
209
- * All credentials owned by a user, newest first.
210
- *
211
- * @param {string} userId - User ID (string from application layer)
212
- * @returns {Promise<Array<Object>>} Credentials with string IDs
213
- */
214
- async findCredentialsByUserId(userId) {
215
- let intUserId;
216
- try {
217
- intUserId = this._convertId(userId);
218
- } catch {
219
- return [];
220
- }
221
- const credentials = await this.prisma.credential.findMany({
222
- where: { userId: intUserId },
223
- orderBy: { createdAt: 'desc' },
224
- });
225
- return credentials.map((credential) => ({
226
- ...(credential.data || {}),
227
- id: credential.id.toString(),
228
- userId: credential.userId?.toString(),
229
- externalId: credential.externalId,
230
- authIsValid: credential.authIsValid,
231
- createdAt: credential.createdAt,
232
- updatedAt: credential.updatedAt,
233
- }));
234
- }
235
-
236
208
  /**
237
209
  * Update a credential by ID
238
210
  *
@@ -58,8 +58,6 @@ async function insertOne(client, collection, document) {
58
58
 
59
59
  // Provide helpful context for common errors
60
60
  if (error.code === 11000) {
61
- // `code` lets callers recognise a lost race without parsing text
62
- // (see database/unique-violation.js).
63
61
  throw Object.assign(
64
62
  new Error(`${errorMsg} - Duplicate key violation`),
65
63
  { code: 11000 }
@@ -39,12 +39,6 @@ const CORE_ENCRYPTION_SCHEMA = {
39
39
  Token: {
40
40
  fields: ['token'],
41
41
  },
42
-
43
- // Multi-step authorization (Management API v2): earlier steps can collect
44
- // emails, usernames or one-time codes.
45
- AuthorizationSession: {
46
- fields: ['stepData'],
47
- },
48
42
  };
49
43
 
50
44
  let customSchema = {};
@@ -180,13 +174,6 @@ function loadModuleEncryptionSchemas(integrations) {
180
174
  return;
181
175
  }
182
176
 
183
- // NOTE: this path does not exist ('../integrations' resolves to
184
- // database/integrations), so today this function throws MODULE_NOT_FOUND,
185
- // loadCustomEncryptionSchema swallows it, and module credentialFields are
186
- // effectively NOT registered. Fixing it changes which fields are encrypted
187
- // and needs a decrypt that tolerates plaintext, so it ships separately:
188
- // branch claude/internal-mcp-03b-module-credential-encryption. Left as is
189
- // here on purpose (ADR-059 must not change what gets encrypted).
190
177
  const { getModulesDefinitionFromIntegrationClasses } = require('../integrations/utils/map-integration-dto');
191
178
 
192
179
  const moduleDefinitions = getModulesDefinitionFromIntegrationClasses(integrations);
package/errors/index.js CHANGED
@@ -6,6 +6,9 @@ const {
6
6
  ParameterTypeError,
7
7
  } = require('./validation-errors');
8
8
  const { ClientSafeError } = require('./client-safe-error');
9
+ const {
10
+ MappingAlreadyExistsError,
11
+ } = require('./mapping-already-exists-error');
9
12
 
10
13
  module.exports = {
11
14
  BaseError,
@@ -14,4 +17,5 @@ module.exports = {
14
17
  RequiredPropertyError,
15
18
  ParameterTypeError,
16
19
  ClientSafeError,
20
+ MappingAlreadyExistsError,
17
21
  };
@@ -0,0 +1,14 @@
1
+ const { BaseError } = require('./base-error');
2
+
3
+ class MappingAlreadyExistsError extends BaseError {
4
+ constructor(integrationId, sourceId, ...errorOptions) {
5
+ super(
6
+ `Mapping already exists for integration ${integrationId} and sourceId ${sourceId}`,
7
+ ...errorOptions
8
+ );
9
+ this.integrationId = integrationId;
10
+ this.sourceId = sourceId;
11
+ }
12
+ }
13
+
14
+ module.exports = { MappingAlreadyExistsError };