@friggframework/core 2.0.0--canary.703.6a9383b.0 → 2.0.0--canary.704.79d9a63.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 (144) hide show
  1. package/CLAUDE.md +40 -0
  2. package/application/commands/entity-commands.js +18 -60
  3. package/application/commands/integration-commands.js +63 -0
  4. package/database/documentdb-utils.js +0 -2
  5. package/generated/prisma-mongodb/edge.js +4 -52
  6. package/generated/prisma-mongodb/index-browser.js +1 -49
  7. package/generated/prisma-mongodb/index.d.ts +60 -3506
  8. package/generated/prisma-mongodb/index.js +4 -52
  9. package/generated/prisma-mongodb/package.json +1 -1
  10. package/generated/prisma-mongodb/schema.prisma +0 -65
  11. package/generated/prisma-mongodb/wasm.js +4 -52
  12. package/generated/prisma-postgresql/edge.js +4 -52
  13. package/generated/prisma-postgresql/index-browser.js +1 -49
  14. package/generated/prisma-postgresql/index.d.ts +94 -3824
  15. package/generated/prisma-postgresql/index.js +4 -52
  16. package/generated/prisma-postgresql/package.json +1 -1
  17. package/generated/prisma-postgresql/schema.prisma +0 -63
  18. package/generated/prisma-postgresql/wasm.js +4 -52
  19. package/handlers/app-handler-helpers.js +2 -5
  20. package/handlers/integration-event-dispatcher.js +3 -7
  21. package/handlers/routers/integration-webhook-routers.js +38 -26
  22. package/index.js +0 -11
  23. package/integrations/index.js +0 -12
  24. package/integrations/integration-base.js +2 -134
  25. package/integrations/integration-router.js +1 -6
  26. package/integrations/repositories/integration-repository-documentdb.js +34 -6
  27. package/integrations/repositories/integration-repository-interface.js +30 -3
  28. package/integrations/repositories/integration-repository-mongo.js +56 -19
  29. package/integrations/repositories/integration-repository-postgres.js +59 -5
  30. package/integrations/use-cases/add-entities-to-integration.js +92 -0
  31. package/integrations/use-cases/create-integration.js +61 -33
  32. package/integrations/use-cases/delete-integration-for-user.js +4 -4
  33. package/integrations/use-cases/get-integration-for-user.js +2 -4
  34. package/integrations/use-cases/get-integration-instance.js +4 -4
  35. package/integrations/use-cases/get-integrations-for-user.js +4 -4
  36. package/integrations/use-cases/get-possible-integrations.js +2 -7
  37. package/integrations/use-cases/remove-entities-from-integration.js +70 -0
  38. package/integrations/use-cases/update-integration.js +4 -4
  39. package/integrations/utils/assert-entity-ids-input.js +42 -0
  40. package/modules/utils/map-module-dto.js +5 -3
  41. package/modules/utils/to-credential-id.js +14 -0
  42. package/package.json +5 -7
  43. package/prisma-mongodb/schema.prisma +0 -65
  44. package/prisma-postgresql/schema.prisma +0 -63
  45. package/queues/queuer-util.js +2 -9
  46. package/user/tests/doubles/test-user-repository.js +1 -1
  47. package/user/use-cases/get-user-from-bearer-token.js +7 -2
  48. package/handlers/workers/integration-triggers.js +0 -25
  49. package/infrastructure/scheduler/recurrence.js +0 -204
  50. package/integrations/define/DESIGN.md +0 -108
  51. package/integrations/define/config.js +0 -98
  52. package/integrations/define/define-app.js +0 -52
  53. package/integrations/define/define-extension.js +0 -154
  54. package/integrations/define/define-integration.js +0 -292
  55. package/integrations/define/examples/deal-alerts.js +0 -138
  56. package/integrations/define/extension-binding.js +0 -174
  57. package/integrations/define/index.js +0 -11
  58. package/integrations/define/json-schema-lite.js +0 -68
  59. package/integrations/define/subscriptions.js +0 -93
  60. package/integrations/define/tests/fixtures/hubspot.js +0 -109
  61. package/integrations/define/tests/fixtures/slack.js +0 -26
  62. package/integrations/define/use.js +0 -39
  63. package/integrations/definitions/README.md +0 -278
  64. package/integrations/definitions/canonical.js +0 -11
  65. package/integrations/definitions/capabilities/AUDIT.md +0 -480
  66. package/integrations/definitions/capabilities/acmeerp.capabilities.json +0 -633
  67. package/integrations/definitions/capabilities/asana.capabilities.json +0 -279
  68. package/integrations/definitions/capabilities/calendly.capabilities.json +0 -385
  69. package/integrations/definitions/capabilities/docusign.capabilities.json +0 -314
  70. package/integrations/definitions/capabilities/fathom.capabilities.json +0 -238
  71. package/integrations/definitions/capabilities/github.capabilities.json +0 -585
  72. package/integrations/definitions/capabilities/hubspot.capabilities.json +0 -822
  73. package/integrations/definitions/capabilities/jira.capabilities.json +0 -391
  74. package/integrations/definitions/capabilities/klaviyo.capabilities.json +0 -503
  75. package/integrations/definitions/capabilities/linear.capabilities.json +0 -378
  76. package/integrations/definitions/capabilities/netsuite.capabilities.json +0 -535
  77. package/integrations/definitions/capabilities/notion.capabilities.json +0 -208
  78. package/integrations/definitions/capabilities/phone.capabilities.json +0 -511
  79. package/integrations/definitions/capabilities/pipedrive.capabilities.json +0 -430
  80. package/integrations/definitions/capabilities/quickbooks.capabilities.json +0 -635
  81. package/integrations/definitions/capabilities/salesforce.capabilities.json +0 -568
  82. package/integrations/definitions/capabilities/shopify.capabilities.json +0 -1128
  83. package/integrations/definitions/capabilities/slack.capabilities.json +0 -373
  84. package/integrations/definitions/capabilities/stripe.capabilities.json +0 -630
  85. package/integrations/definitions/capabilities/webflow.capabilities.json +0 -196
  86. package/integrations/definitions/capabilities/zendesk.capabilities.json +0 -422
  87. package/integrations/definitions/compile.js +0 -80
  88. package/integrations/definitions/config-form.js +0 -137
  89. package/integrations/definitions/examples/acme-erp-quickbooks.definition.json +0 -467
  90. package/integrations/definitions/examples/asana-slack.definition.json +0 -219
  91. package/integrations/definitions/examples/calendly-salesforce.definition.json +0 -459
  92. package/integrations/definitions/examples/docusign-salesforce.definition.json +0 -305
  93. package/integrations/definitions/examples/fathom-hubspot-notion.definition.json +0 -575
  94. package/integrations/definitions/examples/github-linear.definition.json +0 -322
  95. package/integrations/definitions/examples/hubspot-slack.definition.json +0 -427
  96. package/integrations/definitions/examples/klaviyo-hubspot.definition.json +0 -336
  97. package/integrations/definitions/examples/phone-hubspot.definition.json +0 -633
  98. package/integrations/definitions/examples/pipedrive-hubspot.definition.json +0 -136
  99. package/integrations/definitions/examples/pipedrive-phone.definition.json +0 -340
  100. package/integrations/definitions/examples/records/phone-hubspot.definition-version.json +0 -652
  101. package/integrations/definitions/examples/records/phone-hubspot.integration.json +0 -86
  102. package/integrations/definitions/examples/records/phone-hubspot.mappings.json +0 -39
  103. package/integrations/definitions/examples/records/phone-hubspot.workflow-run.json +0 -62
  104. package/integrations/definitions/examples/shopify-klaviyo.definition.json +0 -488
  105. package/integrations/definitions/examples/shopify-netsuite.definition.json +0 -622
  106. package/integrations/definitions/examples/stripe-quickbooks.definition.json +0 -476
  107. package/integrations/definitions/examples/webflow-hubspot-slack.definition.json +0 -262
  108. package/integrations/definitions/examples/zendesk-jira.definition.json +0 -604
  109. package/integrations/definitions/expressions.js +0 -229
  110. package/integrations/definitions/index.js +0 -48
  111. package/integrations/definitions/integration-runtime.js +0 -779
  112. package/integrations/definitions/interpreter.js +0 -104
  113. package/integrations/definitions/manifests.js +0 -177
  114. package/integrations/definitions/renderers/common.js +0 -67
  115. package/integrations/definitions/renderers/html.js +0 -129
  116. package/integrations/definitions/renderers/hubspot.js +0 -59
  117. package/integrations/definitions/renderers/preview.js +0 -154
  118. package/integrations/definitions/renderers/slack.js +0 -146
  119. package/integrations/definitions/repositories/definition-version-repository.js +0 -67
  120. package/integrations/definitions/repositories/workflow-run-repository.js +0 -80
  121. package/integrations/definitions/router.js +0 -170
  122. package/integrations/definitions/runtime.js +0 -268
  123. package/integrations/definitions/schema/capability-manifest.v0.1.schema.json +0 -549
  124. package/integrations/definitions/schema/installation.v0.1.schema.json +0 -130
  125. package/integrations/definitions/schema/integration-definition.v0.1.schema.json +0 -909
  126. package/integrations/definitions/schema/records.md +0 -105
  127. package/integrations/definitions/schema-check.js +0 -58
  128. package/integrations/definitions/scripts/check-manifests.js +0 -220
  129. package/integrations/definitions/scripts/validate.js +0 -66
  130. package/integrations/definitions/steps.js +0 -123
  131. package/integrations/definitions/sync-engine.js +0 -297
  132. package/integrations/definitions/tests/helpers-fake-crm.js +0 -49
  133. package/integrations/definitions/transforms.js +0 -91
  134. package/integrations/definitions/use-cases/definition-use-cases.js +0 -245
  135. package/integrations/definitions/validate.js +0 -436
  136. package/integrations/definitions/versioning.js +0 -176
  137. package/integrations/definitions/webhooks.js +0 -119
  138. package/integrations/integration-types.js +0 -73
  139. package/integrations/triggers.js +0 -244
  140. package/integrations/use-cases/run-trigger-tick.js +0 -155
  141. package/integrations/use-cases/run-trigger.js +0 -77
  142. package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +0 -66
  143. package/prisma-postgresql/migrations/20261019000000_workflow_run_waits/migration.sql +0 -7
  144. package/prisma-postgresql/migrations/20261019120000_definition_workspace/migration.sql +0 -5
package/CLAUDE.md CHANGED
@@ -206,11 +206,51 @@ 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), entities of another user
222
+ (`ENTITY_USER_MISMATCH`, 403) and entities of a module the integration does
223
+ not already hold (`ENTITY_MODULE_MISMATCH`, 400). The remove use case
224
+ (`remove-entities-from-integration.js`) refuses to leave a module with no
225
+ entity (`LAST_ENTITY_OF_MODULE`, 409). Re-adding an attached entity or
226
+ removing a detached one changes nothing; a detached entity is not deleted.
227
+ `integration-repository-entities-parity.test.js` runs every adapter against
228
+ real databases when `INTEGRATION_ENTITIES_PARITY_MONGO_URL` /
229
+ `INTEGRATION_ENTITIES_PARITY_POSTGRES_URL` are set. The DocumentDB adapter
230
+ writes only `Integration.entityIds` (as its `createIntegration` does), not
231
+ `Entity.integrationIds`.
232
+ **One credential, many accounts**: when one credential covers several
233
+ provider accounts, give each account its own entity (externalId = account
234
+ id) and attach them all to one integration; webhooks then route with
235
+ `findIntegrationByEntityExternalId`. Traps:
236
+ (1) `IntegrationBase` keys modules by module name, so `this.<moduleKey>`
237
+ holds only one entity's module, and on the queue/webhook path
238
+ (`get-integration-instance.js`) `this.entities` holds entity **ids**, not
239
+ records. For per-account work (a webhook for one account, teardown of every
240
+ account in `onDelete`), load
241
+ `this.commands.loadIntegrationContextById(this.id)` and use
242
+ `context.modules`: one module per entity, each with its record on
243
+ `module.entity` (pick by `module.entity.externalId`).
244
+ (2) `findIntegrationByEntityExternalId` counts every entity with that
245
+ externalId, attached or not, across users: reuse an existing entity
246
+ (`findEntity`) when an account comes back, or delete it when it is revoked,
247
+ never create a second one.
248
+ (3) `createIntegration` reuses an existing integration of the same user
249
+ and type whose entities include all requested ones (an exact match wins),
250
+ so a reconnect that sends the original entities finds the integration that
251
+ now holds more.
252
+ (4) Every module's delegate is the integration: an auth failure on any
253
+ account marks the shared credential invalid and the integration ERROR.
214
254
  - `integration-mapping-repository-*.js` - Mapping data persistence.
215
255
  `queryMappings(integrationId, { where, orderBy, skip, take, omit })`
216
256
  returns `{ mappings, total }`: one filtered, ordered page of an
@@ -1,12 +1,24 @@
1
1
  const {
2
2
  createModuleRepository,
3
3
  } = require('../../modules/repositories/module-repository-factory');
4
+ const { toCredentialId } = require('../../modules/utils/to-credential-id');
4
5
 
5
6
  const ERROR_CODE_MAP = {
6
7
  ENTITY_NOT_FOUND: 404,
7
8
  INVALID_ENTITY_DATA: 400,
8
9
  };
9
10
 
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
+
10
22
  function mapErrorToResponse(error) {
11
23
  const status = ERROR_CODE_MAP[error?.code] || 500;
12
24
  return {
@@ -66,16 +78,7 @@ function createEntityCommands() {
66
78
 
67
79
  const entity = await moduleRepo.createEntity(entityData);
68
80
 
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
- };
81
+ return toEntityDto(entity);
79
82
  } catch (error) {
80
83
  return mapErrorToResponse(error);
81
84
  }
@@ -109,16 +112,7 @@ function createEntityCommands() {
109
112
  return null;
110
113
  }
111
114
 
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
- };
115
+ return toEntityDto(entity);
122
116
  } catch (error) {
123
117
  return mapErrorToResponse(error);
124
118
  }
@@ -139,16 +133,7 @@ function createEntityCommands() {
139
133
 
140
134
  const entities = await moduleRepo.findEntitiesByUserId(userId);
141
135
 
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
- }));
136
+ return entities.map((entity) => toEntityDto(entity));
152
137
  } catch (error) {
153
138
  if (error.code) {
154
139
  return mapErrorToResponse(error);
@@ -180,16 +165,7 @@ function createEntityCommands() {
180
165
  moduleName
181
166
  );
182
167
 
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
- }));
168
+ return entities.map((entity) => toEntityDto(entity));
193
169
  } catch (error) {
194
170
  if (error.code) {
195
171
  return mapErrorToResponse(error);
@@ -213,16 +189,7 @@ function createEntityCommands() {
213
189
 
214
190
  const entity = await moduleRepo.findEntityById(entityId);
215
191
 
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
- };
192
+ return toEntityDto(entity);
226
193
  } catch (error) {
227
194
  return mapErrorToResponse(error);
228
195
  }
@@ -250,16 +217,7 @@ function createEntityCommands() {
250
217
  throw error;
251
218
  }
252
219
 
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
- };
220
+ return toEntityDto(entity);
263
221
  } catch (error) {
264
222
  return mapErrorToResponse(error);
265
223
  }
@@ -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,11 @@ 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,
52
+ ENTITY_MODULE_MISMATCH: 400,
53
+ LAST_ENTITY_OF_MODULE: 409,
43
54
  };
44
55
 
45
56
  function mapErrorToResponse(error) {
@@ -163,6 +174,17 @@ function createIntegrationCommands({ integrationClass } = {}) {
163
174
  integrationRepository,
164
175
  });
165
176
 
177
+ const addEntitiesToIntegrationUseCase = new AddEntitiesToIntegration({
178
+ integrationRepository,
179
+ moduleRepository,
180
+ });
181
+
182
+ const removeEntitiesFromIntegrationUseCase =
183
+ new RemoveEntitiesFromIntegration({
184
+ integrationRepository,
185
+ moduleRepository,
186
+ });
187
+
166
188
  return {
167
189
  findIntegrationById,
168
190
  listIntegrations,
@@ -307,6 +329,47 @@ function createIntegrationCommands({ integrationClass } = {}) {
307
329
  }
308
330
  },
309
331
 
332
+ /**
333
+ * Attach entities to an existing integration. Every entity must
334
+ * exist, belong to the integration's user, and be of a module the
335
+ * integration already holds; entities that are already attached are
336
+ * left as they are.
337
+ * @param {Object} params
338
+ * @param {string} params.integrationId - Integration ID
339
+ * @param {Array<string>} params.entityIds - Entity IDs to attach
340
+ * @returns {Promise<Object>} Updated integration
341
+ */
342
+ async addEntitiesToIntegration({ integrationId, entityIds }) {
343
+ try {
344
+ return await addEntitiesToIntegrationUseCase.execute(
345
+ integrationId,
346
+ entityIds
347
+ );
348
+ } catch (error) {
349
+ return mapErrorToResponse(error);
350
+ }
351
+ },
352
+
353
+ /**
354
+ * Detach entities from an integration. Entities that are not
355
+ * attached are ignored; detached entities are not deleted. Each
356
+ * module must keep at least one entity.
357
+ * @param {Object} params
358
+ * @param {string} params.integrationId - Integration ID
359
+ * @param {Array<string>} params.entityIds - Entity IDs to detach
360
+ * @returns {Promise<Object>} Updated integration
361
+ */
362
+ async removeEntitiesFromIntegration({ integrationId, entityIds }) {
363
+ try {
364
+ return await removeEntitiesFromIntegrationUseCase.execute(
365
+ integrationId,
366
+ entityIds
367
+ );
368
+ } catch (error) {
369
+ return mapErrorToResponse(error);
370
+ }
371
+ },
372
+
310
373
  /**
311
374
  * Delete an integration by ID
312
375
  * @param {string} integrationId - Integration ID to delete
@@ -25,8 +25,6 @@ async function findMany(client, collection, filter = {}, options = {}) {
25
25
  if (options.projection) command.projection = options.projection;
26
26
  if (options.sort) command.sort = options.sort;
27
27
  if (options.limit) command.limit = options.limit;
28
- // The first batch holds 101 documents unless asked for more; a page wants all of its rows.
29
- if (options.batchSize) command.batchSize = options.batchSize;
30
28
  const result = await client.$runCommandRaw(command);
31
29
  return result?.cursor?.firstBatch || [];
32
30
  }