@friggframework/core 2.0.0--canary.703.12b980b.0 → 2.0.0--canary.707.df80bfd.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 (308) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/CLAUDE.md +28 -0
  3. package/README.md +105 -0
  4. package/application/commands/integration-commands.js +20 -10
  5. package/application/commands/scheduler-commands.js +11 -7
  6. package/application/commands/user-commands.js +17 -5
  7. package/application/index.js +6 -2
  8. package/core/Worker.js +31 -7
  9. package/core/create-handler.js +9 -0
  10. package/credential/repositories/credential-repository-documentdb.js +34 -0
  11. package/credential/repositories/credential-repository-interface.js +15 -0
  12. package/credential/repositories/credential-repository-mongo.js +23 -0
  13. package/credential/repositories/credential-repository-postgres.js +28 -0
  14. package/credential/use-cases/credential-operations-for-user.js +202 -0
  15. package/credential/use-cases/reauthorize-credential-for-user.js +73 -0
  16. package/database/config.js +48 -24
  17. package/database/documentdb-encryption-service.js +9 -22
  18. package/database/documentdb-utils.js +2 -2
  19. package/database/encryption/README.md +78 -14
  20. package/database/encryption/documentdb-encryption-service.md +78 -1
  21. package/database/encryption/encryption-config.js +219 -0
  22. package/database/encryption/encryption-schema-registry.js +121 -24
  23. package/database/encryption/field-encryption-service.js +69 -5
  24. package/database/encryption/integration-mapping-encryption.js +1 -1
  25. package/database/encryption/test-support/fake-ciphertext.js +15 -0
  26. package/database/organization-where.js +18 -0
  27. package/database/prisma.js +15 -38
  28. package/database/unique-violation.js +25 -0
  29. package/database/use-cases/check-encryption-health-use-case.js +43 -52
  30. package/errors/index.js +0 -4
  31. package/extensions/bootstrap-runner.js +43 -0
  32. package/extensions/extension-loader.js +230 -0
  33. package/extensions/index.js +44 -0
  34. package/extensions/initialize-app.js +123 -0
  35. package/extensions/prisma-composer.js +270 -0
  36. package/extensions/route-mounter.js +103 -0
  37. package/extensions/schema-composer.js +188 -0
  38. package/generated/prisma-mongodb/edge.js +33 -42
  39. package/generated/prisma-mongodb/index-browser.js +30 -39
  40. package/generated/prisma-mongodb/index.d.ts +3187 -2005
  41. package/generated/prisma-mongodb/index.js +33 -42
  42. package/generated/prisma-mongodb/package.json +1 -1
  43. package/generated/prisma-mongodb/schema.prisma +86 -59
  44. package/generated/prisma-mongodb/wasm.js +33 -42
  45. package/generated/prisma-postgresql/edge.js +33 -42
  46. package/generated/prisma-postgresql/index-browser.js +30 -39
  47. package/generated/prisma-postgresql/index.d.ts +3380 -2177
  48. package/generated/prisma-postgresql/index.js +33 -42
  49. package/generated/prisma-postgresql/package.json +1 -1
  50. package/generated/prisma-postgresql/schema.prisma +81 -57
  51. package/generated/prisma-postgresql/wasm.js +33 -42
  52. package/handlers/WEBHOOKS.md +0 -29
  53. package/handlers/app-definition-loader.js +128 -19
  54. package/handlers/app-handler-helpers.js +27 -8
  55. package/handlers/backend-utils.js +31 -25
  56. package/handlers/integration-event-dispatcher.js +3 -7
  57. package/handlers/middleware/admin-auth.js +14 -1
  58. package/handlers/routers/auth.js +46 -10
  59. package/handlers/routers/health.js +13 -4
  60. package/handlers/routers/integration-defined-routers.js +144 -92
  61. package/handlers/routers/integration-webhook-routers.js +100 -50
  62. package/handlers/workers/integration-defined-workers.js +5 -2
  63. package/index.js +66 -16
  64. package/infrastructure/scheduler/index.js +19 -6
  65. package/infrastructure/scheduler/netlify-scheduler-adapter.js +216 -0
  66. package/infrastructure/scheduler/scheduled-job-repository.js +118 -0
  67. package/infrastructure/scheduler/scheduler-service-factory.js +55 -7
  68. package/integrations/index.js +1 -14
  69. package/integrations/integration-base.js +2 -163
  70. package/integrations/integration-router.js +132 -102
  71. package/integrations/repositories/integration-mapping-repository-documentdb.js +0 -50
  72. package/integrations/repositories/integration-mapping-repository-interface.js +0 -15
  73. package/integrations/repositories/integration-mapping-repository-mongo.js +0 -29
  74. package/integrations/repositories/integration-mapping-repository-postgres.js +0 -30
  75. package/integrations/repositories/integration-mapping-repository.js +0 -29
  76. package/integrations/repositories/integration-not-found.js +14 -0
  77. package/integrations/repositories/integration-repository-documentdb.js +37 -9
  78. package/integrations/repositories/integration-repository-interface.js +17 -4
  79. package/integrations/repositories/integration-repository-mongo.js +64 -96
  80. package/integrations/repositories/integration-repository-postgres.js +63 -81
  81. package/integrations/repositories/process-repository-mongo.js +1 -1
  82. package/integrations/schedule-config.js +80 -0
  83. package/integrations/tests/doubles/test-integration-repository.js +12 -1
  84. package/integrations/use-cases/create-integration.js +56 -31
  85. package/integrations/use-cases/delete-integration-for-user.js +4 -4
  86. package/integrations/use-cases/dispatch-ongoing-syncs.js +100 -0
  87. package/integrations/use-cases/get-integration-for-user.js +2 -4
  88. package/integrations/use-cases/get-integration-instance.js +4 -4
  89. package/integrations/use-cases/get-integrations-for-user.js +79 -39
  90. package/integrations/use-cases/get-possible-integrations.js +2 -7
  91. package/integrations/use-cases/index.js +7 -1
  92. package/integrations/use-cases/integration-operations-for-user.js +256 -0
  93. package/integrations/use-cases/resolve-integration-ownership.js +177 -0
  94. package/integrations/use-cases/update-integration.js +4 -4
  95. package/integrations/utils/map-integration-dto.js +43 -14
  96. package/management-api/create-management-api-router.js +49 -0
  97. package/management-api/error-response.js +116 -0
  98. package/management-api/management-api-config.js +151 -0
  99. package/management-api/meta-router.js +76 -0
  100. package/management-api/openapi/build-openapi.js +144 -0
  101. package/management-api/openapi/components.js +415 -0
  102. package/management-api/openapi/v1-operations.js +83 -0
  103. package/management-api/openapi/v2-operations.js +264 -0
  104. package/management-api/route-registry.js +411 -0
  105. package/management-api/test-support/http-client.js +39 -0
  106. package/management-api/use-cases/get-management-api-meta.js +57 -0
  107. package/management-api/v1-deprecation.js +120 -0
  108. package/management-api/v2/create-v2-router.js +85 -0
  109. package/management-api/v2/dependencies.js +281 -0
  110. package/management-api/v2/dto.js +143 -0
  111. package/management-api/v2/handlers/authorization.js +94 -0
  112. package/management-api/v2/handlers/credentials.js +33 -0
  113. package/management-api/v2/handlers/entities.js +77 -0
  114. package/management-api/v2/handlers/integrations.js +128 -0
  115. package/management-api/v2/handlers/proxy.js +34 -0
  116. package/management-api/v2/request.js +85 -0
  117. package/management-api/v2/test-support/fake-dependencies.js +103 -0
  118. package/modules/domain/authorization-session.js +144 -0
  119. package/modules/entity-scope.js +15 -0
  120. package/modules/entity-type-registry.js +311 -0
  121. package/modules/index.js +14 -0
  122. package/modules/module-factory.js +12 -0
  123. package/modules/module.js +53 -2
  124. package/modules/proxy/proxy-bases.js +169 -0
  125. package/modules/proxy/proxy-errors.js +31 -0
  126. package/modules/proxy/proxy-path.js +183 -0
  127. package/modules/proxy/proxy-policy.js +277 -0
  128. package/modules/repositories/authorization-session-repository-documentdb.js +112 -0
  129. package/modules/repositories/authorization-session-repository-factory.js +31 -0
  130. package/modules/repositories/authorization-session-repository-interface.js +47 -0
  131. package/modules/repositories/authorization-session-repository-prisma.js +87 -0
  132. package/modules/repositories/entity-not-found.js +18 -0
  133. package/modules/repositories/module-repository-documentdb.js +51 -6
  134. package/modules/repositories/module-repository-interface.js +19 -2
  135. package/modules/repositories/module-repository-mongo.js +67 -74
  136. package/modules/repositories/module-repository-postgres.js +66 -74
  137. package/modules/repositories/module-repository.js +2 -1
  138. package/modules/requester/proxy-request.js +245 -0
  139. package/modules/requester/requester.js +35 -5
  140. package/modules/scope-config.js +192 -0
  141. package/modules/scope-policy.js +112 -0
  142. package/modules/scope-resolution.js +288 -0
  143. package/modules/scope-visibility.js +247 -0
  144. package/modules/tests/doubles/in-memory-authorization-session-repository.js +45 -0
  145. package/modules/tests/doubles/ownership-matching.js +18 -0
  146. package/modules/use-cases/authorize-entity-for-user.js +61 -0
  147. package/modules/use-cases/create-entity-from-credential.js +113 -0
  148. package/modules/use-cases/entity-operations-for-user.js +162 -0
  149. package/modules/use-cases/entity-types.js +139 -0
  150. package/modules/use-cases/execute-entity-proxy-request.js +331 -0
  151. package/modules/use-cases/get-authorization-requirements.js +100 -0
  152. package/modules/use-cases/get-entities-for-user.js +26 -13
  153. package/modules/use-cases/get-entity-module-for-user.js +113 -0
  154. package/modules/use-cases/get-entity-options-by-id.js +10 -4
  155. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  156. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  157. package/modules/use-cases/get-module.js +10 -4
  158. package/modules/use-cases/manage-access.js +21 -0
  159. package/modules/use-cases/multi-step-authorization.js +329 -0
  160. package/modules/use-cases/process-authorization-callback.js +53 -19
  161. package/modules/use-cases/refresh-entity-options.js +10 -4
  162. package/modules/use-cases/test-module-auth.js +10 -4
  163. package/package.json +6 -7
  164. package/prisma-mongodb/schema.prisma +86 -59
  165. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +28 -0
  166. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +26 -0
  167. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +25 -0
  168. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +28 -0
  169. package/prisma-postgresql/schema.prisma +81 -57
  170. package/providers/index.js +23 -0
  171. package/providers/resolve-provider.js +93 -0
  172. package/providers/runtime-detection.js +124 -0
  173. package/queues/index.js +25 -0
  174. package/queues/providers/index.js +11 -0
  175. package/queues/providers/netlify-background-provider.js +150 -0
  176. package/queues/providers/qstash-queue-provider.js +219 -0
  177. package/queues/providers/sqs-queue-provider.js +70 -0
  178. package/queues/queue-client-interface.js +55 -0
  179. package/queues/queue-message-auth.js +193 -0
  180. package/queues/queue-provider-factory.js +101 -0
  181. package/queues/queue-provider.js +65 -0
  182. package/queues/queuer-util.js +2 -9
  183. package/queues/sqs-queue-client.js +81 -0
  184. package/types/index.d.ts +2 -0
  185. package/types/modules/index.d.ts +214 -0
  186. package/types/user/index.d.ts +134 -0
  187. package/user/adopter-jwt-config.js +177 -0
  188. package/user/adopter-jwt-errors.js +59 -0
  189. package/user/adopter-token-verifier.js +122 -0
  190. package/user/app-user-helpers.js +97 -0
  191. package/user/create-user-authentication.js +64 -0
  192. package/user/membership.js +51 -0
  193. package/user/repositories/organization-membership-record.js +19 -0
  194. package/user/repositories/organization-membership-repository-documentdb.js +106 -0
  195. package/user/repositories/organization-membership-repository-factory.js +42 -0
  196. package/user/repositories/organization-membership-repository-interface.js +57 -0
  197. package/user/repositories/organization-membership-repository-mongo.js +13 -0
  198. package/user/repositories/organization-membership-repository-postgres.js +29 -0
  199. package/user/repositories/organization-membership-repository-prisma.js +81 -0
  200. package/user/repositories/user-repository-documentdb.js +27 -2
  201. package/user/repositories/user-repository-interface.js +18 -0
  202. package/user/repositories/user-repository-mongo.js +24 -0
  203. package/user/repositories/user-repository-postgres.js +18 -0
  204. package/user/tests/doubles/adopter-jwt-fixtures.js +220 -0
  205. package/user/use-cases/authenticate-user.js +7 -1
  206. package/user/use-cases/ensure-organization-membership.js +92 -0
  207. package/user/use-cases/find-individual-users-by-organization.js +54 -0
  208. package/user/use-cases/get-user-from-adopter-jwt.js +95 -124
  209. package/user/use-cases/get-user-from-x-frigg-headers.js +83 -80
  210. package/user/user.js +64 -1
  211. package/errors/mapping-already-exists-error.js +0 -14
  212. package/handlers/workers/integration-triggers.js +0 -25
  213. package/infrastructure/scheduler/recurrence.js +0 -204
  214. package/integrations/define/DESIGN.md +0 -122
  215. package/integrations/define/config.js +0 -98
  216. package/integrations/define/define-app.js +0 -66
  217. package/integrations/define/define-extension.js +0 -154
  218. package/integrations/define/define-integration.js +0 -335
  219. package/integrations/define/examples/deal-alerts.js +0 -140
  220. package/integrations/define/extension-binding.js +0 -174
  221. package/integrations/define/index.js +0 -11
  222. package/integrations/define/json-schema-lite.js +0 -68
  223. package/integrations/define/subscriptions.js +0 -186
  224. package/integrations/define/tests/fixtures/hubspot.js +0 -109
  225. package/integrations/define/tests/fixtures/slack.js +0 -26
  226. package/integrations/define/use.js +0 -39
  227. package/integrations/definitions/README.md +0 -278
  228. package/integrations/definitions/canonical.js +0 -11
  229. package/integrations/definitions/capabilities/AUDIT.md +0 -480
  230. package/integrations/definitions/capabilities/acmeerp.capabilities.json +0 -633
  231. package/integrations/definitions/capabilities/asana.capabilities.json +0 -279
  232. package/integrations/definitions/capabilities/calendly.capabilities.json +0 -385
  233. package/integrations/definitions/capabilities/docusign.capabilities.json +0 -314
  234. package/integrations/definitions/capabilities/fathom.capabilities.json +0 -238
  235. package/integrations/definitions/capabilities/github.capabilities.json +0 -585
  236. package/integrations/definitions/capabilities/hubspot.capabilities.json +0 -822
  237. package/integrations/definitions/capabilities/jira.capabilities.json +0 -391
  238. package/integrations/definitions/capabilities/klaviyo.capabilities.json +0 -503
  239. package/integrations/definitions/capabilities/linear.capabilities.json +0 -378
  240. package/integrations/definitions/capabilities/netsuite.capabilities.json +0 -535
  241. package/integrations/definitions/capabilities/notion.capabilities.json +0 -208
  242. package/integrations/definitions/capabilities/phone.capabilities.json +0 -511
  243. package/integrations/definitions/capabilities/pipedrive.capabilities.json +0 -430
  244. package/integrations/definitions/capabilities/quickbooks.capabilities.json +0 -635
  245. package/integrations/definitions/capabilities/salesforce.capabilities.json +0 -568
  246. package/integrations/definitions/capabilities/shopify.capabilities.json +0 -1128
  247. package/integrations/definitions/capabilities/slack.capabilities.json +0 -373
  248. package/integrations/definitions/capabilities/stripe.capabilities.json +0 -630
  249. package/integrations/definitions/capabilities/webflow.capabilities.json +0 -196
  250. package/integrations/definitions/capabilities/zendesk.capabilities.json +0 -422
  251. package/integrations/definitions/compile.js +0 -80
  252. package/integrations/definitions/config-form.js +0 -137
  253. package/integrations/definitions/examples/acme-erp-quickbooks.definition.json +0 -467
  254. package/integrations/definitions/examples/asana-slack.definition.json +0 -219
  255. package/integrations/definitions/examples/calendly-salesforce.definition.json +0 -459
  256. package/integrations/definitions/examples/docusign-salesforce.definition.json +0 -305
  257. package/integrations/definitions/examples/fathom-hubspot-notion.definition.json +0 -575
  258. package/integrations/definitions/examples/github-linear.definition.json +0 -322
  259. package/integrations/definitions/examples/hubspot-slack.definition.json +0 -427
  260. package/integrations/definitions/examples/klaviyo-hubspot.definition.json +0 -336
  261. package/integrations/definitions/examples/phone-hubspot.definition.json +0 -633
  262. package/integrations/definitions/examples/pipedrive-hubspot.definition.json +0 -136
  263. package/integrations/definitions/examples/pipedrive-phone.definition.json +0 -340
  264. package/integrations/definitions/examples/records/phone-hubspot.definition-version.json +0 -652
  265. package/integrations/definitions/examples/records/phone-hubspot.integration.json +0 -86
  266. package/integrations/definitions/examples/records/phone-hubspot.mappings.json +0 -39
  267. package/integrations/definitions/examples/records/phone-hubspot.workflow-run.json +0 -62
  268. package/integrations/definitions/examples/shopify-klaviyo.definition.json +0 -488
  269. package/integrations/definitions/examples/shopify-netsuite.definition.json +0 -622
  270. package/integrations/definitions/examples/stripe-quickbooks.definition.json +0 -476
  271. package/integrations/definitions/examples/webflow-hubspot-slack.definition.json +0 -262
  272. package/integrations/definitions/examples/zendesk-jira.definition.json +0 -604
  273. package/integrations/definitions/expressions.js +0 -229
  274. package/integrations/definitions/index.js +0 -48
  275. package/integrations/definitions/integration-runtime.js +0 -779
  276. package/integrations/definitions/interpreter.js +0 -104
  277. package/integrations/definitions/manifests.js +0 -177
  278. package/integrations/definitions/renderers/common.js +0 -67
  279. package/integrations/definitions/renderers/html.js +0 -129
  280. package/integrations/definitions/renderers/hubspot.js +0 -59
  281. package/integrations/definitions/renderers/preview.js +0 -154
  282. package/integrations/definitions/renderers/slack.js +0 -146
  283. package/integrations/definitions/repositories/definition-version-repository.js +0 -67
  284. package/integrations/definitions/repositories/workflow-run-repository.js +0 -80
  285. package/integrations/definitions/router.js +0 -170
  286. package/integrations/definitions/runtime.js +0 -268
  287. package/integrations/definitions/schema/capability-manifest.v0.1.schema.json +0 -549
  288. package/integrations/definitions/schema/installation.v0.1.schema.json +0 -130
  289. package/integrations/definitions/schema/integration-definition.v0.1.schema.json +0 -909
  290. package/integrations/definitions/schema/records.md +0 -105
  291. package/integrations/definitions/schema-check.js +0 -58
  292. package/integrations/definitions/scripts/check-manifests.js +0 -220
  293. package/integrations/definitions/scripts/validate.js +0 -66
  294. package/integrations/definitions/steps.js +0 -123
  295. package/integrations/definitions/sync-engine.js +0 -297
  296. package/integrations/definitions/tests/helpers-fake-crm.js +0 -49
  297. package/integrations/definitions/transforms.js +0 -91
  298. package/integrations/definitions/use-cases/definition-use-cases.js +0 -245
  299. package/integrations/definitions/validate.js +0 -436
  300. package/integrations/definitions/versioning.js +0 -176
  301. package/integrations/definitions/webhooks.js +0 -119
  302. package/integrations/integration-types.js +0 -73
  303. package/integrations/triggers.js +0 -244
  304. package/integrations/use-cases/run-trigger-tick.js +0 -155
  305. package/integrations/use-cases/run-trigger.js +0 -77
  306. package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +0 -66
  307. package/prisma-postgresql/migrations/20261019000000_workflow_run_waits/migration.sql +0 -7
  308. package/prisma-postgresql/migrations/20261019120000_definition_workspace/migration.sql +0 -5
@@ -1,245 +0,0 @@
1
- /**
2
- * Application layer for integrations published as definitions (ADR-063): publishing definition versions, the catalogue the
3
- * runtime reads from, and per-installation upgrades. Business rules live here; the repositories only store rows.
4
- *
5
- * createDefinitionCatalog() what defineSharedIntegrationClass({ definitions }) reads: get(id, version), latest(id)
6
- * PublishDefinitionVersion validate (schema + references) → checkBump against the latest published → store
7
- * DeprecateDefinitionVersion PUBLISHED → DEPRECATED (still runs for its pins, cannot be newly installed)
8
- * ListPublishedDefinitions the install catalogue: latest published version of each definition
9
- * GetDefinitionHistory every version of one definition, newest first
10
- * PlanInstallationUpgrade planUpgrade(installation, pinned, target), without changing anything
11
- */
12
- const { checksum } = require('../canonical');
13
- const { checkBump, planUpgrade } = require('../versioning');
14
-
15
- class DefinitionError extends Error {
16
- constructor(message, { status = 400, code, details } = {}) { super(message); this.name = 'DefinitionError'; this.status = status; this.code = code; this.details = details; }
17
- }
18
-
19
- const SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([\w.]+))?$/;
20
- function compareVersions(a, b) {
21
- const x = SEMVER.exec(a), y = SEMVER.exec(b);
22
- if (!x || !y) return String(a).localeCompare(String(b));
23
- for (let i = 1; i <= 3; i += 1) if (Number(x[i]) !== Number(y[i])) return Number(x[i]) - Number(y[i]);
24
- if (x[4] && !y[4]) return -1; // 1.0.0-beta < 1.0.0
25
- if (!x[4] && y[4]) return 1;
26
- return String(x[4] || '').localeCompare(String(y[4] || ''));
27
- }
28
- const newestFirst = (rows) => [...rows].sort((a, b) => compareVersions(b.version, a.version));
29
- const SERVABLE = new Set(['PUBLISHED', 'DEPRECATED', 'RETIRED']); // a DRAFT never reaches the runtime
30
-
31
- /**
32
- * The definition store the runtime reads from. Published rows are immutable, so they are cached by id@version for the
33
- * life of the process; a pinned checksum is verified, so an installation provably runs the document it was pinned to.
34
- */
35
- function createDefinitionCatalog({ repository } = {}) {
36
- const repo = () => repository || (repository = require('../repositories/definition-version-repository').createDefinitionVersionRepository());
37
- const cache = new Map();
38
- return {
39
- async get(definitionId, version, { checksum: expected } = {}) {
40
- const key = `${definitionId}@${version}`;
41
- let row = cache.get(key);
42
- if (!row) {
43
- row = await repo().findVersion(definitionId, version);
44
- if (!row || !SERVABLE.has(row.status)) return null;
45
- cache.set(key, row);
46
- }
47
- if (expected && expected !== row.checksum) throw new DefinitionError(`${key} does not match the pinned checksum`, { status: 409, code: 'CHECKSUM_MISMATCH' });
48
- return row.document;
49
- },
50
- /** The latest PUBLISHED version (not deprecated, not draft): what a new installation pins. */
51
- async latest(definitionId) {
52
- const rows = (await repo().listVersions(definitionId)).filter((r) => r.status === 'PUBLISHED');
53
- return rows.length ? newestFirst(rows)[0].document : null;
54
- },
55
- async checksumOf(definitionId, version) {
56
- return (await repo().findVersion(definitionId, version))?.checksum || null;
57
- },
58
- /** The workspace that built a definition, or null for the app's own catalogue. */
59
- async ownerOf(definitionId) {
60
- const rows = await repo().listVersions(definitionId);
61
- return rows[0]?.workspaceId ?? null;
62
- },
63
- /** The install catalogue: latest published version of each definition (with `workspaceId`, that workspace's too). */
64
- list: (context) => new ListPublishedDefinitions({ repository: repo() }).execute(context),
65
- /** The repository behind this catalogue, for publishing (bundled stores have none). */
66
- get repository() { return repo(); },
67
- };
68
- }
69
-
70
- /** The one answer for an integration id that is not this workspace's, whether it is someone else's or does not exist. */
71
- const notInWorkspace = (id) => new DefinitionError(`No integration "${id}" in this workspace`, { status: 404, code: 'NOT_FOUND' });
72
-
73
- class PublishDefinitionVersion {
74
- /**
75
- * @param {{ repository, manifests: object, check?: (def, manifests) => { valid, errors, warnings } }} deps
76
- * check defaults to schema-check's checkDefinition (JSON Schema + references)
77
- */
78
- constructor({ repository, manifests, check, isReserved }) {
79
- this.repository = repository;
80
- this.manifests = manifests;
81
- this.check = check || require('../schema-check').checkDefinition;
82
- /** Names a definition may not take: the app's registered (code) integrations. */
83
- this.isReserved = isReserved || (() => false);
84
- }
85
-
86
- /** @returns {Promise<{ version: object, created: boolean, changeLevel: string|null, warnings: array }>} */
87
- /** @param {{ workspaceId?: string }} p workspaceId: the workspace building its own integration (null: the app's catalogue) */
88
- async execute({ document, notes, createdBy, publish = true, strict = false, workspaceId = null } = {}) {
89
- if (!document || typeof document !== 'object') throw new DefinitionError('Send the definition as `document`');
90
- const result = this.check(document, this.manifests);
91
- if (!result.valid || (strict && result.warnings.length)) throw new DefinitionError(`${document.id}@${document.version} is not valid`, { code: 'INVALID', details: { errors: result.errors, warnings: result.warnings } });
92
-
93
- // An id belongs to one owner: the app's catalogue, or one workspace. Nobody publishes over someone else's, nor
94
- // takes the name of a code integration.
95
- if (this.isReserved(document.id)) throw new DefinitionError(`"${document.id}" is the name of an integration this app already has`, { status: 409, code: 'ID_TAKEN' });
96
- const versions = await this.repository.listVersions(document.id);
97
- const other = versions.find((v) => (v.workspaceId ?? null) !== (workspaceId ?? null));
98
- if (other) {
99
- // Another WORKSPACE's id must not be confirmed to this one: answer as a miss, exactly as reading it does
100
- // (GetOwnDefinition; ADR-052 section 3: a foreign resource is a 404, so ids cannot be probed). The catalogue's
101
- // ids are public (they are listed), and the admin is not a tenant, so those may say what is wrong.
102
- if (workspaceId != null && other.workspaceId != null) throw notInWorkspace(document.id);
103
- throw new DefinitionError(`"${document.id}" is already used by another integration`, { status: 409, code: 'ID_TAKEN' });
104
- }
105
-
106
- const sum = checksum(document);
107
- const existing = await this.repository.findVersion(document.id, document.version);
108
- if (existing && existing.status !== 'DRAFT') {
109
- if (existing.checksum === sum) return { version: existing, created: false, changeLevel: existing.changeLevel, warnings: result.warnings };
110
- throw new DefinitionError(`${document.id}@${document.version} is already ${existing.status.toLowerCase()} and cannot change. Publish a new version.`, { status: 409, code: 'IMMUTABLE' });
111
- }
112
-
113
- const published = newestFirst(versions.filter((r) => r.status === 'PUBLISHED' || r.status === 'DEPRECATED'));
114
- const previous = published.find((r) => r.version !== document.version) || null;
115
- let changeLevel = null;
116
- if (previous) {
117
- if (compareVersions(document.version, previous.version) <= 0) throw new DefinitionError(`${document.version} is not newer than the latest published ${previous.version}`, { status: 409, code: 'NOT_NEWER' });
118
- const bump = checkBump(previous.document, document);
119
- if (!bump.ok) throw new DefinitionError(bump.message, { status: 409, code: 'BUMP_TOO_SMALL', details: { required: bump.required, declared: bump.declared, changes: bump.changes } });
120
- changeLevel = bump.required;
121
- }
122
-
123
- const row = {
124
- definitionId: document.id, version: document.version, schemaVersion: document.schemaVersion, checksum: sum, document,
125
- changeLevel, previousVersion: previous?.version || null, notes: notes ?? null, createdBy: createdBy ?? null, workspaceId: workspaceId ?? null,
126
- status: publish ? 'PUBLISHED' : 'DRAFT', publishedAt: publish ? new Date() : null,
127
- };
128
- if (existing) {
129
- // A draft may be rewritten, and published, as long as it is still a draft when we write (checked by the
130
- // database, so two concurrent publishes cannot both win).
131
- const updated = await this.repository.updateIfStatus(document.id, document.version, 'DRAFT', row);
132
- if (!updated) throw new DefinitionError(`${document.id}@${document.version} changed while publishing; try again`, { status: 409, code: 'CONFLICT' });
133
- return { version: updated, created: false, changeLevel, warnings: result.warnings };
134
- }
135
- try {
136
- return { version: await this.repository.createVersion(row), created: true, changeLevel, warnings: result.warnings };
137
- } catch (e) {
138
- if (e?.code === 'P2002') {
139
- // Lost a race to the same version: the same document is the same publish; anything else is a conflict.
140
- const winner = await this.repository.findVersion(document.id, document.version);
141
- if (winner?.checksum === sum) return { version: winner, created: false, changeLevel: winner.changeLevel, warnings: result.warnings };
142
- throw new DefinitionError(`${document.id}@${document.version} was published with different content just now`, { status: 409, code: 'CONFLICT' });
143
- }
144
- throw e;
145
- }
146
- }
147
- }
148
-
149
- class DeprecateDefinitionVersion {
150
- constructor({ repository }) { this.repository = repository; }
151
- async execute({ definitionId, version }) {
152
- const row = await this.repository.updateIfStatus(definitionId, version, 'PUBLISHED', { status: 'DEPRECATED' });
153
- if (row) return row;
154
- const existing = await this.repository.findVersion(definitionId, version);
155
- if (!existing) throw new DefinitionError(`${definitionId}@${version} does not exist`, { status: 404, code: 'NOT_FOUND' });
156
- throw new DefinitionError(`${definitionId}@${version} is ${existing.status.toLowerCase()}; only a published version can be deprecated`, { status: 409, code: 'WRONG_STATUS' });
157
- }
158
- }
159
-
160
- class ListPublishedDefinitions {
161
- constructor({ repository }) { this.repository = repository; }
162
- /**
163
- * Latest published version of each definition, with what an install screen shows: the app's catalogue, plus the
164
- * integrations `workspaceId` built (another workspace's are never listed). Without a workspace, every one.
165
- */
166
- async execute({ workspaceId } = {}) {
167
- const byId = new Map();
168
- for (const r of await this.repository.listByStatus(['PUBLISHED'])) {
169
- if (workspaceId && r.workspaceId && r.workspaceId !== String(workspaceId)) continue;
170
- const cur = byId.get(r.definitionId);
171
- if (!cur || compareVersions(r.version, cur.version) > 0) byId.set(r.definitionId, r);
172
- }
173
- const out = [];
174
- for (const r of byId.values()) {
175
- const doc = (await this.repository.findVersion(r.definitionId, r.version)).document;
176
- out.push({
177
- id: r.definitionId, version: r.version, checksum: r.checksum, publishedAt: r.publishedAt, workspaceId: r.workspaceId ?? null,
178
- display: doc.display, modules: Object.fromEntries(Object.entries(doc.modules || {}).map(([k, m]) => [k, m.module])),
179
- counts: { syncs: (doc.syncs || []).length, links: (doc.links || []).length, workflows: (doc.workflows || []).length, ui: (doc.ui || []).length },
180
- });
181
- }
182
- return out.sort((a, b) => String(a.display?.label || a.id).localeCompare(String(b.display?.label || b.id)));
183
- }
184
- }
185
-
186
- class GetDefinitionHistory {
187
- constructor({ repository }) { this.repository = repository; }
188
- async execute({ definitionId }) {
189
- const rows = newestFirst(await this.repository.listVersions(definitionId));
190
- if (!rows.length) throw new DefinitionError(`No definition "${definitionId}"`, { status: 404, code: 'NOT_FOUND' });
191
- return rows.map(({ document, ...meta }) => meta);
192
- }
193
- }
194
-
195
- /**
196
- * The installation an approval decision is about, for someone who may not own it: an approval step names its
197
- * approvers, and they decide from their own sign-in. Whether this user may decide a particular run is the run's
198
- * business (decide()); here, only who owns it.
199
- */
200
- /**
201
- * A workspace's own integration, for its editor: every version (drafts included) and the newest document. Anyone
202
- * else's, or the app's catalogue, is "not found": the editor exists only for what this workspace built.
203
- */
204
- class GetOwnDefinition {
205
- constructor({ repository }) { this.repository = repository; }
206
- async execute({ definitionId, workspaceId }) {
207
- const rows = newestFirst(await this.repository.listVersions(definitionId));
208
- if (!rows.length || !workspaceId || rows[0].workspaceId !== String(workspaceId)) throw notInWorkspace(definitionId);
209
- return { id: definitionId, versions: rows.map(({ document, ...meta }) => meta), document: rows[0].document };
210
- }
211
- }
212
-
213
- class GetIntegrationForDecision {
214
- constructor({ getIntegrationInstance }) { this.getIntegrationInstance = getIntegrationInstance; }
215
- async execute(integrationId, userId) {
216
- const record = await this.getIntegrationInstance.integrationRepository.findIntegrationById(integrationId).catch(() => null);
217
- if (!record) throw new DefinitionError(`Integration ${integrationId} not found`, { status: 404, code: 'NOT_FOUND' });
218
- const isOwner = String(record.userId) === String(userId);
219
- const integration = await this.getIntegrationInstance.execute(integrationId, record.userId);
220
- return { integration, isOwner };
221
- }
222
- }
223
-
224
- class PlanInstallationUpgrade {
225
- constructor({ catalog }) { this.catalog = catalog; }
226
- /** @returns {Promise<{ from, to, allowed, needs, actions, config, changes }>} */
227
- async execute({ config, toVersion }) {
228
- const pin = config?.definition;
229
- if (!pin?.id || !pin?.version) throw new DefinitionError('This installation does not name a definition', { code: 'NO_PIN' });
230
- const from = await this.catalog.get(pin.id, pin.version);
231
- const target = toVersion || (await this.catalog.latest(pin.id))?.version;
232
- if (!target) throw new DefinitionError(`No published version of "${pin.id}"`, { status: 404, code: 'NOT_FOUND' });
233
- const to = await this.catalog.get(pin.id, target);
234
- if (!from || !to) throw new DefinitionError(`${pin.id}@${!from ? pin.version : target} is not published`, { status: 404, code: 'NOT_FOUND' });
235
- const plan = planUpgrade(config, from, to);
236
- plan.config.definition = { ...plan.config.definition, checksum: await this.catalog.checksumOf(pin.id, target) };
237
- return { from: pin.version, to: target, ...plan, changes: require('../versioning').diff(from, to).changes };
238
- }
239
- }
240
-
241
- module.exports = {
242
- DefinitionError, compareVersions, createDefinitionCatalog,
243
- PublishDefinitionVersion, DeprecateDefinitionVersion, ListPublishedDefinitions, GetDefinitionHistory, PlanInstallationUpgrade,
244
- GetIntegrationForDecision, GetOwnDefinition,
245
- };