@friggframework/core 2.0.0--canary.690.08cd07e.0 → 2.0.0--canary.693.d8859c2.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 (261) hide show
  1. package/CLAUDE.md +0 -28
  2. package/README.md +0 -105
  3. package/application/commands/integration-commands.js +10 -20
  4. package/application/commands/scheduler-commands.js +7 -11
  5. package/application/commands/user-commands.js +5 -17
  6. package/application/index.js +2 -6
  7. package/core/Worker.js +7 -31
  8. package/core/create-handler.js +0 -9
  9. package/credential/repositories/credential-repository-documentdb.js +0 -34
  10. package/credential/repositories/credential-repository-interface.js +0 -15
  11. package/credential/repositories/credential-repository-mongo.js +0 -23
  12. package/credential/repositories/credential-repository-postgres.js +0 -28
  13. package/database/config.js +24 -48
  14. package/database/documentdb-utils.js +0 -2
  15. package/database/encryption/encryption-schema-registry.js +12 -93
  16. package/database/prisma.js +2 -4
  17. package/errors/index.js +4 -0
  18. package/errors/mapping-already-exists-error.js +14 -0
  19. package/generated/prisma-mongodb/edge.js +38 -33
  20. package/generated/prisma-mongodb/index-browser.js +35 -30
  21. package/generated/prisma-mongodb/index.d.ts +1880 -3191
  22. package/generated/prisma-mongodb/index.js +38 -33
  23. package/generated/prisma-mongodb/package.json +1 -1
  24. package/generated/prisma-mongodb/schema.prisma +50 -86
  25. package/generated/prisma-mongodb/wasm.js +38 -33
  26. package/generated/prisma-postgresql/edge.js +38 -33
  27. package/generated/prisma-postgresql/index-browser.js +35 -30
  28. package/generated/prisma-postgresql/index.d.ts +2050 -3390
  29. package/generated/prisma-postgresql/index.js +38 -33
  30. package/generated/prisma-postgresql/package.json +1 -1
  31. package/generated/prisma-postgresql/schema.prisma +48 -81
  32. package/generated/prisma-postgresql/wasm.js +38 -33
  33. package/handlers/WEBHOOKS.md +29 -0
  34. package/handlers/app-definition-loader.js +19 -128
  35. package/handlers/app-handler-helpers.js +3 -25
  36. package/handlers/backend-utils.js +25 -31
  37. package/handlers/middleware/admin-auth.js +1 -14
  38. package/handlers/routers/auth.js +10 -46
  39. package/handlers/routers/health.js +4 -13
  40. package/handlers/routers/integration-defined-routers.js +92 -144
  41. package/handlers/routers/integration-webhook-routers.js +64 -102
  42. package/handlers/workers/integration-defined-workers.js +2 -5
  43. package/index.js +3 -65
  44. package/infrastructure/scheduler/index.js +6 -19
  45. package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
  46. package/integrations/data-defined/README.md +169 -0
  47. package/integrations/data-defined/canonical.js +11 -0
  48. package/integrations/data-defined/capabilities/acmeerp.capabilities.json +608 -0
  49. package/integrations/data-defined/capabilities/asana.capabilities.json +265 -0
  50. package/integrations/data-defined/capabilities/calendly.capabilities.json +385 -0
  51. package/integrations/data-defined/capabilities/docusign.capabilities.json +314 -0
  52. package/integrations/data-defined/capabilities/fathom.capabilities.json +228 -0
  53. package/integrations/data-defined/capabilities/github.capabilities.json +563 -0
  54. package/integrations/data-defined/capabilities/hubspot.capabilities.json +715 -0
  55. package/integrations/data-defined/capabilities/jira.capabilities.json +376 -0
  56. package/integrations/data-defined/capabilities/klaviyo.capabilities.json +475 -0
  57. package/integrations/data-defined/capabilities/linear.capabilities.json +324 -0
  58. package/integrations/data-defined/capabilities/netsuite.capabilities.json +491 -0
  59. package/integrations/data-defined/capabilities/notion.capabilities.json +208 -0
  60. package/integrations/data-defined/capabilities/phone.capabilities.json +470 -0
  61. package/integrations/data-defined/capabilities/pipedrive.capabilities.json +388 -0
  62. package/integrations/data-defined/capabilities/quickbooks.capabilities.json +615 -0
  63. package/integrations/data-defined/capabilities/salesforce.capabilities.json +507 -0
  64. package/integrations/data-defined/capabilities/shopify.capabilities.json +1082 -0
  65. package/integrations/data-defined/capabilities/slack.capabilities.json +363 -0
  66. package/integrations/data-defined/capabilities/stripe.capabilities.json +580 -0
  67. package/integrations/data-defined/capabilities/webflow.capabilities.json +196 -0
  68. package/integrations/data-defined/capabilities/zendesk.capabilities.json +409 -0
  69. package/integrations/data-defined/compile.js +77 -0
  70. package/integrations/data-defined/config-form.js +110 -0
  71. package/integrations/data-defined/data-defined-integration.js +366 -0
  72. package/integrations/data-defined/examples/acme-erp-quickbooks.definition.json +467 -0
  73. package/integrations/data-defined/examples/asana-slack.definition.json +220 -0
  74. package/integrations/data-defined/examples/calendly-salesforce.definition.json +459 -0
  75. package/integrations/data-defined/examples/docusign-salesforce.definition.json +305 -0
  76. package/integrations/data-defined/examples/fathom-hubspot-notion.definition.json +575 -0
  77. package/integrations/data-defined/examples/github-linear.definition.json +316 -0
  78. package/integrations/data-defined/examples/hubspot-slack.definition.json +409 -0
  79. package/integrations/data-defined/examples/klaviyo-hubspot.definition.json +339 -0
  80. package/integrations/data-defined/examples/phone-hubspot.definition.json +637 -0
  81. package/integrations/data-defined/examples/pipedrive-hubspot.definition.json +136 -0
  82. package/integrations/data-defined/examples/pipedrive-phone.definition.json +340 -0
  83. package/integrations/data-defined/examples/records/phone-hubspot.definition-version.json +652 -0
  84. package/integrations/data-defined/examples/records/phone-hubspot.integration.json +86 -0
  85. package/integrations/data-defined/examples/records/phone-hubspot.mappings.json +39 -0
  86. package/integrations/data-defined/examples/records/phone-hubspot.workflow-run.json +62 -0
  87. package/integrations/data-defined/examples/shopify-klaviyo.definition.json +488 -0
  88. package/integrations/data-defined/examples/shopify-netsuite.definition.json +622 -0
  89. package/integrations/data-defined/examples/stripe-quickbooks.definition.json +476 -0
  90. package/integrations/data-defined/examples/webflow-hubspot-slack.definition.json +262 -0
  91. package/integrations/data-defined/examples/zendesk-jira.definition.json +604 -0
  92. package/integrations/data-defined/expressions.js +225 -0
  93. package/integrations/data-defined/index.js +39 -0
  94. package/integrations/data-defined/interpreter.js +89 -0
  95. package/integrations/data-defined/manifests.js +125 -0
  96. package/integrations/data-defined/repositories/definition-version-repository.js +67 -0
  97. package/integrations/data-defined/repositories/workflow-run-repository.js +59 -0
  98. package/integrations/data-defined/router.js +97 -0
  99. package/integrations/data-defined/runtime.js +208 -0
  100. package/integrations/data-defined/schema/capability-manifest.v0.1.schema.json +377 -0
  101. package/integrations/data-defined/schema/installation.v0.1.schema.json +83 -0
  102. package/integrations/data-defined/schema/integration-definition.v0.1.schema.json +848 -0
  103. package/integrations/data-defined/schema/records.md +98 -0
  104. package/integrations/data-defined/schema-check.js +58 -0
  105. package/integrations/data-defined/scripts/validate.js +66 -0
  106. package/integrations/data-defined/steps.js +89 -0
  107. package/integrations/data-defined/transforms.js +91 -0
  108. package/integrations/data-defined/use-cases/definition-use-cases.js +187 -0
  109. package/integrations/data-defined/validate.js +370 -0
  110. package/integrations/data-defined/versioning.js +161 -0
  111. package/integrations/data-defined/webhooks.js +119 -0
  112. package/integrations/index.js +0 -1
  113. package/integrations/integration-base.js +29 -0
  114. package/integrations/integration-router.js +101 -131
  115. package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
  116. package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
  117. package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
  118. package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
  119. package/integrations/repositories/integration-mapping-repository.js +29 -0
  120. package/integrations/repositories/integration-repository-documentdb.js +3 -35
  121. package/integrations/repositories/integration-repository-interface.js +1 -17
  122. package/integrations/repositories/integration-repository-mongo.js +77 -60
  123. package/integrations/repositories/integration-repository-postgres.js +76 -62
  124. package/integrations/repositories/process-repository-mongo.js +1 -1
  125. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  126. package/integrations/use-cases/create-integration.js +20 -55
  127. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  128. package/integrations/use-cases/index.js +1 -7
  129. package/integrations/utils/map-integration-dto.js +14 -43
  130. package/modules/index.js +0 -14
  131. package/modules/module-factory.js +0 -12
  132. package/modules/module.js +2 -53
  133. package/modules/repositories/module-repository-documentdb.js +6 -51
  134. package/modules/repositories/module-repository-interface.js +2 -19
  135. package/modules/repositories/module-repository-mongo.js +74 -67
  136. package/modules/repositories/module-repository-postgres.js +74 -66
  137. package/modules/repositories/module-repository.js +1 -2
  138. package/modules/requester/requester.js +5 -35
  139. package/modules/use-cases/get-entities-for-user.js +13 -26
  140. package/modules/use-cases/get-entity-options-by-id.js +4 -10
  141. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  142. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  143. package/modules/use-cases/get-module.js +4 -10
  144. package/modules/use-cases/process-authorization-callback.js +19 -53
  145. package/modules/use-cases/refresh-entity-options.js +4 -10
  146. package/modules/use-cases/test-module-auth.js +4 -10
  147. package/package.json +6 -6
  148. package/prisma-mongodb/schema.prisma +50 -86
  149. package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +66 -0
  150. package/prisma-postgresql/schema.prisma +48 -81
  151. package/queues/index.js +0 -25
  152. package/types/index.d.ts +0 -2
  153. package/user/repositories/user-repository-documentdb.js +2 -27
  154. package/user/repositories/user-repository-interface.js +0 -18
  155. package/user/repositories/user-repository-mongo.js +0 -24
  156. package/user/repositories/user-repository-postgres.js +0 -18
  157. package/user/use-cases/authenticate-user.js +1 -7
  158. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  159. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  160. package/user/user.js +1 -64
  161. package/credential/use-cases/credential-operations-for-user.js +0 -202
  162. package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
  163. package/database/organization-where.js +0 -18
  164. package/database/unique-violation.js +0 -25
  165. package/extensions/bootstrap-runner.js +0 -43
  166. package/extensions/extension-loader.js +0 -187
  167. package/extensions/index.js +0 -41
  168. package/extensions/initialize-app.js +0 -123
  169. package/extensions/route-mounter.js +0 -103
  170. package/extensions/schema-composer.js +0 -188
  171. package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
  172. package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
  173. package/integrations/repositories/integration-not-found.js +0 -14
  174. package/integrations/schedule-config.js +0 -80
  175. package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
  176. package/integrations/use-cases/integration-operations-for-user.js +0 -256
  177. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  178. package/management-api/create-management-api-router.js +0 -49
  179. package/management-api/error-response.js +0 -116
  180. package/management-api/management-api-config.js +0 -151
  181. package/management-api/meta-router.js +0 -76
  182. package/management-api/openapi/build-openapi.js +0 -144
  183. package/management-api/openapi/components.js +0 -415
  184. package/management-api/openapi/v1-operations.js +0 -83
  185. package/management-api/openapi/v2-operations.js +0 -264
  186. package/management-api/route-registry.js +0 -411
  187. package/management-api/test-support/http-client.js +0 -39
  188. package/management-api/use-cases/get-management-api-meta.js +0 -57
  189. package/management-api/v1-deprecation.js +0 -120
  190. package/management-api/v2/create-v2-router.js +0 -85
  191. package/management-api/v2/dependencies.js +0 -281
  192. package/management-api/v2/dto.js +0 -143
  193. package/management-api/v2/handlers/authorization.js +0 -94
  194. package/management-api/v2/handlers/credentials.js +0 -33
  195. package/management-api/v2/handlers/entities.js +0 -77
  196. package/management-api/v2/handlers/integrations.js +0 -128
  197. package/management-api/v2/handlers/proxy.js +0 -34
  198. package/management-api/v2/request.js +0 -85
  199. package/management-api/v2/test-support/fake-dependencies.js +0 -103
  200. package/modules/domain/authorization-session.js +0 -144
  201. package/modules/entity-scope.js +0 -15
  202. package/modules/entity-type-registry.js +0 -311
  203. package/modules/proxy/proxy-bases.js +0 -169
  204. package/modules/proxy/proxy-errors.js +0 -31
  205. package/modules/proxy/proxy-path.js +0 -183
  206. package/modules/proxy/proxy-policy.js +0 -277
  207. package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
  208. package/modules/repositories/authorization-session-repository-factory.js +0 -31
  209. package/modules/repositories/authorization-session-repository-interface.js +0 -47
  210. package/modules/repositories/authorization-session-repository-prisma.js +0 -87
  211. package/modules/repositories/entity-not-found.js +0 -18
  212. package/modules/requester/proxy-request.js +0 -245
  213. package/modules/scope-config.js +0 -192
  214. package/modules/scope-policy.js +0 -112
  215. package/modules/scope-resolution.js +0 -288
  216. package/modules/scope-visibility.js +0 -247
  217. package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
  218. package/modules/tests/doubles/ownership-matching.js +0 -18
  219. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  220. package/modules/use-cases/create-entity-from-credential.js +0 -113
  221. package/modules/use-cases/entity-operations-for-user.js +0 -162
  222. package/modules/use-cases/entity-types.js +0 -139
  223. package/modules/use-cases/execute-entity-proxy-request.js +0 -331
  224. package/modules/use-cases/get-authorization-requirements.js +0 -100
  225. package/modules/use-cases/get-entity-module-for-user.js +0 -113
  226. package/modules/use-cases/manage-access.js +0 -21
  227. package/modules/use-cases/multi-step-authorization.js +0 -329
  228. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
  229. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  230. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  231. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
  232. package/providers/index.js +0 -23
  233. package/providers/resolve-provider.js +0 -93
  234. package/providers/runtime-detection.js +0 -124
  235. package/queues/providers/index.js +0 -11
  236. package/queues/providers/netlify-background-provider.js +0 -150
  237. package/queues/providers/qstash-queue-provider.js +0 -219
  238. package/queues/providers/sqs-queue-provider.js +0 -70
  239. package/queues/queue-client-interface.js +0 -55
  240. package/queues/queue-message-auth.js +0 -193
  241. package/queues/queue-provider-factory.js +0 -101
  242. package/queues/queue-provider.js +0 -65
  243. package/queues/sqs-queue-client.js +0 -81
  244. package/types/modules/index.d.ts +0 -214
  245. package/types/user/index.d.ts +0 -134
  246. package/user/adopter-jwt-config.js +0 -177
  247. package/user/adopter-jwt-errors.js +0 -59
  248. package/user/adopter-token-verifier.js +0 -122
  249. package/user/app-user-helpers.js +0 -97
  250. package/user/create-user-authentication.js +0 -64
  251. package/user/membership.js +0 -51
  252. package/user/repositories/organization-membership-record.js +0 -19
  253. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  254. package/user/repositories/organization-membership-repository-factory.js +0 -42
  255. package/user/repositories/organization-membership-repository-interface.js +0 -57
  256. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  257. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  258. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  259. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  260. package/user/use-cases/ensure-organization-membership.js +0 -92
  261. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -1,5 +1,4 @@
1
1
  const { Module } = require('../module');
2
- const { DEFAULT_SCOPE_POLICY } = require('../scope-policy');
3
2
 
4
3
  class TestModuleAuth {
5
4
  /**
@@ -7,11 +6,9 @@ class TestModuleAuth {
7
6
  * @param {import('../repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository - Repository for module data operations.
8
7
  * @param {Array<Object>} params.moduleDefinitions - Array of module definitions.
9
8
  */
10
- constructor({ moduleRepository, moduleDefinitions, scopePolicy }) {
9
+ constructor({ moduleRepository, moduleDefinitions }) {
11
10
  this.moduleRepository = moduleRepository;
12
11
  this.moduleDefinitions = moduleDefinitions;
13
- // Who may load an entity follows its scope (ADR-056 §3.6).
14
- this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
15
12
  }
16
13
 
17
14
  /**
@@ -38,7 +35,7 @@ class TestModuleAuth {
38
35
 
39
36
  // Validate entity ownership
40
37
  const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
41
- ? this.scopePolicy.canUseEntity(userIdOrUser, entity)
38
+ ? userIdOrUser.ownsUserId(entity.userId)
42
39
  : entity.userId?.toString() === userId?.toString();
43
40
 
44
41
  if (!isOwned) {
@@ -60,11 +57,8 @@ class TestModuleAuth {
60
57
  );
61
58
  }
62
59
 
63
- // Built for the entity's OWNER, not the caller: a caller may use an
64
- // organization's entity, and a token refresh during this call must
65
- // update the owner's credential, not create one under the caller.
66
- const module = await Module.create({
67
- userId: entity.userId ?? userId,
60
+ const module = new Module({
61
+ userId,
68
62
  entity,
69
63
  definition: moduleDefinition,
70
64
  });
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.690.08cd07e.0",
4
+ "version": "2.0.0--canary.693.d8859c2.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -28,7 +28,6 @@
28
28
  "express-async-handler": "^1.2.0",
29
29
  "form-data": "^4.0.6",
30
30
  "fs-extra": "^11.2.0",
31
- "jose": "^5.10.0",
32
31
  "lodash": "4.18.1",
33
32
  "lodash.get": "^4.4.2",
34
33
  "node-fetch": "^2.6.7",
@@ -48,9 +47,9 @@
48
47
  }
49
48
  },
50
49
  "devDependencies": {
51
- "@friggframework/eslint-config": "2.0.0--canary.690.08cd07e.0",
52
- "@friggframework/prettier-config": "2.0.0--canary.690.08cd07e.0",
53
- "@friggframework/test": "2.0.0--canary.690.08cd07e.0",
50
+ "@friggframework/eslint-config": "2.0.0--canary.693.d8859c2.0",
51
+ "@friggframework/prettier-config": "2.0.0--canary.693.d8859c2.0",
52
+ "@friggframework/test": "2.0.0--canary.693.d8859c2.0",
54
53
  "@prisma/client": "^6.19.3",
55
54
  "@types/lodash": "4.17.15",
56
55
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -68,6 +67,7 @@
68
67
  "scripts": {
69
68
  "lint:fix": "prettier --write --loglevel error . && eslint . --fix",
70
69
  "test": "jest --passWithNoTests # TODO",
70
+ "validate:definitions": "node integrations/data-defined/scripts/validate.js",
71
71
  "prisma:generate:mongo": "npx prisma generate --schema ./prisma-mongodb/schema.prisma",
72
72
  "prisma:generate:postgres": "npx prisma generate --schema ./prisma-postgresql/schema.prisma",
73
73
  "prisma:generate": "npm run prisma:generate:mongo && npm run prisma:generate:postgres",
@@ -90,5 +90,5 @@
90
90
  "publishConfig": {
91
91
  "access": "public"
92
92
  },
93
- "gitHead": "08cd07ef5720ad7a0d5141a442613d56d82e3730"
93
+ "gitHead": "d8859c23160346b2f8b15cd2caffc814dfd5bf6d"
94
94
  }
@@ -56,14 +56,6 @@ model User {
56
56
  integrations Integration[]
57
57
  processes Process[]
58
58
 
59
- // Organization memberships (ADR-056 §3.6). `organizationId` above is the legacy
60
- // single-org pointer: set on a person's first link, never moved afterwards, and
61
- // read as an implicit membership when no row exists for it.
62
- // As an individual: the organizations this person belongs to.
63
- memberships OrganizationMembership[] @relation("IndividualMemberships")
64
- // As an organization: the people who belong to it.
65
- orgMembers OrganizationMembership[] @relation("OrganizationMemberships")
66
-
67
59
  @@unique([username, appUserId])
68
60
  @@index([type])
69
61
  @@index([appUserId])
@@ -134,14 +126,6 @@ model Entity {
134
126
  moduleName String?
135
127
  externalId String?
136
128
 
137
- // Organization this entity was connected in (ADR-056 §3.6): the active
138
- // organization for `individual`, the owner for `organization`, null for
139
- // `global`. Null on an `individual` entity is also what every row written
140
- // before this column existed has, and what `individualAcross: 'all'` writes.
141
- // Deliberately not a relation: a deleted organization must leave its rows
142
- // invisible, not turn them into "unscoped" ones (SetNull would do that).
143
- organizationId String? @db.ObjectId
144
-
145
129
  data Json @default("{}")
146
130
 
147
131
  createdAt DateTime @default(now())
@@ -158,7 +142,6 @@ model Entity {
158
142
  associationObjects AssociationObject[]
159
143
 
160
144
  @@index([userId])
161
- @@index([userId, organizationId])
162
145
  @@index([externalId])
163
146
  @@index([moduleName])
164
147
  @@index([credentialId])
@@ -176,11 +159,6 @@ model Integration {
176
159
  user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
177
160
  status IntegrationStatus @default(IN_CREATION)
178
161
 
179
- // Organization this integration was created in (ADR-056 §3.6): the active
180
- // organization at create time. Null for rows that predate the column. Not a
181
- // relation, for the same reason as Entity.organizationId.
182
- organizationId String? @db.ObjectId
183
-
184
162
  // Configuration and version
185
163
  config Json? // Integration configuration object
186
164
  version String?
@@ -203,9 +181,9 @@ model Integration {
203
181
  syncs Sync[]
204
182
  mappings IntegrationMapping[]
205
183
  processes Process[]
184
+ workflowRuns WorkflowRun[]
206
185
 
207
186
  @@index([userId])
208
- @@index([userId, organizationId])
209
187
  @@index([status])
210
188
  @@map("Integration")
211
189
  }
@@ -477,71 +455,57 @@ model UsageCounter {
477
455
  @@map("UsageCounter")
478
456
  }
479
457
 
480
- // ============================================================================
481
- // MANAGEMENT API MODELS
482
- // ============================================================================
483
-
484
- /// Multi-step authorization state for Management API v2 (/api/v2/authorize,
485
- /// /api/v2/credentials/:id/reauthorize). Short-lived: expired documents are
486
- /// removed opportunistically. stepData is encrypted at rest.
487
- model AuthorizationSession {
488
- id String @id @default(auto()) @map("_id") @db.ObjectId
489
- sessionId String @unique
490
- userId String
491
- entityType String
492
- credentialId String?
493
- currentStep Int @default(1)
494
- maxSteps Int
495
- stepData Json @default("{}")
496
- expiresAt DateTime
497
- completed Boolean @default(false)
498
-
499
- createdAt DateTime @default(now())
500
- updatedAt DateTime @updatedAt
501
-
502
- @@index([userId, entityType])
503
- @@index([expiresAt])
504
- @@map("AuthorizationSession")
458
+ // Data-defined integrations (ADR-063): one immutable row per published definition version.
459
+ enum DefinitionStatus {
460
+ DRAFT
461
+ PUBLISHED
462
+ DEPRECATED
463
+ RETIRED
505
464
  }
506
465
 
507
- // ============================================================================
508
- // ORGANIZATION MEMBERSHIP (ADR-056 §3.6)
509
- // ============================================================================
510
-
511
- /// One person's membership of one organization. Unique on the pair, so adding a
512
- /// membership is an idempotent upsert. `orgRole` is the organization role the
513
- /// adopter token carried when the membership was created (MEMBER when it named none). NoAction on delete
514
- /// because MongoDB gives no referential integrity: delete a user's memberships
515
- /// before the user.
516
- model OrganizationMembership {
517
- id String @id @default(auto()) @map("_id") @db.ObjectId
518
- individualUserId String @db.ObjectId
519
- organizationUserId String @db.ObjectId
520
- orgRole String @default("MEMBER")
521
- createdAt DateTime @default(now())
522
-
523
- individualUser User @relation("IndividualMemberships", fields: [individualUserId], references: [id], onDelete: NoAction, onUpdate: NoAction)
524
- organizationUser User @relation("OrganizationMemberships", fields: [organizationUserId], references: [id], onDelete: NoAction, onUpdate: NoAction)
525
-
526
- @@unique([individualUserId, organizationUserId])
527
- @@index([organizationUserId])
528
- @@map("OrganizationMembership")
466
+ model IntegrationDefinitionVersion {
467
+ id String @id @default(auto()) @map("_id") @db.ObjectId
468
+ definitionId String
469
+ version String
470
+ schemaVersion String
471
+ status DefinitionStatus @default(DRAFT)
472
+ checksum String
473
+ document Json
474
+ changeLevel String?
475
+ previousVersion String?
476
+ notes String?
477
+ createdBy String?
478
+ publishedAt DateTime?
479
+ createdAt DateTime @default(now())
480
+ updatedAt DateTime @updatedAt
481
+
482
+ @@unique([definitionId, version])
483
+ @@index([definitionId, status])
484
+ @@map("IntegrationDefinitionVersion")
529
485
  }
530
486
 
531
- /// One-time scheduled jobs for poll-and-dispatch scheduling pattern.
532
- /// Used by providers without native one-time scheduling APIs (e.g., Netlify).
533
- /// A cron function queries for due jobs and dispatches them to a queue.
534
- model ScheduledJob {
535
- id String @id @default(auto()) @map("_id") @db.ObjectId
536
- scheduleName String @unique
537
- scheduledAt DateTime
538
- queueResourceId String
539
- payload Json?
540
- state String @default("PENDING")
541
-
542
- createdAt DateTime @default(now())
543
- updatedAt DateTime @updatedAt
544
-
545
- @@index([state, scheduledAt])
546
- @@map("ScheduledJob")
487
+ // One row per data-defined workflow run. trace holds step ids, statuses, timings and error messages, never request or
488
+ // response bodies.
489
+ model WorkflowRun {
490
+ id String @id @default(auto()) @map("_id") @db.ObjectId
491
+ integrationId String @db.ObjectId
492
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
493
+ definitionId String
494
+ definitionVersion String
495
+ workflowId String
496
+ triggerType String
497
+ eventId String?
498
+ status String
499
+ reason String?
500
+ error String?
501
+ retryable Boolean @default(false)
502
+ trace Json @default("[]")
503
+ startedAt DateTime
504
+ endedAt DateTime?
505
+ createdAt DateTime @default(now())
506
+
507
+ @@index([integrationId, startedAt])
508
+ @@index([integrationId, workflowId, startedAt])
509
+ @@index([integrationId, status])
510
+ @@map("WorkflowRun")
547
511
  }
@@ -0,0 +1,66 @@
1
+ -- Data-defined integrations (ADR-063): published definition versions and workflow runs.
2
+ --
3
+ -- Additive and non-destructive: one enum type and two tables. No existing table or row is touched; WorkflowRun
4
+ -- references Integration with ON DELETE CASCADE, so deleting an installation removes its run history.
5
+
6
+ -- CreateEnum
7
+ CREATE TYPE "DefinitionStatus" AS ENUM ('DRAFT', 'PUBLISHED', 'DEPRECATED', 'RETIRED');
8
+
9
+ -- CreateTable
10
+ CREATE TABLE "IntegrationDefinitionVersion" (
11
+ "id" SERIAL NOT NULL,
12
+ "definitionId" TEXT NOT NULL,
13
+ "version" TEXT NOT NULL,
14
+ "schemaVersion" TEXT NOT NULL,
15
+ "status" "DefinitionStatus" NOT NULL DEFAULT 'DRAFT',
16
+ "checksum" TEXT NOT NULL,
17
+ "document" JSONB NOT NULL,
18
+ "changeLevel" TEXT,
19
+ "previousVersion" TEXT,
20
+ "notes" TEXT,
21
+ "createdBy" TEXT,
22
+ "publishedAt" TIMESTAMP(3),
23
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
24
+ "updatedAt" TIMESTAMP(3) NOT NULL,
25
+
26
+ CONSTRAINT "IntegrationDefinitionVersion_pkey" PRIMARY KEY ("id")
27
+ );
28
+
29
+ -- CreateTable
30
+ CREATE TABLE "WorkflowRun" (
31
+ "id" SERIAL NOT NULL,
32
+ "integrationId" INTEGER NOT NULL,
33
+ "definitionId" TEXT NOT NULL,
34
+ "definitionVersion" TEXT NOT NULL,
35
+ "workflowId" TEXT NOT NULL,
36
+ "triggerType" TEXT NOT NULL,
37
+ "eventId" TEXT,
38
+ "status" TEXT NOT NULL,
39
+ "reason" TEXT,
40
+ "error" TEXT,
41
+ "retryable" BOOLEAN NOT NULL DEFAULT false,
42
+ "trace" JSONB NOT NULL DEFAULT '[]',
43
+ "startedAt" TIMESTAMP(3) NOT NULL,
44
+ "endedAt" TIMESTAMP(3),
45
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
46
+
47
+ CONSTRAINT "WorkflowRun_pkey" PRIMARY KEY ("id")
48
+ );
49
+
50
+ -- CreateIndex
51
+ CREATE UNIQUE INDEX "IntegrationDefinitionVersion_definitionId_version_key" ON "IntegrationDefinitionVersion"("definitionId", "version");
52
+
53
+ -- CreateIndex
54
+ CREATE INDEX "IntegrationDefinitionVersion_definitionId_status_idx" ON "IntegrationDefinitionVersion"("definitionId", "status");
55
+
56
+ -- CreateIndex
57
+ CREATE INDEX "WorkflowRun_integrationId_startedAt_idx" ON "WorkflowRun"("integrationId", "startedAt");
58
+
59
+ -- CreateIndex
60
+ CREATE INDEX "WorkflowRun_integrationId_workflowId_startedAt_idx" ON "WorkflowRun"("integrationId", "workflowId", "startedAt");
61
+
62
+ -- CreateIndex
63
+ CREATE INDEX "WorkflowRun_integrationId_status_idx" ON "WorkflowRun"("integrationId", "status");
64
+
65
+ -- AddForeignKey
66
+ ALTER TABLE "WorkflowRun" ADD CONSTRAINT "WorkflowRun_integrationId_fkey" FOREIGN KEY ("integrationId") REFERENCES "Integration"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -56,14 +56,6 @@ model User {
56
56
  integrations Integration[]
57
57
  processes Process[]
58
58
 
59
- // Organization memberships (ADR-056 §3.6). `organizationId` above is the legacy
60
- // single-org pointer: set on a person's first link, never moved afterwards, and
61
- // read as an implicit membership when no row exists for it.
62
- // As an individual: the organizations this person belongs to.
63
- memberships OrganizationMembership[] @relation("IndividualMemberships")
64
- // As an organization: the people who belong to it.
65
- orgMembers OrganizationMembership[] @relation("OrganizationMemberships")
66
-
67
59
  @@unique([username, appUserId])
68
60
  @@index([type])
69
61
  @@index([appUserId])
@@ -131,14 +123,6 @@ model Entity {
131
123
  moduleName String?
132
124
  externalId String?
133
125
 
134
- // Organization this entity was connected in (ADR-056 §3.6): the active
135
- // organization for `individual`, the owner for `organization`, null for
136
- // `global`. Null on an `individual` entity is also what every row written
137
- // before this column existed has, and what `individualAcross: 'all'` writes.
138
- // Deliberately not a relation: a deleted organization must leave its rows
139
- // invisible, not turn them into "unscoped" ones (SetNull would do that).
140
- organizationId Int?
141
-
142
126
  data Json @default("{}")
143
127
 
144
128
  createdAt DateTime @default(now())
@@ -152,7 +136,6 @@ model Entity {
152
136
  associationObjects AssociationObject[]
153
137
 
154
138
  @@index([userId])
155
- @@index([userId, organizationId])
156
139
  @@index([externalId])
157
140
  @@index([moduleName])
158
141
  @@index([credentialId])
@@ -169,11 +152,6 @@ model Integration {
169
152
  user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
170
153
  status IntegrationStatus @default(IN_CREATION)
171
154
 
172
- // Organization this integration was created in (ADR-056 §3.6): the active
173
- // organization at create time. Null for rows that predate the column. Not a
174
- // relation, for the same reason as Entity.organizationId.
175
- organizationId Int?
176
-
177
155
  // Configuration and version
178
156
  config Json? // Integration configuration object
179
157
  version String?
@@ -195,9 +173,9 @@ model Integration {
195
173
  syncs Sync[]
196
174
  mappings IntegrationMapping[]
197
175
  processes Process[]
176
+ workflowRuns WorkflowRun[]
198
177
 
199
178
  @@index([userId])
200
- @@index([userId, organizationId])
201
179
  @@index([status])
202
180
  }
203
181
 
@@ -458,66 +436,55 @@ model UsageCounter {
458
436
  @@index([metric, integrationType, window])
459
437
  }
460
438
 
461
- // ============================================================================
462
- // MANAGEMENT API MODELS
463
- // ============================================================================
464
-
465
- /// Multi-step authorization state for Management API v2 (/api/v2/authorize,
466
- /// /api/v2/credentials/:id/reauthorize). Short-lived: expired rows are removed
467
- /// opportunistically. stepData is encrypted at rest.
468
- model AuthorizationSession {
469
- id Int @id @default(autoincrement())
470
- sessionId String @unique
471
- userId String
472
- entityType String
473
- credentialId String?
474
- currentStep Int @default(1)
475
- maxSteps Int
476
- stepData Json @default("{}")
477
- expiresAt DateTime
478
- completed Boolean @default(false)
479
-
480
- createdAt DateTime @default(now())
481
- updatedAt DateTime @updatedAt
482
-
483
- @@index([userId, entityType])
484
- @@index([expiresAt])
439
+ // Data-defined integrations (ADR-063): one immutable row per published definition version.
440
+ enum DefinitionStatus {
441
+ DRAFT
442
+ PUBLISHED
443
+ DEPRECATED
444
+ RETIRED
485
445
  }
486
446
 
487
- // ============================================================================
488
- // ORGANIZATION MEMBERSHIP (ADR-056 §3.6)
489
- // ============================================================================
490
-
491
- /// One person's membership of one organization. Unique on the pair, so adding a
492
- /// membership is an idempotent upsert. `orgRole` is the organization role the
493
- /// adopter token carried when the membership was created (MEMBER when it named none).
494
- model OrganizationMembership {
495
- id Int @id @default(autoincrement())
496
- individualUserId Int
497
- organizationUserId Int
498
- orgRole String @default("MEMBER")
499
- createdAt DateTime @default(now())
500
-
501
- individualUser User @relation("IndividualMemberships", fields: [individualUserId], references: [id], onDelete: Cascade, onUpdate: NoAction)
502
- organizationUser User @relation("OrganizationMemberships", fields: [organizationUserId], references: [id], onDelete: Cascade, onUpdate: NoAction)
503
-
504
- @@unique([individualUserId, organizationUserId])
505
- @@index([organizationUserId])
447
+ model IntegrationDefinitionVersion {
448
+ id Int @id @default(autoincrement())
449
+ definitionId String
450
+ version String
451
+ schemaVersion String
452
+ status DefinitionStatus @default(DRAFT)
453
+ checksum String
454
+ document Json
455
+ changeLevel String?
456
+ previousVersion String?
457
+ notes String?
458
+ createdBy String?
459
+ publishedAt DateTime?
460
+ createdAt DateTime @default(now())
461
+ updatedAt DateTime @updatedAt
462
+
463
+ @@unique([definitionId, version])
464
+ @@index([definitionId, status])
506
465
  }
507
466
 
508
- /// One-time scheduled jobs for poll-and-dispatch scheduling pattern.
509
- /// Used by providers without native one-time scheduling APIs (e.g., Netlify).
510
- /// A cron function queries for due jobs and dispatches them to a queue.
511
- model ScheduledJob {
512
- id Int @id @default(autoincrement())
513
- scheduleName String @unique
514
- scheduledAt DateTime
515
- queueResourceId String
516
- payload Json?
517
- state String @default("PENDING")
518
-
519
- createdAt DateTime @default(now())
520
- updatedAt DateTime @updatedAt
521
-
522
- @@index([state, scheduledAt])
467
+ // One row per data-defined workflow run. trace holds step ids, statuses, timings and error messages, never request or
468
+ // response bodies.
469
+ model WorkflowRun {
470
+ id Int @id @default(autoincrement())
471
+ integrationId Int
472
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
473
+ definitionId String
474
+ definitionVersion String
475
+ workflowId String
476
+ triggerType String
477
+ eventId String?
478
+ status String
479
+ reason String?
480
+ error String?
481
+ retryable Boolean @default(false)
482
+ trace Json @default("[]")
483
+ startedAt DateTime
484
+ endedAt DateTime?
485
+ createdAt DateTime @default(now())
486
+
487
+ @@index([integrationId, startedAt])
488
+ @@index([integrationId, workflowId, startedAt])
489
+ @@index([integrationId, status])
523
490
  }
package/queues/index.js CHANGED
@@ -1,29 +1,4 @@
1
1
  const { QueuerUtil } = require('./queuer-util');
2
- const { QueueProvider } = require('./queue-provider');
3
- const { QueueClientInterface } = require('./queue-client-interface');
4
- const {
5
- createQueueProvider,
6
- determineProvider,
7
- QUEUE_PROVIDERS,
8
- } = require('./queue-provider-factory');
9
- const {
10
- NetlifyBackgroundProvider,
11
- QStashQueueProvider,
12
- } = require('./providers');
13
-
14
2
  module.exports = {
15
3
  QueuerUtil,
16
- QueueProvider,
17
- QueueClientInterface,
18
- createQueueProvider,
19
- determineProvider,
20
- QUEUE_PROVIDERS,
21
- NetlifyBackgroundProvider,
22
- QStashQueueProvider,
23
- get SqsQueueProvider() {
24
- return require('./providers/sqs-queue-provider').SqsQueueProvider;
25
- },
26
- get SqsQueueClient() {
27
- return require('./sqs-queue-client').SqsQueueClient;
28
- },
29
4
  };
package/types/index.d.ts CHANGED
@@ -12,5 +12,3 @@
12
12
  /// <reference path="./eslint-config/index.d.ts" />
13
13
  /// <reference path="./associations/index.d.ts" />
14
14
  /// <reference path="./syncs/index.d.ts" />
15
- /// <reference path="./user/index.d.ts" />
16
- /// <reference path="./modules/index.d.ts" />
@@ -4,7 +4,7 @@ const {
4
4
  toObjectId,
5
5
  fromObjectId,
6
6
  findOne,
7
- findManyDrained,
7
+ findMany,
8
8
  insertOne,
9
9
  updateOne,
10
10
  deleteOne,
@@ -72,9 +72,7 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
72
72
  }
73
73
 
74
74
  async findIndividualUsersByOrganizationId(organizationUserId) {
75
- // Drained: findMany returns only the first cursor batch, which would
76
- // drop people from a large organization.
77
- const docs = await findManyDrained(this.prisma, 'User', {
75
+ const docs = await findMany(this.prisma, 'User', {
78
76
  organizationId: toObjectId(organizationUserId),
79
77
  type: 'INDIVIDUAL',
80
78
  });
@@ -423,29 +421,6 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
423
421
  );
424
422
  return this._mapUser(decrypted);
425
423
  }
426
-
427
- /**
428
- * Set the legacy organizationId only if it is still null or absent (see the
429
- * interface): the filter carries the condition, so it is one atomic update.
430
- * @param {string} individualUserId
431
- * @param {string} organizationUserId
432
- * @returns {Promise<Object|null>} The individual user as stored afterwards
433
- */
434
- async linkIndividualToOrganizationIfUnset(individualUserId, organizationUserId) {
435
- await updateOne(
436
- this.prisma,
437
- 'User',
438
- {
439
- _id: toObjectId(individualUserId),
440
- type: 'INDIVIDUAL',
441
- // Raw MongoDB query (not Prisma): `{ field: null }` matches both a
442
- // null value and a missing field, so no `$exists` branch is needed.
443
- organizationId: null,
444
- },
445
- { $set: { organizationId: toObjectId(organizationUserId) } }
446
- );
447
- return this.findIndividualUserById(individualUserId);
448
- }
449
424
  }
450
425
 
451
426
  module.exports = { UserRepositoryDocumentDB };
@@ -209,24 +209,6 @@ class UserRepositoryInterface {
209
209
  'Method linkIndividualToOrganization must be implemented by subclass'
210
210
  );
211
211
  }
212
-
213
- /**
214
- * Set an individual user's legacy `organizationId` pointer only if it is
215
- * not set yet (their first link), decided by the database in the same
216
- * statement so two concurrent first logins cannot overwrite each other.
217
- * Never changes an existing pointer (ADR-056 §3.6).
218
- *
219
- * @param {string|number} individualUserId - Individual user ID
220
- * @param {string|number} organizationUserId - Organization user ID
221
- * @returns {Promise<Object|null>} The individual user as stored afterwards
222
- * (null if there is no such individual)
223
- * @abstract
224
- */
225
- async linkIndividualToOrganizationIfUnset(individualUserId, organizationUserId) {
226
- throw new Error(
227
- 'Method linkIndividualToOrganizationIfUnset must be implemented by subclass'
228
- );
229
- }
230
212
  }
231
213
 
232
214
  module.exports = { UserRepositoryInterface };
@@ -318,30 +318,6 @@ class UserRepositoryMongo extends UserRepositoryInterface {
318
318
  },
319
319
  });
320
320
  }
321
-
322
- /**
323
- * Set the legacy organizationId only if it is still null (see the interface).
324
- * @param {string} individualUserId
325
- * @param {string} organizationUserId
326
- * @returns {Promise<Object|null>} The individual user as stored afterwards
327
- */
328
- async linkIndividualToOrganizationIfUnset(individualUserId, organizationUserId) {
329
- await this.prisma.user.updateMany({
330
- where: {
331
- id: individualUserId,
332
- type: 'INDIVIDUAL',
333
- // Prisma on MongoDB: `null` matches only an explicit null, and
334
- // `isSet: false` only a missing field. An individual created
335
- // without the field is the common case, so match both.
336
- OR: [
337
- { organizationId: null },
338
- { organizationId: { isSet: false } },
339
- ],
340
- },
341
- data: { organizationId: organizationUserId },
342
- });
343
- return this.findIndividualUserById(individualUserId);
344
- }
345
321
  }
346
322
 
347
323
  module.exports = { UserRepositoryMongo };