@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,229 +0,0 @@
1
- /**
2
- * Expressions for integrations published as definitions (v0.1).
3
- *
4
- * Three forms, and no others (nothing is ever passed to eval):
5
- * 1. Reference "$.event.contact.id" A string that is exactly a path. Resolves to the value, keeping its type.
6
- * 2. Template "Call with {{event.from}}" A string with {{path | filter}} parts. Resolves to a string, except
7
- * that a string which is exactly one tag ("{{record.amount | to_cents}}") keeps the value's type, so
8
- * a filter can produce a number or a list for an API argument.
9
- * 3. Condition "$.steps.contact == null && $.event.duration > 30" Used by `when` and trigger filters.
10
- *
11
- * Paths are read from a context object: { event, steps, settings, install, vars, item, record, now }.
12
- * A missing segment resolves to undefined; it never throws.
13
- */
14
-
15
- const PATH_RE = /^\$\.[A-Za-z_][\w-]*(\.[\w-]+|\[\d+\])*$/;
16
-
17
- function getPath(ctx, path) {
18
- const clean = path.replace(/^\$\./, '');
19
- let cur = ctx;
20
- for (const seg of clean.split(/\.|(?=\[)/)) {
21
- if (cur == null) return undefined;
22
- const m = /^\[(\d+)\]$/.exec(seg);
23
- cur = m ? cur[Number(m[1])] : cur[seg];
24
- }
25
- return cur;
26
- }
27
-
28
- const num = (v) => (v == null || v === '' ? null : Number(v));
29
- const arith = (f) => (v, arg) => { const a = num(v), b = num(arg); return a == null || b == null || Number.isNaN(a) || Number.isNaN(b) ? null : f(a, b); };
30
-
31
- /**
32
- * Template filters. Each is a named, total function of (value, argument): no filter loops, calls out or throws on
33
- * bad input (a non-number gives null, which renders as ''). Arithmetic is here, as named filters, instead of an
34
- * expression language: {{item.amount | to_cents}}, {{record.created | from_unix}}.
35
- */
36
- // ISO 4217 minor-unit exponents that are not 2 (Stripe's zero-decimal and three-decimal lists).
37
- const MINOR_EXPONENT = { BIF: 0, CLP: 0, DJF: 0, GNF: 0, JPY: 0, KMF: 0, KRW: 0, MGA: 0, PYG: 0, RWF: 0, UGX: 0, VND: 0, VUV: 0, XAF: 0, XOF: 0, XPF: 0, BHD: 3, JOD: 3, KWD: 3, OMR: 3, TND: 3 };
38
- const exponentOf = (currency) => MINOR_EXPONENT[String(currency || '').toUpperCase()] ?? 2;
39
- /** a * 10^e, rounded half-up in decimal (19.995 -> 1999.5 -> 2000; 19.995 * 100 is 1999.4999... in binary). */
40
- const shiftRound = (a, e) => Math.round(Number(`${a}e${e}`));
41
-
42
- const FILTERS = {
43
- upper: (v) => String(v ?? '').toUpperCase(),
44
- lower: (v) => String(v ?? '').toLowerCase(),
45
- minutes: (v) => (v == null ? '' : String(Math.round(Number(v) / 60000))),
46
- seconds: (v) => (v == null ? '' : String(Math.round(Number(v) / 1000))),
47
- date: (v) => (v ? new Date(v).toISOString().slice(0, 10) : ''),
48
- json: (v) => JSON.stringify(v),
49
- // Record data inside a Slack message: escaped so a name like "*x* <!channel>" neither formats nor pings.
50
- mrkdwn: (v) => require('./renderers/slack').escapeMrkdwn(v),
51
- default: (v, arg) => (v == null || v === '' ? arg : v),
52
- truncate: (v, arg) => { const s = String(v ?? ''); const n = Number(arg) || 80; return s.length > n ? s.slice(0, n - 1) + '…' : s; },
53
- // lists
54
- join: (v, arg) => (Array.isArray(v) ? v.filter((x) => x != null && x !== '').join(arg ?? ', ') : v),
55
- size: (v) => (Array.isArray(v) || typeof v === 'string' ? v.length : v && typeof v === 'object' ? Object.keys(v).length : 0),
56
- first: (v) => (Array.isArray(v) ? v[0] : v),
57
- last: (v) => (Array.isArray(v) ? v[v.length - 1] : v),
58
- split: (v, arg) => (typeof v === 'string' ? v.split(arg ?? ',') : v),
59
- // arithmetic
60
- plus: arith((a, b) => a + b),
61
- minus: arith((a, b) => a - b),
62
- times: arith((a, b) => a * b),
63
- divided_by: arith((a, b) => (b === 0 ? null : a / b)),
64
- // Rounded in decimal-exponent form, so 1.005 | round: 2 is 1.01 (1.005 * 100 is 100.49999... in binary floating point).
65
- round: (v, arg) => { const a = num(v); if (a == null || Number.isNaN(a)) return null; const d = Math.max(0, Math.min(10, Number(arg) || 0)); return Number(`${shiftRound(a, d)}e-${d}`); },
66
- abs: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : Math.abs(a); },
67
- number: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : a; },
68
- // money in minor units, rounded so 19.99 -> 1999 (19.99 * 100 is 1998.9999999999998)
69
- to_cents: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : shiftRound(a, 2); },
70
- from_cents: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : Math.round(a) / 100; },
71
- // the same for any currency: {{record.amount | from_minor_units: record.currency}} (JPY has no minor unit, KWD three)
72
- to_minor_units: (v, cur) => { const a = num(v); if (a == null || Number.isNaN(a)) return null; return shiftRound(a, exponentOf(cur)); },
73
- from_minor_units: (v, cur) => { const a = num(v); if (a == null || Number.isNaN(a)) return null; const e = exponentOf(cur); return Math.round(a) / 10 ** e; },
74
- // unix seconds <-> ISO 8601
75
- from_unix: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : new Date(a * 1000).toISOString(); },
76
- to_unix: (v) => { const t = v ? Date.parse(v) : NaN; return Number.isNaN(t) ? null : Math.floor(t / 1000); },
77
- };
78
-
79
- /** A filter argument is a quoted literal ('x'), a number, or a path read from the context (steps.full.owner). */
80
- function filterArg(raw, ctx) {
81
- if (raw == null) return undefined;
82
- const a = raw.trim();
83
- if (/^'.*'$/.test(a)) return a.slice(1, -1).replace(/\\n/g, '\n').replace(/\\t/g, '\t');
84
- if (/^-?\d+(\.\d+)?$/.test(a)) return a;
85
- return getPath(ctx, '$.' + a.replace(/^\$\./, ''));
86
- }
87
-
88
- const TAG_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
89
- const SINGLE_TAG_RE = /^\{\{\s*([^}]+?)\s*\}\}$/;
90
-
91
- /** Split one tag's expression into its path and filters: "item.amount | times: 100" -> { path, filters: [{ name, arg }] }. */
92
- function parseTag(expr) {
93
- const [p, ...filters] = expr.split('|').map((x) => x.trim());
94
- return { path: p.replace(/^\$\./, ''), filters: filters.map((f) => { const m = /^(\w+)(?::\s*(.*))?$/.exec(f); return m ? { name: m[1], arg: m[2] } : { name: f, arg: undefined, bad: true }; }) };
95
- }
96
-
97
- function evalTag(expr, ctx) {
98
- const { path, filters } = parseTag(expr);
99
- let v = getPath(ctx, '$.' + path);
100
- for (const f of filters) {
101
- const fn = !f.bad && FILTERS[f.name];
102
- if (!fn) throw new Error(`Unknown template filter "${f.name}"`);
103
- v = fn(v, filterArg(f.arg, ctx));
104
- }
105
- return v;
106
- }
107
-
108
- function renderTemplate(str, ctx) {
109
- const single = SINGLE_TAG_RE.exec(str);
110
- if (single) return evalTag(single[1], ctx) ?? '';
111
- return str.replace(TAG_RE, (_, expr) => {
112
- const v = evalTag(expr, ctx);
113
- return v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);
114
- });
115
- }
116
-
117
- /** Every tag in a value (deep), for validation: [{ path, filters }]. */
118
- function templateTags(value, acc = []) {
119
- if (typeof value === 'string') for (const m of value.matchAll(TAG_RE)) acc.push(parseTag(m[1]));
120
- else if (Array.isArray(value)) value.forEach((v) => templateTags(v, acc));
121
- else if (value && typeof value === 'object') Object.values(value).forEach((v) => templateTags(v, acc));
122
- return acc;
123
- }
124
-
125
- /** Resolve every reference and template inside a value (deep). Non-string scalars pass through. */
126
- function resolve(value, ctx) {
127
- if (typeof value === 'string') {
128
- if (PATH_RE.test(value)) return getPath(ctx, value);
129
- if (value.includes('{{')) return renderTemplate(value, ctx);
130
- return value;
131
- }
132
- if (Array.isArray(value)) return value.map((v) => resolve(v, ctx));
133
- if (value && typeof value === 'object') {
134
- const out = {};
135
- for (const [k, v] of Object.entries(value)) out[k] = resolve(v, ctx);
136
- return out;
137
- }
138
- return value;
139
- }
140
-
141
- // ---------- conditions ----------
142
- const TOKEN_RE = /\s*(\$\.[A-Za-z_][\w.\-[\]]*|'(?:[^'\\]|\\.)*'|-?\d+(?:\.\d+)?|==|!=|>=|<=|&&|\|\||[()!<>]|\bin\b|\bcontains\b|\bexists\b|\btrue\b|\bfalse\b|\bnull\b)/y;
143
-
144
- function tokenize(src) {
145
- const out = [];
146
- TOKEN_RE.lastIndex = 0;
147
- let pos = 0;
148
- while (pos < src.length) {
149
- if (/^\s*$/.test(src.slice(pos))) break;
150
- TOKEN_RE.lastIndex = pos;
151
- const m = TOKEN_RE.exec(src);
152
- if (!m) throw new Error(`Cannot parse condition near "${src.slice(pos, pos + 20)}"`);
153
- out.push(m[1]);
154
- pos = TOKEN_RE.lastIndex;
155
- }
156
- return out;
157
- }
158
-
159
- /** Parse a condition into a small AST. Grammar: or := and ('||' and)* ; and := not ('&&' not)* ; not := '!' not | cmp ; cmp := atom (op atom | 'exists')? */
160
- function parseCondition(src) {
161
- const t = tokenize(src);
162
- let i = 0;
163
- const peek = () => t[i];
164
- const next = () => t[i++];
165
- function atom() {
166
- const tok = next();
167
- if (i > t.length) throw new Error(`Condition ends early: "${src}"`);
168
- if (tok === '(') { const e = or(); if (next() !== ')') throw new Error(`Missing ) in "${src}"`); return e; }
169
- if (tok.startsWith('$.')) return { ref: tok };
170
- if (tok.startsWith("'")) return { lit: tok.slice(1, -1).replace(/\\'/g, "'") };
171
- if (tok === 'true' || tok === 'false') return { lit: tok === 'true' };
172
- if (tok === 'null') return { lit: null };
173
- if (/^-?\d/.test(tok)) return { lit: Number(tok) };
174
- throw new Error(`Unexpected "${tok}" in "${src}"`);
175
- }
176
- function cmp() {
177
- const left = atom();
178
- const op = peek();
179
- if (op === 'exists') { next(); return { op: 'exists', left }; }
180
- if (['==', '!=', '>', '>=', '<', '<=', 'in', 'contains'].includes(op)) { next(); return { op, left, right: atom() }; }
181
- return { op: 'truthy', left };
182
- }
183
- function not() { if (peek() === '!') { next(); return { op: '!', left: not() }; } return cmp(); }
184
- function and() { let l = not(); while (peek() === '&&') { next(); l = { op: '&&', left: l, right: not() }; } return l; }
185
- function or() { let l = and(); while (peek() === '||') { next(); l = { op: '||', left: l, right: and() }; } return l; }
186
- const ast = or();
187
- if (i < t.length) throw new Error(`Unexpected "${t[i]}" in "${src}"`);
188
- return ast;
189
- }
190
-
191
- function evalAst(n, ctx) {
192
- if ('ref' in n) return getPath(ctx, n.ref);
193
- if ('lit' in n) return n.lit;
194
- const L = () => evalAst(n.left, ctx), R = () => evalAst(n.right, ctx);
195
- switch (n.op) {
196
- case 'truthy': return Boolean(L());
197
- case 'exists': return L() != null;
198
- case '!': return !evalAst(n.left, ctx);
199
- case '&&': return Boolean(L()) && Boolean(R());
200
- case '||': return Boolean(L()) || Boolean(R());
201
- case '==': return L() == R(); // eslint-disable-line eqeqeq -- null == undefined is intended
202
- case '!=': return L() != R(); // eslint-disable-line eqeqeq
203
- case '>': return L() > R();
204
- case '>=': return L() >= R();
205
- case '<': return L() < R();
206
- case '<=': return L() <= R();
207
- case 'in': { const r = R(); return Array.isArray(r) ? r.includes(L()) : typeof r === 'string' && r.includes(String(L())); }
208
- case 'contains': { const l = L(); return Array.isArray(l) ? l.includes(R()) : String(l ?? '').includes(String(R())); }
209
- default: throw new Error(`Unknown operator ${n.op}`);
210
- }
211
- }
212
-
213
- function evaluate(condition, ctx) {
214
- if (condition == null) return true;
215
- return Boolean(evalAst(parseCondition(condition), ctx));
216
- }
217
-
218
- /** Every $.root referenced by an expression-bearing value (for validation). */
219
- function referencedPaths(value, acc = []) {
220
- if (typeof value === 'string') {
221
- if (PATH_RE.test(value)) acc.push(value);
222
- for (const m of value.matchAll(/\{\{\s*([^}|]+?)\s*(\|[^}]*)?\}\}/g)) acc.push('$.' + m[1].replace(/^\$\./, ''));
223
- for (const m of value.matchAll(/\$\.[A-Za-z_][\w.\-[\]]*/g)) if (!acc.includes(m[0])) acc.push(m[0]);
224
- } else if (Array.isArray(value)) value.forEach((v) => referencedPaths(v, acc));
225
- else if (value && typeof value === 'object') Object.values(value).forEach((v) => referencedPaths(v, acc));
226
- return acc;
227
- }
228
-
229
- module.exports = { getPath, resolve, renderTemplate, evaluate, parseCondition, referencedPaths, templateTags, FILTERS };
@@ -1,48 +0,0 @@
1
- /**
2
- * Integrations published as definitions, v0.1 (ADR-063). An integration as a document: modules, syncs, workflows, queries,
3
- * UI surfaces and templates, run by one runtime class on the existing Frigg infrastructure.
4
- */
5
- const fs = require('fs');
6
- const path = require('path');
7
- const { validate } = require('./validate');
8
- const { compile, routeWebhook, eventName } = require('./compile');
9
- const { execute } = require('./interpreter');
10
- const { makePorts, runWorkflow, runQuery, renderSurface, resolveView, runLifecycle, resolveOptions } = require('./runtime');
11
- const { diff, checkBump, planUpgrade } = require('./versioning');
12
- const { fromDefinitionDocument, definitionDocumentHost, embedDefinitionDocument, defineIntegrationClass, defineSharedIntegrationClass, extendIntegrationClass, memoryDefinitions } = require('./integration-runtime');
13
- const { BUILTINS } = require('./steps');
14
- const { TRANSFORMS, applyMapping } = require('./transforms');
15
- const { FILTERS } = require('./expressions');
16
- const { validateManifest } = require('./manifests');
17
- const { checkDefinition, checkManifest, checkInstallation } = require('./schema-check');
18
- const { viewToSlack, escapeMrkdwn } = require('./renderers/slack');
19
- const { viewToHubSpot } = require('./renderers/hubspot');
20
- const { viewToHtml, slackMessageToHtml } = require('./renderers/html');
21
- const { previewSurface, previewWorkflow, recordingPorts } = require('./renderers/preview');
22
- const { decodeAction } = require('./renderers/common');
23
- const useCases = require('./use-cases/definition-use-cases');
24
-
25
- /** Load every capabilities/*.capabilities.json (bundled mocks; real modules ship their own). */
26
- function loadManifests(dir = path.join(__dirname, 'capabilities')) {
27
- const out = {};
28
- for (const f of fs.readdirSync(dir)) if (f.endsWith('.capabilities.json')) { const m = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')); out[m.module] = m; }
29
- return out;
30
- }
31
- function loadExamples(dir = path.join(__dirname, 'examples')) {
32
- return fs.readdirSync(dir).filter((f) => f.endsWith('.definition.json')).map((f) => JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')));
33
- }
34
-
35
- module.exports = {
36
- SCHEMA_VERSION: '0.1',
37
- validate, validateManifest, checkDefinition, checkManifest, checkInstallation, compile, routeWebhook, eventName, execute,
38
- makePorts, runWorkflow, runQuery, renderSurface, resolveView, runLifecycle,
39
- diff, checkBump, planUpgrade,
40
- // the document front door (ADR-063); `defineIntegration` (../define) is the code front door. Same compile target.
41
- fromDefinitionDocument, definitionDocumentHost, embedDefinitionDocument, memoryDefinitions,
42
- // deprecated aliases, removed before Stack B merges
43
- defineIntegrationClass, defineSharedIntegrationClass, extendIntegrationClass,
44
- BUILTINS, TRANSFORMS, FILTERS, applyMapping, resolveOptions,
45
- loadManifests, loadExamples,
46
- viewToSlack, escapeMrkdwn, viewToHubSpot, viewToHtml, slackMessageToHtml, previewSurface, previewWorkflow, recordingPorts, decodeAction,
47
- ...useCases,
48
- };