@friggframework/core 2.0.0--canary.688.87a9bf3.0 → 2.0.0--canary.689.386395c.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.
package/CLAUDE.md CHANGED
@@ -206,11 +206,41 @@ packages/core/
206
206
  - `delete-integration-for-user.js` - Remove integration
207
207
  - `get-integration-instance.js` - Load integration with modules
208
208
  - `load-integration-context.js` - Full integration context loading
209
+ - `add-entities-to-integration.js` / `remove-entities-from-integration.js` - Attach or detach entities on an existing integration
209
210
 
210
211
  **Repositories**:
211
212
  - `integration-repository-factory.js` - Creates database-specific repositories
212
213
  - `integration-repository-mongo.js` - MongoDB implementation
213
214
  - `integration-repository-postgres.js` - PostgreSQL implementation
215
+ - `addEntitiesToIntegration(integrationId, entityIds)` /
216
+ `removeEntitiesFromIntegration(integrationId, entityIds)` change the entity
217
+ set of an existing integration (all three adapters; `null` for an unknown
218
+ integration). Reach them through the `addEntitiesToIntegration` /
219
+ `removeEntitiesFromIntegration` commands: the add use case
220
+ (`add-entities-to-integration.js`) rejects unknown entities
221
+ (`ENTITIES_NOT_FOUND`, 404) and entities of another user
222
+ (`ENTITY_USER_MISMATCH`, 403). Re-adding an attached entity or removing a
223
+ detached one changes nothing; a detached entity is not deleted.
224
+ `integration-repository-entities-parity.test.js` runs every adapter against
225
+ real databases when `INTEGRATION_ENTITIES_PARITY_MONGO_URL` /
226
+ `INTEGRATION_ENTITIES_PARITY_POSTGRES_URL` are set. The DocumentDB adapter
227
+ writes only `Integration.entityIds` (as its `createIntegration` does), not
228
+ `Entity.integrationIds`.
229
+ **One credential, many accounts**: when one credential covers several
230
+ provider accounts, give each account its own entity (externalId = account
231
+ id) and attach them all to one integration; webhooks then route with
232
+ `findIntegrationByEntityExternalId`. Three traps:
233
+ (1) `IntegrationBase` keys modules by module name, so `this.<moduleKey>`
234
+ holds only the last entity's module: pick the account's entity from
235
+ `this.entities` by `externalId`.
236
+ (2) `findIntegrationByEntityExternalId` counts every entity with that
237
+ externalId, attached or not: reuse an existing entity (`findEntity`) when an
238
+ account comes back, or delete it when it is revoked, never create a second
239
+ one.
240
+ (3) `createIntegration` reuses an integration only for the exact same entity
241
+ set, so after an attach a second `createIntegration` with the original
242
+ entities creates a new integration: re-authorization must update the
243
+ existing integration, not create one.
214
244
  - `integration-mapping-repository-*.js` - Mapping data persistence.
215
245
  `queryMappings(integrationId, { where, orderBy, skip, take, omit })`
216
246
  returns `{ mappings, total }`: one filtered, ordered page of an
@@ -1,24 +1,12 @@
1
1
  const {
2
2
  createModuleRepository,
3
3
  } = require('../../modules/repositories/module-repository-factory');
4
- const { toCredentialId } = require('../../modules/utils/to-credential-id');
5
4
 
6
5
  const ERROR_CODE_MAP = {
7
6
  ENTITY_NOT_FOUND: 404,
8
7
  INVALID_ENTITY_DATA: 400,
9
8
  };
10
9
 
11
- function toEntityDto(entity) {
12
- return {
13
- id: entity.id,
14
- userId: entity.userId,
15
- externalId: entity.externalId,
16
- name: entity.name,
17
- moduleName: entity.moduleName,
18
- credentialId: toCredentialId(entity.credential),
19
- };
20
- }
21
-
22
10
  function mapErrorToResponse(error) {
23
11
  const status = ERROR_CODE_MAP[error?.code] || 500;
24
12
  return {
@@ -78,7 +66,16 @@ function createEntityCommands() {
78
66
 
79
67
  const entity = await moduleRepo.createEntity(entityData);
80
68
 
81
- return toEntityDto(entity);
69
+ return {
70
+ id: entity.id,
71
+ userId: entity.userId,
72
+ externalId: entity.externalId,
73
+ name: entity.name,
74
+ moduleName: entity.moduleName,
75
+ credentialId: entity.credential?._id
76
+ ? entity.credential._id.toString()
77
+ : entity.credential,
78
+ };
82
79
  } catch (error) {
83
80
  return mapErrorToResponse(error);
84
81
  }
@@ -112,7 +109,16 @@ function createEntityCommands() {
112
109
  return null;
113
110
  }
114
111
 
115
- return toEntityDto(entity);
112
+ return {
113
+ id: entity.id,
114
+ userId: entity.userId,
115
+ externalId: entity.externalId,
116
+ name: entity.name,
117
+ moduleName: entity.moduleName,
118
+ credentialId: entity.credential?._id
119
+ ? entity.credential._id.toString()
120
+ : entity.credential,
121
+ };
116
122
  } catch (error) {
117
123
  return mapErrorToResponse(error);
118
124
  }
@@ -133,7 +139,16 @@ function createEntityCommands() {
133
139
 
134
140
  const entities = await moduleRepo.findEntitiesByUserId(userId);
135
141
 
136
- return entities.map((entity) => toEntityDto(entity));
142
+ return entities.map((entity) => ({
143
+ id: entity.id,
144
+ userId: entity.userId,
145
+ externalId: entity.externalId,
146
+ name: entity.name,
147
+ moduleName: entity.moduleName,
148
+ credentialId: entity.credential?._id
149
+ ? entity.credential._id.toString()
150
+ : entity.credential,
151
+ }));
137
152
  } catch (error) {
138
153
  if (error.code) {
139
154
  return mapErrorToResponse(error);
@@ -165,7 +180,16 @@ function createEntityCommands() {
165
180
  moduleName
166
181
  );
167
182
 
168
- return entities.map((entity) => toEntityDto(entity));
183
+ return entities.map((entity) => ({
184
+ id: entity.id,
185
+ userId: entity.userId,
186
+ externalId: entity.externalId,
187
+ name: entity.name,
188
+ moduleName: entity.moduleName,
189
+ credentialId: entity.credential?._id
190
+ ? entity.credential._id.toString()
191
+ : entity.credential,
192
+ }));
169
193
  } catch (error) {
170
194
  if (error.code) {
171
195
  return mapErrorToResponse(error);
@@ -189,7 +213,16 @@ function createEntityCommands() {
189
213
 
190
214
  const entity = await moduleRepo.findEntityById(entityId);
191
215
 
192
- return toEntityDto(entity);
216
+ return {
217
+ id: entity.id,
218
+ userId: entity.userId,
219
+ externalId: entity.externalId,
220
+ name: entity.name,
221
+ moduleName: entity.moduleName,
222
+ credentialId: entity.credential?._id
223
+ ? entity.credential._id.toString()
224
+ : entity.credential,
225
+ };
193
226
  } catch (error) {
194
227
  return mapErrorToResponse(error);
195
228
  }
@@ -217,7 +250,16 @@ function createEntityCommands() {
217
250
  throw error;
218
251
  }
219
252
 
220
- return toEntityDto(entity);
253
+ return {
254
+ id: entity.id,
255
+ userId: entity.userId,
256
+ externalId: entity.externalId,
257
+ name: entity.name,
258
+ moduleName: entity.moduleName,
259
+ credentialId: entity.credential?._id
260
+ ? entity.credential._id.toString()
261
+ : entity.credential,
262
+ };
221
263
  } catch (error) {
222
264
  return mapErrorToResponse(error);
223
265
  }
@@ -29,6 +29,12 @@ const {
29
29
  const {
30
30
  PatchIntegrationConfig,
31
31
  } = require('../../integrations/use-cases/patch-integration-config');
32
+ const {
33
+ AddEntitiesToIntegration,
34
+ } = require('../../integrations/use-cases/add-entities-to-integration');
35
+ const {
36
+ RemoveEntitiesFromIntegration,
37
+ } = require('../../integrations/use-cases/remove-entities-from-integration');
32
38
  const {
33
39
  getModulesDefinitionFromIntegrationClasses,
34
40
  } = require('../../integrations/utils/map-integration-dto');
@@ -40,6 +46,9 @@ const ERROR_CODE_MAP = {
40
46
  EXTERNAL_ID_REQUIRED: 400,
41
47
  TYPE_REQUIRED: 400,
42
48
  INTEGRATION_RECORD_NOT_FOUND: 404,
49
+ INVALID_INTEGRATION_DATA: 400,
50
+ ENTITIES_NOT_FOUND: 404,
51
+ ENTITY_USER_MISMATCH: 403,
43
52
  };
44
53
 
45
54
  function mapErrorToResponse(error) {
@@ -163,6 +172,16 @@ function createIntegrationCommands({ integrationClass } = {}) {
163
172
  integrationRepository,
164
173
  });
165
174
 
175
+ const addEntitiesToIntegrationUseCase = new AddEntitiesToIntegration({
176
+ integrationRepository,
177
+ moduleRepository,
178
+ });
179
+
180
+ const removeEntitiesFromIntegrationUseCase =
181
+ new RemoveEntitiesFromIntegration({
182
+ integrationRepository,
183
+ });
184
+
166
185
  return {
167
186
  findIntegrationById,
168
187
  listIntegrations,
@@ -307,6 +326,45 @@ function createIntegrationCommands({ integrationClass } = {}) {
307
326
  }
308
327
  },
309
328
 
329
+ /**
330
+ * Attach entities to an existing integration. Every entity must
331
+ * exist and belong to the integration's user; entities that are
332
+ * already attached are left as they are.
333
+ * @param {Object} params
334
+ * @param {string} params.integrationId - Integration ID
335
+ * @param {Array<string>} params.entityIds - Entity IDs to attach
336
+ * @returns {Promise<Object>} Updated integration
337
+ */
338
+ async addEntitiesToIntegration({ integrationId, entityIds }) {
339
+ try {
340
+ return await addEntitiesToIntegrationUseCase.execute(
341
+ integrationId,
342
+ entityIds
343
+ );
344
+ } catch (error) {
345
+ return mapErrorToResponse(error);
346
+ }
347
+ },
348
+
349
+ /**
350
+ * Detach entities from an integration. Entities that are not
351
+ * attached are ignored; detached entities are not deleted.
352
+ * @param {Object} params
353
+ * @param {string} params.integrationId - Integration ID
354
+ * @param {Array<string>} params.entityIds - Entity IDs to detach
355
+ * @returns {Promise<Object>} Updated integration
356
+ */
357
+ async removeEntitiesFromIntegration({ integrationId, entityIds }) {
358
+ try {
359
+ return await removeEntitiesFromIntegrationUseCase.execute(
360
+ integrationId,
361
+ entityIds
362
+ );
363
+ } catch (error) {
364
+ return mapErrorToResponse(error);
365
+ }
366
+ },
367
+
310
368
  /**
311
369
  * Delete an integration by ID
312
370
  * @param {string} integrationId - Integration ID to delete
@@ -164,6 +164,38 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
164
164
  return this._mapIntegration(created);
165
165
  }
166
166
 
167
+ async addEntitiesToIntegration(integrationId, entityIds) {
168
+ const objectId = toObjectId(integrationId);
169
+ if (!objectId) return null;
170
+ const result = await updateOne(
171
+ this.prisma,
172
+ 'Integration',
173
+ { _id: objectId },
174
+ {
175
+ $addToSet: { entityIds: { $each: toObjectIdArray(entityIds) } },
176
+ $set: { updatedAt: { $date: new Date().toISOString() } },
177
+ }
178
+ );
179
+ if (!result?.n) return null;
180
+ return this.findIntegrationById(integrationId);
181
+ }
182
+
183
+ async removeEntitiesFromIntegration(integrationId, entityIds) {
184
+ const objectId = toObjectId(integrationId);
185
+ if (!objectId) return null;
186
+ const result = await updateOne(
187
+ this.prisma,
188
+ 'Integration',
189
+ { _id: objectId },
190
+ {
191
+ $pullAll: { entityIds: toObjectIdArray(entityIds) },
192
+ $set: { updatedAt: { $date: new Date().toISOString() } },
193
+ }
194
+ );
195
+ if (!result?.n) return null;
196
+ return this.findIntegrationById(integrationId);
197
+ }
198
+
167
199
  async findIntegrationByUserId(userId) {
168
200
  const objectId = toObjectId(userId);
169
201
  if (!objectId) return null;
@@ -184,6 +184,36 @@ class IntegrationRepositoryInterface {
184
184
  'Method findIntegrationsByEntityId must be implemented by subclass'
185
185
  );
186
186
  }
187
+
188
+ /**
189
+ * Attach entities to an existing integration. Entities that are already
190
+ * attached are left as they are.
191
+ *
192
+ * @param {string|number} integrationId - Integration ID
193
+ * @param {Array<string|number>} entityIds - Entity IDs to attach
194
+ * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
195
+ * @abstract
196
+ */
197
+ async addEntitiesToIntegration(integrationId, entityIds) {
198
+ throw new Error(
199
+ 'Method addEntitiesToIntegration must be implemented by subclass'
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Detach entities from an integration. Entities that are not attached
205
+ * are ignored; the entities themselves are not deleted.
206
+ *
207
+ * @param {string|number} integrationId - Integration ID
208
+ * @param {Array<string|number>} entityIds - Entity IDs to detach
209
+ * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
210
+ * @abstract
211
+ */
212
+ async removeEntitiesFromIntegration(integrationId, entityIds) {
213
+ throw new Error(
214
+ 'Method removeEntitiesFromIntegration must be implemented by subclass'
215
+ );
216
+ }
187
217
  }
188
218
 
189
219
  module.exports = { IntegrationRepositoryInterface };
@@ -338,6 +338,58 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
338
338
  };
339
339
  }
340
340
 
341
+ /**
342
+ * Attach entities to an existing integration. Entities already attached
343
+ * are left as they are.
344
+ *
345
+ * @param {string} integrationId - Integration ID
346
+ * @param {Array<string>} entityIds - Entity IDs to attach
347
+ * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
348
+ */
349
+ async addEntitiesToIntegration(integrationId, entityIds) {
350
+
351
+ try {
352
+ await this.prisma.integration.update({
353
+ where: { id: integrationId },
354
+ data: {
355
+ entities: {
356
+ connect: entityIds.map((id) => ({ id })),
357
+ },
358
+ },
359
+ });
360
+ } catch (error) {
361
+ if (error.code === 'P2025') return null;
362
+ throw error;
363
+ }
364
+ return this.findIntegrationById(integrationId);
365
+ }
366
+
367
+ /**
368
+ * Detach entities from an integration. Entities that are not attached
369
+ * are ignored. The entities themselves are not deleted.
370
+ *
371
+ * @param {string} integrationId - Integration ID
372
+ * @param {Array<string>} entityIds - Entity IDs to detach
373
+ * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
374
+ */
375
+ async removeEntitiesFromIntegration(integrationId, entityIds) {
376
+
377
+ try {
378
+ await this.prisma.integration.update({
379
+ where: { id: integrationId },
380
+ data: {
381
+ entities: {
382
+ disconnect: entityIds.map((id) => ({ id })),
383
+ },
384
+ },
385
+ });
386
+ } catch (error) {
387
+ if (error.code === 'P2025') return null;
388
+ throw error;
389
+ }
390
+ return this.findIntegrationById(integrationId);
391
+ }
392
+
341
393
  /**
342
394
  * Find integration by user ID (returns single integration)
343
395
  * Replaces: IntegrationModel.findOne({ user: userId }).populate('entities')
@@ -365,6 +365,64 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
365
365
  };
366
366
  }
367
367
 
368
+ /**
369
+ * Attach entities to an existing integration. Entities already attached
370
+ * are left as they are.
371
+ *
372
+ * @param {string} integrationId - Integration ID (string from application layer)
373
+ * @param {Array<string>} entityIds - Entity IDs to attach
374
+ * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
375
+ */
376
+ async addEntitiesToIntegration(integrationId, entityIds) {
377
+ const intId = this._convertId(integrationId);
378
+
379
+ try {
380
+ await this.prisma.integration.update({
381
+ where: { id: intId },
382
+ data: {
383
+ entities: {
384
+ connect: entityIds.map((id) => ({
385
+ id: this._convertId(id),
386
+ })),
387
+ },
388
+ },
389
+ });
390
+ } catch (error) {
391
+ if (error.code === 'P2025') return null;
392
+ throw error;
393
+ }
394
+ return this.findIntegrationById(integrationId);
395
+ }
396
+
397
+ /**
398
+ * Detach entities from an integration. Entities that are not attached
399
+ * are ignored. The entities themselves are not deleted.
400
+ *
401
+ * @param {string} integrationId - Integration ID (string from application layer)
402
+ * @param {Array<string>} entityIds - Entity IDs to detach
403
+ * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
404
+ */
405
+ async removeEntitiesFromIntegration(integrationId, entityIds) {
406
+ const intId = this._convertId(integrationId);
407
+
408
+ try {
409
+ await this.prisma.integration.update({
410
+ where: { id: intId },
411
+ data: {
412
+ entities: {
413
+ disconnect: entityIds.map((id) => ({
414
+ id: this._convertId(id),
415
+ })),
416
+ },
417
+ },
418
+ });
419
+ } catch (error) {
420
+ if (error.code === 'P2025') return null;
421
+ throw error;
422
+ }
423
+ return this.findIntegrationById(integrationId);
424
+ }
425
+
368
426
  /**
369
427
  * Find integration by user ID (returns single integration)
370
428
  *
@@ -0,0 +1,73 @@
1
+ const {
2
+ assertEntityIdsInput,
3
+ integrationNotFound,
4
+ } = require('../utils/assert-entity-ids-input');
5
+
6
+ /**
7
+ * Attaches entities to an existing integration, e.g. when a user grants
8
+ * access to more accounts after the integration was created. Every entity
9
+ * must exist and belong to the integration's user. Entities that are already
10
+ * attached are left as they are.
11
+ * @class AddEntitiesToIntegration
12
+ */
13
+ class AddEntitiesToIntegration {
14
+ /**
15
+ * @param {Object} params
16
+ * @param {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface} params.integrationRepository
17
+ * @param {import('../../modules/repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
18
+ */
19
+ constructor({ integrationRepository, moduleRepository }) {
20
+ this.integrationRepository = integrationRepository;
21
+ this.moduleRepository = moduleRepository;
22
+ }
23
+
24
+ /**
25
+ * @param {string} integrationId
26
+ * @param {string[]} entityIds
27
+ * @returns {Promise<Object>} The updated integration record.
28
+ */
29
+ async execute(integrationId, entityIds) {
30
+ assertEntityIdsInput(integrationId, entityIds);
31
+
32
+ const integration = await this.integrationRepository
33
+ .findIntegrationById(integrationId)
34
+ .catch(() => null);
35
+ if (!integration) throw integrationNotFound(integrationId);
36
+
37
+ const entities = await this.moduleRepository.findEntitiesByIds(
38
+ entityIds
39
+ );
40
+ const foundIds = new Set(entities.map((e) => String(e.id)));
41
+ const missing = entityIds.filter((id) => !foundIds.has(String(id)));
42
+ if (missing.length > 0) {
43
+ const error = new Error(
44
+ `Entities not found: ${missing.join(', ')}`
45
+ );
46
+ error.code = 'ENTITIES_NOT_FOUND';
47
+ throw error;
48
+ }
49
+
50
+ const foreign = entities.filter(
51
+ (e) => String(e.userId) !== String(integration.userId)
52
+ );
53
+ if (foreign.length > 0) {
54
+ const error = new Error(
55
+ `Entities do not belong to the integration's user: ${foreign
56
+ .map((e) => e.id)
57
+ .join(', ')}`
58
+ );
59
+ error.code = 'ENTITY_USER_MISMATCH';
60
+ throw error;
61
+ }
62
+
63
+ const updated =
64
+ await this.integrationRepository.addEntitiesToIntegration(
65
+ integrationId,
66
+ entityIds
67
+ );
68
+ if (!updated) throw integrationNotFound(integrationId);
69
+ return updated;
70
+ }
71
+ }
72
+
73
+ module.exports = { AddEntitiesToIntegration };
@@ -0,0 +1,38 @@
1
+ const {
2
+ assertEntityIdsInput,
3
+ integrationNotFound,
4
+ } = require('../utils/assert-entity-ids-input');
5
+
6
+ /**
7
+ * Detaches entities from an integration. Entities that are not attached are
8
+ * ignored, and detached entities are not deleted.
9
+ * @class RemoveEntitiesFromIntegration
10
+ */
11
+ class RemoveEntitiesFromIntegration {
12
+ /**
13
+ * @param {Object} params
14
+ * @param {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface} params.integrationRepository
15
+ */
16
+ constructor({ integrationRepository }) {
17
+ this.integrationRepository = integrationRepository;
18
+ }
19
+
20
+ /**
21
+ * @param {string} integrationId
22
+ * @param {string[]} entityIds
23
+ * @returns {Promise<Object>} The updated integration record.
24
+ */
25
+ async execute(integrationId, entityIds) {
26
+ assertEntityIdsInput(integrationId, entityIds);
27
+
28
+ const updated =
29
+ await this.integrationRepository.removeEntitiesFromIntegration(
30
+ integrationId,
31
+ entityIds
32
+ );
33
+ if (!updated) throw integrationNotFound(integrationId);
34
+ return updated;
35
+ }
36
+ }
37
+
38
+ module.exports = { RemoveEntitiesFromIntegration };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Throws INVALID_INTEGRATION_DATA unless an integration id and a non-empty
3
+ * list of entity ids are given.
4
+ * @param {string} integrationId
5
+ * @param {string[]} entityIds
6
+ */
7
+ function assertEntityIdsInput(integrationId, entityIds) {
8
+ if (!integrationId || !Array.isArray(entityIds) || entityIds.length === 0) {
9
+ const error = new Error(
10
+ 'integrationId and a non-empty entityIds array are required'
11
+ );
12
+ error.code = 'INVALID_INTEGRATION_DATA';
13
+ throw error;
14
+ }
15
+ }
16
+
17
+ function integrationNotFound(integrationId) {
18
+ const error = new Error(`Integration ${integrationId} not found`);
19
+ error.code = 'INTEGRATION_NOT_FOUND';
20
+ return error;
21
+ }
22
+
23
+ module.exports = { assertEntityIdsInput, integrationNotFound };
@@ -1,5 +1,3 @@
1
- const { toCredentialId } = require('./to-credential-id');
2
-
3
1
  /**
4
2
  * @param {import('../module').Module} moduleInstance
5
3
  * Convert a Module domain instance to a plain DTO suitable for JSON responses.
@@ -12,9 +10,9 @@ function mapModuleClassToModuleDTO(moduleInstance) {
12
10
  name: moduleInstance.name,
13
11
  userId: moduleInstance.userId,
14
12
  entity: moduleInstance.entity,
15
- credentialId: toCredentialId(moduleInstance.credential),
16
- type: moduleInstance.getName(),
13
+ credentialId: moduleInstance.credential?._id?.toString(),
14
+ type: moduleInstance.getName()
17
15
  };
18
16
  }
19
17
 
20
- module.exports = { mapModuleClassToModuleDTO };
18
+ module.exports = { mapModuleClassToModuleDTO };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.688.87a9bf3.0",
4
+ "version": "2.0.0--canary.689.386395c.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -47,9 +47,9 @@
47
47
  }
48
48
  },
49
49
  "devDependencies": {
50
- "@friggframework/eslint-config": "2.0.0--canary.688.87a9bf3.0",
51
- "@friggframework/prettier-config": "2.0.0--canary.688.87a9bf3.0",
52
- "@friggframework/test": "2.0.0--canary.688.87a9bf3.0",
50
+ "@friggframework/eslint-config": "2.0.0--canary.689.386395c.0",
51
+ "@friggframework/prettier-config": "2.0.0--canary.689.386395c.0",
52
+ "@friggframework/test": "2.0.0--canary.689.386395c.0",
53
53
  "@prisma/client": "^6.19.3",
54
54
  "@types/lodash": "4.17.15",
55
55
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -89,5 +89,5 @@
89
89
  "publishConfig": {
90
90
  "access": "public"
91
91
  },
92
- "gitHead": "87a9bf3315fc2daa2b7f38b7b1ddd5bbcb323b20"
92
+ "gitHead": "386395c1f89690e6098b761e2b343db6c7c3dc59"
93
93
  }
@@ -1,14 +0,0 @@
1
- /**
2
- * Resolve the credential id from an entity's or module's `credential`, which
3
- * repositories return as the decrypted credential object (or a bare id).
4
- * @param {Object|string|number|null|undefined} credential
5
- * @returns {string|null}
6
- */
7
- function toCredentialId(credential) {
8
- if (credential && typeof credential === 'object') {
9
- return credential.id?.toString() ?? null;
10
- }
11
- return credential?.toString() ?? null;
12
- }
13
-
14
- module.exports = { toCredentialId };