@elevasis/core 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/README.md +41 -41
- package/src/__tests__/publish.test.ts +18 -18
- package/src/__tests__/{template-foundations-compatibility.test.ts → template-core-compatibility.test.ts} +99 -99
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1135 -1131
- package/src/_gen/__tests__/scaffold-contracts.test.ts +53 -53
- package/src/_gen/scaffold-contracts.ts +45 -45
- package/src/auth/multi-tenancy/credentials/README.md +38 -38
- package/src/auth/multi-tenancy/credentials/index.ts +6 -6
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -39
- package/src/auth/multi-tenancy/credentials/server/service.ts +60 -60
- package/src/auth/multi-tenancy/index.ts +17 -17
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -107
- package/src/auth/multi-tenancy/invitations/index.ts +37 -37
- package/src/auth/multi-tenancy/invitations/invitation.ts +86 -86
- package/src/auth/multi-tenancy/invitations/server/index.ts +25 -25
- package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -24
- package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -24
- package/src/auth/multi-tenancy/invitations/supabase.ts +50 -50
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -126
- package/src/auth/multi-tenancy/memberships/index.ts +21 -21
- package/src/auth/multi-tenancy/memberships/membership.ts +138 -138
- package/src/auth/multi-tenancy/memberships/server/index.ts +15 -15
- package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -32
- package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -21
- package/src/auth/multi-tenancy/memberships/supabase.ts +46 -46
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -128
- package/src/auth/multi-tenancy/organizations/index.ts +23 -23
- package/src/auth/multi-tenancy/organizations/organization.ts +24 -24
- package/src/auth/multi-tenancy/organizations/server/index.ts +10 -10
- package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -35
- package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -20
- package/src/auth/multi-tenancy/types.ts +83 -83
- package/src/auth/multi-tenancy/users/api-schemas.ts +194 -194
- package/src/auth/multi-tenancy/users/index.ts +27 -27
- package/src/auth/multi-tenancy/users/server/index.ts +19 -19
- package/src/auth/multi-tenancy/users/server/transforms.ts +21 -21
- package/src/auth/multi-tenancy/users/server/workos.ts +16 -16
- package/src/auth/multi-tenancy/users/user.ts +65 -65
- package/src/business/README.md +52 -52
- package/src/business/__tests__/entities-published.test.ts +33 -33
- package/src/business/acquisition/api-schemas.ts +759 -759
- package/src/business/acquisition/index.ts +109 -109
- package/src/business/acquisition/types.ts +402 -402
- package/src/business/base-entities.test.ts +481 -481
- package/src/business/base-entities.ts +241 -241
- package/src/business/entities-published.ts +24 -24
- package/src/business/index.ts +15 -15
- package/src/business/pdf/browser/pdfmake-browser.ts +229 -229
- package/src/business/pdf/index.ts +10 -10
- package/src/business/pdf/server/index.ts +21 -21
- package/src/business/pdf/server/themes/default.ts +8 -8
- package/src/business/pdf/server/themes/index.ts +9 -9
- package/src/business/pdf/server/themes/types.ts +8 -8
- package/src/business/pdf/types.ts +272 -272
- package/src/business/projects/index.ts +2 -2
- package/src/business/projects/sse-events.ts +21 -21
- package/src/business/projects/types.ts +89 -89
- package/src/business/sales/api-schemas.ts +75 -75
- package/src/business/seo/__tests__/linking.test.ts +549 -549
- package/src/business/seo/__tests__/types.test.ts +404 -404
- package/src/business/seo/index.ts +2 -2
- package/src/business/seo/linking.ts +281 -281
- package/src/business/seo/types.ts +199 -199
- package/src/commands/queue/index.ts +3 -3
- package/src/commands/queue/schemas.test.ts +593 -593
- package/src/commands/queue/schemas.ts +125 -125
- package/src/commands/queue/sse-events.ts +61 -61
- package/src/commands/queue/types/action.ts +52 -52
- package/src/commands/queue/types/checkpoint.ts +44 -44
- package/src/commands/queue/types/index.ts +7 -7
- package/src/commands/queue/types/task.ts +116 -116
- package/src/commands/queue/types.ts +14 -14
- package/src/content/distribution-metadata.ts +61 -61
- package/src/content/index.ts +10 -10
- package/src/deployments/index.ts +22 -22
- package/src/execution/core/__tests__/archived-logs.test.ts +72 -72
- package/src/execution/core/index.ts +11 -11
- package/src/execution/core/runner-types.ts +80 -80
- package/src/execution/core/server/environment.ts +31 -31
- package/src/execution/core/sse-executions.ts +119 -119
- package/src/execution/core/types.ts +29 -29
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +4 -4
- package/src/execution/engine/__tests__/timeout.test.ts +565 -565
- package/src/execution/engine/agent/__tests__/errors.test.ts +508 -508
- package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -531
- package/src/execution/engine/agent/actions/executor.ts +205 -205
- package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -230
- package/src/execution/engine/agent/actions/processor.ts +116 -116
- package/src/execution/engine/agent/actions/types.ts +70 -70
- package/src/execution/engine/agent/core/agent.ts +810 -810
- package/src/execution/engine/agent/core/types.ts +155 -155
- package/src/execution/engine/agent/errors.ts +251 -251
- package/src/execution/engine/agent/index.ts +78 -78
- package/src/execution/engine/agent/knowledge-map/types.ts +106 -106
- package/src/execution/engine/agent/knowledge-map/utils.ts +101 -101
- package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -754
- package/src/execution/engine/agent/memory/domains.ts +99 -99
- package/src/execution/engine/agent/memory/manager.ts +365 -365
- package/src/execution/engine/agent/memory/processor.ts +66 -66
- package/src/execution/engine/agent/memory/types.ts +90 -90
- package/src/execution/engine/agent/memory/utils.ts +134 -134
- package/src/execution/engine/agent/observability/logging.ts +467 -467
- package/src/execution/engine/agent/observability/types.ts +64 -64
- package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -349
- package/src/execution/engine/agent/reasoning/processor.ts +92 -92
- package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -134
- package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -49
- package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -93
- package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -65
- package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -44
- package/src/execution/engine/agent/reasoning/request-builder.ts +169 -169
- package/src/execution/engine/agent/reasoning/types.ts +18 -18
- package/src/execution/engine/base/errors.ts +118 -118
- package/src/execution/engine/base/index.ts +2 -2
- package/src/execution/engine/base/logging.ts +31 -31
- package/src/execution/engine/base/serialization.ts +324 -324
- package/src/execution/engine/base/types.ts +126 -126
- package/src/execution/engine/base/utils.ts +41 -41
- package/src/execution/engine/index.ts +434 -434
- package/src/execution/engine/interface/index.ts +1 -1
- package/src/execution/engine/interface/types.ts +62 -62
- package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -50
- package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -321
- package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -115
- package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -375
- package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -463
- package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -177
- package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -722
- package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -376
- package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -551
- package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -563
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -105
- package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -537
- package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -147
- package/src/execution/engine/llm/adapters/index.ts +17 -17
- package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -116
- package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -130
- package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -137
- package/src/execution/engine/llm/adapters/server/google.ts +283 -283
- package/src/execution/engine/llm/adapters/server/index.ts +12 -12
- package/src/execution/engine/llm/adapters/server/openai.ts +206 -206
- package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -235
- package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -230
- package/src/execution/engine/llm/errors.ts +186 -186
- package/src/execution/engine/llm/model-info.ts +332 -332
- package/src/execution/engine/llm/response-schema-validator.ts +113 -113
- package/src/execution/engine/llm/types.ts +86 -86
- package/src/execution/engine/test-utils/index.ts +6 -6
- package/src/execution/engine/test-utils/mocks.ts +56 -56
- package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -50
- package/src/execution/engine/tools/integration/index.ts +53 -53
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -73
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -209
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -82
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -122
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -89
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -84
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -16
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -293
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -100
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -217
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -92
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -218
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -87
- package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -11
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +361 -361
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -162
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -594
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -214
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -152
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -141
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -105
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -118
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -165
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -96
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -156
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -220
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -140
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +146 -146
- package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -31
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -210
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -1189
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -641
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -18
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -289
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -154
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -325
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -153
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -84
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -125
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -107
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -85
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -91
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -92
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -195
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -113
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -155
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -196
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -189
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -87
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -112
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -76
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -98
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -126
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -193
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -99
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -621
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -125
- package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -29
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -178
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -1473
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -59
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -17
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -80
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -134
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -75
- package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -27
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -108
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -132
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -274
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -230
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -133
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -90
- package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -210
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -517
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -309
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -133
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -122
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -111
- package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -11
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -78
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -222
- package/src/execution/engine/tools/integration/server/index.ts +61 -61
- package/src/execution/engine/tools/integration/service.ts +161 -161
- package/src/execution/engine/tools/integration/tool.ts +253 -253
- package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -74
- package/src/execution/engine/tools/integration/types/apify.ts +92 -92
- package/src/execution/engine/tools/integration/types/index.ts +19 -19
- package/src/execution/engine/tools/integration/types/instantly.ts +557 -557
- package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -56
- package/src/execution/engine/tools/integration/types/stripe.ts +162 -162
- package/src/execution/engine/tools/integration/types/tomba.ts +94 -94
- package/src/execution/engine/tools/lead-service-types.ts +884 -884
- package/src/execution/engine/tools/llm/index.ts +11 -11
- package/src/execution/engine/tools/llm/server/index.ts +8 -8
- package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -118
- package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -441
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -248
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -319
- package/src/execution/engine/tools/platform/acquisition/index.ts +43 -43
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -148
- package/src/execution/engine/tools/platform/acquisition/types.ts +260 -260
- package/src/execution/engine/tools/platform/email/index.ts +122 -122
- package/src/execution/engine/tools/platform/email/types.ts +96 -96
- package/src/execution/engine/tools/platform/index.ts +157 -157
- package/src/execution/engine/tools/platform/notification.ts +81 -81
- package/src/execution/engine/tools/platform/pdf/index.ts +110 -110
- package/src/execution/engine/tools/platform/pdf/types.ts +77 -77
- package/src/execution/engine/tools/platform/scheduler.ts +87 -87
- package/src/execution/engine/tools/platform/storage/index.ts +370 -370
- package/src/execution/engine/tools/platform/types.ts +148 -148
- package/src/execution/engine/tools/registry.ts +700 -700
- package/src/execution/engine/tools/tool-maps.ts +786 -786
- package/src/execution/engine/tools/types.ts +233 -233
- package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -139
- package/src/execution/engine/workflow/errors.ts +63 -63
- package/src/execution/engine/workflow/helpers/index.ts +11 -11
- package/src/execution/engine/workflow/helpers/server/index.ts +8 -8
- package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -93
- package/src/execution/engine/workflow/index.ts +19 -19
- package/src/execution/engine/workflow/log-truncate.ts +26 -26
- package/src/execution/engine/workflow/logging.ts +191 -191
- package/src/execution/engine/workflow/types.ts +182 -182
- package/src/execution/engine/workflow/utils.ts +280 -280
- package/src/execution/engine/workflow/workflow.ts +168 -168
- package/src/execution/index.ts +3 -3
- package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -733
- package/src/execution/scheduler/__tests__/utils.test.ts +1009 -1009
- package/src/execution/scheduler/api-schemas.ts +296 -296
- package/src/execution/scheduler/index.ts +50 -50
- package/src/execution/scheduler/schemas.ts +264 -264
- package/src/execution/scheduler/types.ts +111 -111
- package/src/execution/scheduler/utils.ts +364 -364
- package/src/forms/index.ts +7 -7
- package/src/forms/schemas.ts +69 -69
- package/src/forms/types.ts +70 -70
- package/src/index.ts +71 -60
- package/src/integrations/credentials/__tests__/schemas.test.ts +82 -82
- package/src/integrations/credentials/__tests__/utils.test.ts +144 -144
- package/src/integrations/credentials/api-schemas.ts +143 -143
- package/src/integrations/credentials/index.ts +32 -32
- package/src/integrations/credentials/schemas.ts +164 -164
- package/src/integrations/credentials/utils.ts +59 -59
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -59
- package/src/integrations/oauth/api-schemas.ts +92 -92
- package/src/integrations/oauth/index.ts +19 -19
- package/src/integrations/oauth/provider-registry.ts +61 -61
- package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -183
- package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -577
- package/src/integrations/oauth/server/credentials.ts +39 -39
- package/src/integrations/oauth/server/refresh.ts +214 -214
- package/src/integrations/oauth/types.ts +34 -34
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -318
- package/src/integrations/webhook-endpoints/api-schemas.ts +102 -102
- package/src/integrations/webhook-endpoints/index.ts +28 -28
- package/src/integrations/webhook-endpoints/types.ts +51 -51
- package/src/operations/activities/api-schemas.ts +79 -79
- package/src/operations/activities/index.ts +9 -9
- package/src/operations/activities/sse-events.ts +30 -30
- package/src/operations/activities/types.ts +63 -63
- package/src/operations/debug-logs/client.ts +60 -60
- package/src/operations/debug-logs/debug-logger.ts +83 -83
- package/src/operations/debug-logs/index.ts +8 -8
- package/src/operations/debug-logs/server.ts +19 -19
- package/src/operations/debug-logs/types.ts +33 -33
- package/src/operations/index.ts +50 -50
- package/src/operations/notifications/api-schemas.ts +91 -91
- package/src/operations/notifications/index.ts +3 -3
- package/src/operations/notifications/sse-events.ts +21 -21
- package/src/operations/notifications/types.ts +47 -47
- package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -297
- package/src/operations/observability/__tests__/utils.test.ts +54 -54
- package/src/operations/observability/ai-usage-collector.ts +64 -64
- package/src/operations/observability/index.ts +13 -13
- package/src/operations/observability/metrics-collector.ts +49 -49
- package/src/operations/observability/schemas.ts +39 -39
- package/src/operations/observability/types.ts +463 -463
- package/src/operations/observability/utils.ts +77 -77
- package/src/operations/sessions/__tests__/manager.test.ts +821 -821
- package/src/operations/sessions/index.ts +26 -26
- package/src/operations/sessions/server/manager.ts +90 -90
- package/src/operations/sessions/server/session.ts +180 -180
- package/src/operations/sessions/types.ts +98 -98
- package/src/operations/triggers/index.ts +12 -12
- package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -44
- package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -51
- package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -45
- package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -49
- package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -45
- package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -54
- package/src/operations/triggers/webhook/index.ts +35 -35
- package/src/operations/triggers/webhook/types.ts +74 -74
- package/src/organization-model/README.md +97 -97
- package/src/organization-model/__tests__/defaults.test.ts +175 -175
- package/src/organization-model/__tests__/domains/customers.test.ts +295 -295
- package/src/organization-model/__tests__/domains/goals.test.ts +479 -479
- package/src/organization-model/__tests__/domains/identity.test.ts +279 -279
- package/src/organization-model/__tests__/domains/navigation.test.ts +212 -212
- package/src/organization-model/__tests__/domains/offerings.test.ts +419 -419
- package/src/organization-model/__tests__/domains/operations.test.ts +203 -203
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +362 -362
- package/src/organization-model/__tests__/domains/roles.test.ts +347 -347
- package/src/organization-model/__tests__/domains/statuses.test.ts +243 -243
- package/src/organization-model/__tests__/foundation.test.ts +105 -105
- package/src/organization-model/__tests__/graph.test.ts +894 -894
- package/src/organization-model/__tests__/resolve.test.ts +690 -690
- package/src/organization-model/__tests__/schema.test.ts +407 -407
- package/src/organization-model/contracts.ts +14 -14
- package/src/organization-model/defaults.ts +148 -148
- package/src/organization-model/domains/branding.ts +22 -22
- package/src/organization-model/domains/customers.ts +75 -75
- package/src/organization-model/domains/features.ts +22 -22
- package/src/organization-model/domains/goals.ts +80 -80
- package/src/organization-model/domains/identity.ts +94 -94
- package/src/organization-model/domains/navigation.ts +391 -391
- package/src/organization-model/domains/offerings.ts +66 -66
- package/src/organization-model/domains/operations.ts +85 -85
- package/src/organization-model/domains/projects.ts +48 -48
- package/src/organization-model/domains/prospecting.ts +33 -33
- package/src/organization-model/domains/roles.ts +55 -55
- package/src/organization-model/domains/sales.ts +94 -94
- package/src/organization-model/domains/shared.ts +62 -62
- package/src/organization-model/domains/statuses.ts +130 -130
- package/src/organization-model/foundation.ts +97 -97
- package/src/organization-model/graph/build.ts +399 -399
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/schema.ts +48 -48
- package/src/organization-model/graph/types.ts +40 -40
- package/src/organization-model/index.ts +13 -13
- package/src/organization-model/organization-graph.mdx +272 -272
- package/src/organization-model/organization-model.mdx +320 -320
- package/src/organization-model/published.ts +85 -85
- package/src/organization-model/resolve.ts +66 -66
- package/src/organization-model/schema.ts +287 -287
- package/src/organization-model/types.ts +46 -46
- package/src/platform/api/index.ts +1 -1
- package/src/platform/api/types.ts +35 -35
- package/src/platform/constants/http.ts +37 -37
- package/src/platform/constants/index.ts +5 -5
- package/src/platform/constants/limits.ts +32 -32
- package/src/platform/constants/resilience.ts +51 -51
- package/src/platform/constants/timeouts.ts +20 -20
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -347
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1028 -1028
- package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -393
- package/src/platform/registry/__tests__/resource-registry.test.ts +2005 -2005
- package/src/platform/registry/__tests__/serialization.test.ts +1127 -1127
- package/src/platform/registry/command-view.ts +180 -180
- package/src/platform/registry/domains.ts +165 -165
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/reserved.ts +24 -24
- package/src/platform/registry/resource-metadata.ts +59 -59
- package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -129
- package/src/platform/registry/resource-registry.ts +876 -876
- package/src/platform/registry/serialization.ts +273 -273
- package/src/platform/registry/serialized-types.ts +231 -231
- package/src/platform/registry/stats-types.ts +66 -66
- package/src/platform/registry/types.ts +404 -404
- package/src/platform/registry/validation.ts +513 -513
- package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -471
- package/src/platform/resilience/circuit-breaker.ts +164 -164
- package/src/platform/resilience/errors.ts +68 -68
- package/src/platform/resilience/http-error-mapper.ts +129 -129
- package/src/platform/resilience/index.ts +93 -93
- package/src/platform/resilience/rate-limiter-types.ts +46 -46
- package/src/platform/resilience/rate-limiter.ts +140 -140
- package/src/platform/resilience/retry.ts +89 -89
- package/src/platform/resilience/timeout.ts +63 -63
- package/src/platform/sse/events.ts +37 -37
- package/src/platform/sse/index.ts +7 -7
- package/src/platform/utils/__tests__/validation.test.ts +1083 -1083
- package/src/platform/utils/currency.ts +96 -96
- package/src/platform/utils/debounce.ts +52 -52
- package/src/platform/utils/error.ts +41 -41
- package/src/platform/utils/hmac.test.ts +97 -97
- package/src/platform/utils/index.ts +32 -32
- package/src/platform/utils/server/betterstack-logger.ts +210 -210
- package/src/platform/utils/server/hmac.ts +44 -44
- package/src/platform/utils/server/unsubscribe.ts +111 -111
- package/src/platform/utils/token-counter.ts +96 -96
- package/src/platform/utils/validation.ts +425 -425
- package/src/projects/api-schemas.ts +268 -268
- package/src/published.ts +1 -1
- package/src/reference/_generated/contracts.md +607 -607
- package/src/reference/glossary.md +105 -105
- package/src/requests/__tests__/api-schemas.test.ts +277 -277
- package/src/requests/api-schemas.ts +83 -83
- package/src/requests/index.ts +1 -1
- package/src/scaffold-registry/__tests__/index.test.ts +17 -0
- package/src/scaffold-registry/__tests__/schema.test.ts +329 -230
- package/src/scaffold-registry/index.ts +205 -189
- package/src/scaffold-registry/schema.ts +196 -128
- package/src/server.ts +272 -272
- package/src/supabase/database.types.ts +2719 -2719
- package/src/supabase/helpers.ts +20 -20
- package/src/supabase/index.ts +52 -52
- package/src/supabase/server/client.ts +58 -58
- package/src/test-utils/README.md +38 -38
- package/src/test-utils/browser-mocks.ts +54 -54
- package/src/test-utils/fixtures/api-keys.ts +52 -52
- package/src/test-utils/fixtures/index.ts +4 -4
- package/src/test-utils/fixtures/memberships.ts +80 -80
- package/src/test-utils/fixtures/organizations.ts +69 -69
- package/src/test-utils/fixtures/users.ts +79 -79
- package/src/test-utils/index.ts +11 -11
- package/src/test-utils/mocks/index.ts +2 -2
- package/src/test-utils/mocks/supabase.ts +142 -142
- package/src/test-utils/mocks/workos.ts +108 -108
- package/src/test-utils/rls/RLSTestContext.ts +556 -556
- package/src/test-utils/rls/index.ts +1 -1
|
@@ -1,1127 +1,1127 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test: Registry Serialization - buildEdges
|
|
3
|
-
* Tests for buildEdges function that creates Command View edges from resource relationships
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect } from 'vitest'
|
|
7
|
-
import { buildEdges } from '../serialization'
|
|
8
|
-
import type { DeploymentSpec } from '../resource-registry'
|
|
9
|
-
import type {
|
|
10
|
-
TriggerDefinition,
|
|
11
|
-
ExternalResourceDefinition,
|
|
12
|
-
HumanCheckpointDefinition,
|
|
13
|
-
ResourceRelationships
|
|
14
|
-
} from '../types'
|
|
15
|
-
import type { CommandViewEdge } from '../command-view'
|
|
16
|
-
|
|
17
|
-
describe('buildEdges', () => {
|
|
18
|
-
describe('Trigger Edges', () => {
|
|
19
|
-
it('creates "triggers" edge for trigger -> workflow', () => {
|
|
20
|
-
const trigger: TriggerDefinition = {
|
|
21
|
-
resourceId: 'trigger-webhook',
|
|
22
|
-
type: 'trigger',
|
|
23
|
-
triggerType: 'webhook',
|
|
24
|
-
name: 'Order Webhook',
|
|
25
|
-
description: 'Webhook for new orders',
|
|
26
|
-
version: '1.0.0',
|
|
27
|
-
status: 'prod'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const relationships: ResourceRelationships = {
|
|
31
|
-
'trigger-webhook': {
|
|
32
|
-
triggers: { workflows: ['order-workflow'] }
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const resources: DeploymentSpec = {
|
|
37
|
-
triggers: [trigger],
|
|
38
|
-
relationships
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const edges = buildEdges(resources)
|
|
42
|
-
|
|
43
|
-
expect(edges).toHaveLength(1)
|
|
44
|
-
expect(edges[0]).toEqual({
|
|
45
|
-
id: 'edge-0',
|
|
46
|
-
source: 'trigger-webhook',
|
|
47
|
-
target: 'order-workflow',
|
|
48
|
-
relationship: 'triggers'
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('creates "triggers" edge for trigger -> agent', () => {
|
|
53
|
-
const trigger: TriggerDefinition = {
|
|
54
|
-
resourceId: 'trigger-schedule',
|
|
55
|
-
type: 'trigger',
|
|
56
|
-
triggerType: 'schedule',
|
|
57
|
-
name: 'Daily Report',
|
|
58
|
-
description: 'Daily report trigger',
|
|
59
|
-
version: '1.0.0',
|
|
60
|
-
status: 'prod',
|
|
61
|
-
schedule: '0 6 * * *'
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const relationships: ResourceRelationships = {
|
|
65
|
-
'trigger-schedule': {
|
|
66
|
-
triggers: { agents: ['report-agent'] }
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const resources: DeploymentSpec = {
|
|
71
|
-
triggers: [trigger],
|
|
72
|
-
relationships
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const edges = buildEdges(resources)
|
|
76
|
-
|
|
77
|
-
expect(edges).toHaveLength(1)
|
|
78
|
-
expect(edges[0]).toEqual({
|
|
79
|
-
id: 'edge-0',
|
|
80
|
-
source: 'trigger-schedule',
|
|
81
|
-
target: 'report-agent',
|
|
82
|
-
relationship: 'triggers'
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('creates "triggers" edge for trigger -> externalResource (via relationships)', () => {
|
|
87
|
-
// Note: trigger -> externalResource edges are created via ResourceRelationships
|
|
88
|
-
// The triggers.externalResources field doesn't exist - use relationships instead
|
|
89
|
-
const trigger: TriggerDefinition = {
|
|
90
|
-
resourceId: 'trigger-event',
|
|
91
|
-
type: 'trigger',
|
|
92
|
-
triggerType: 'event',
|
|
93
|
-
name: 'Low Stock Event',
|
|
94
|
-
description: 'Triggers on low stock',
|
|
95
|
-
version: '1.0.0',
|
|
96
|
-
status: 'prod',
|
|
97
|
-
eventType: 'low-stock-alert'
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Note: ResourceRelationships.triggers only supports agents/workflows
|
|
101
|
-
// For external resources, edges come from ExternalResourceDefinition.triggers (external -> internal)
|
|
102
|
-
// There's no mechanism for trigger -> external edges in the current model
|
|
103
|
-
const resources: DeploymentSpec = {
|
|
104
|
-
triggers: [trigger]
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const edges = buildEdges(resources)
|
|
108
|
-
|
|
109
|
-
// No edges created - trigger -> external relationships are not supported
|
|
110
|
-
// External resources declare what THEY trigger (external -> internal), not the reverse
|
|
111
|
-
expect(edges).toHaveLength(0)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('handles triggers with multiple targets', () => {
|
|
115
|
-
const trigger: TriggerDefinition = {
|
|
116
|
-
resourceId: 'trigger-multi',
|
|
117
|
-
type: 'trigger',
|
|
118
|
-
triggerType: 'webhook',
|
|
119
|
-
name: 'Multi Target Trigger',
|
|
120
|
-
description: 'Triggers multiple resources',
|
|
121
|
-
version: '1.0.0',
|
|
122
|
-
status: 'prod'
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const relationships: ResourceRelationships = {
|
|
126
|
-
'trigger-multi': {
|
|
127
|
-
triggers: {
|
|
128
|
-
workflows: ['workflow-1', 'workflow-2'],
|
|
129
|
-
agents: ['agent-1']
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const resources: DeploymentSpec = {
|
|
135
|
-
triggers: [trigger],
|
|
136
|
-
relationships
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const edges = buildEdges(resources)
|
|
140
|
-
|
|
141
|
-
// Note: ResourceRelationships only supports agents/workflows, not externalResources
|
|
142
|
-
expect(edges).toHaveLength(3)
|
|
143
|
-
expect(edges).toEqual([
|
|
144
|
-
{
|
|
145
|
-
id: 'edge-0',
|
|
146
|
-
source: 'trigger-multi',
|
|
147
|
-
target: 'agent-1',
|
|
148
|
-
relationship: 'triggers'
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
id: 'edge-1',
|
|
152
|
-
source: 'trigger-multi',
|
|
153
|
-
target: 'workflow-1',
|
|
154
|
-
relationship: 'triggers'
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
id: 'edge-2',
|
|
158
|
-
source: 'trigger-multi',
|
|
159
|
-
target: 'workflow-2',
|
|
160
|
-
relationship: 'triggers'
|
|
161
|
-
}
|
|
162
|
-
])
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('handles triggers with no targets (empty relationships)', () => {
|
|
166
|
-
const trigger: TriggerDefinition = {
|
|
167
|
-
resourceId: 'trigger-empty',
|
|
168
|
-
type: 'trigger',
|
|
169
|
-
triggerType: 'manual',
|
|
170
|
-
name: 'Manual Trigger',
|
|
171
|
-
description: 'Manual trigger with no targets',
|
|
172
|
-
version: '1.0.0',
|
|
173
|
-
status: 'dev'
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const relationships: ResourceRelationships = {
|
|
177
|
-
'trigger-empty': {
|
|
178
|
-
triggers: {
|
|
179
|
-
workflows: [],
|
|
180
|
-
agents: []
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const resources: DeploymentSpec = {
|
|
186
|
-
triggers: [trigger],
|
|
187
|
-
relationships
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const edges = buildEdges(resources)
|
|
191
|
-
|
|
192
|
-
expect(edges).toHaveLength(0)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('handles triggers with no relationships declared', () => {
|
|
196
|
-
const trigger: TriggerDefinition = {
|
|
197
|
-
resourceId: 'trigger-no-targets',
|
|
198
|
-
type: 'trigger',
|
|
199
|
-
triggerType: 'webhook',
|
|
200
|
-
name: 'No Targets Trigger',
|
|
201
|
-
description: 'Trigger without relationships',
|
|
202
|
-
version: '1.0.0',
|
|
203
|
-
status: 'dev'
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const resources: DeploymentSpec = {
|
|
207
|
-
triggers: [trigger]
|
|
208
|
-
// No relationships declared for this trigger
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const edges = buildEdges(resources)
|
|
212
|
-
|
|
213
|
-
expect(edges).toHaveLength(0)
|
|
214
|
-
})
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
describe('Relationship Edges', () => {
|
|
218
|
-
it('creates "triggers" edge for resource -> agent relationship', () => {
|
|
219
|
-
const relationships: ResourceRelationships = {
|
|
220
|
-
'order-processor': {
|
|
221
|
-
triggers: {
|
|
222
|
-
agents: ['fulfillment-agent']
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const resources: DeploymentSpec = {
|
|
228
|
-
relationships
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const edges = buildEdges(resources)
|
|
232
|
-
|
|
233
|
-
expect(edges).toHaveLength(1)
|
|
234
|
-
expect(edges[0]).toEqual({
|
|
235
|
-
id: 'edge-0',
|
|
236
|
-
source: 'order-processor',
|
|
237
|
-
target: 'fulfillment-agent',
|
|
238
|
-
relationship: 'triggers'
|
|
239
|
-
})
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
it('creates "triggers" edge for resource -> workflow relationship', () => {
|
|
243
|
-
const relationships: ResourceRelationships = {
|
|
244
|
-
'triage-agent': {
|
|
245
|
-
triggers: {
|
|
246
|
-
workflows: ['escalation-workflow']
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const resources: DeploymentSpec = {
|
|
252
|
-
relationships
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const edges = buildEdges(resources)
|
|
256
|
-
|
|
257
|
-
expect(edges).toHaveLength(1)
|
|
258
|
-
expect(edges[0]).toEqual({
|
|
259
|
-
id: 'edge-0',
|
|
260
|
-
source: 'triage-agent',
|
|
261
|
-
target: 'escalation-workflow',
|
|
262
|
-
relationship: 'triggers'
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('creates "uses" edge for resource -> integration relationship', () => {
|
|
267
|
-
const relationships: ResourceRelationships = {
|
|
268
|
-
'data-sync-workflow': {
|
|
269
|
-
uses: {
|
|
270
|
-
integrations: ['integration-postgres', 'integration-shopify']
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const resources: DeploymentSpec = {
|
|
276
|
-
relationships
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const edges = buildEdges(resources)
|
|
280
|
-
|
|
281
|
-
expect(edges).toHaveLength(2)
|
|
282
|
-
expect(edges).toEqual([
|
|
283
|
-
{
|
|
284
|
-
id: 'edge-0',
|
|
285
|
-
source: 'data-sync-workflow',
|
|
286
|
-
target: 'integration-postgres',
|
|
287
|
-
relationship: 'uses'
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
id: 'edge-1',
|
|
291
|
-
source: 'data-sync-workflow',
|
|
292
|
-
target: 'integration-shopify',
|
|
293
|
-
relationship: 'uses'
|
|
294
|
-
}
|
|
295
|
-
])
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
it('handles relationships with all relationship types', () => {
|
|
299
|
-
const relationships: ResourceRelationships = {
|
|
300
|
-
'complex-agent': {
|
|
301
|
-
triggers: {
|
|
302
|
-
agents: ['agent-1', 'agent-2'],
|
|
303
|
-
workflows: ['workflow-1']
|
|
304
|
-
},
|
|
305
|
-
uses: {
|
|
306
|
-
integrations: ['integration-1', 'integration-2']
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const resources: DeploymentSpec = {
|
|
312
|
-
relationships
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const edges = buildEdges(resources)
|
|
316
|
-
|
|
317
|
-
expect(edges).toHaveLength(5)
|
|
318
|
-
expect(edges).toEqual([
|
|
319
|
-
{
|
|
320
|
-
id: 'edge-0',
|
|
321
|
-
source: 'complex-agent',
|
|
322
|
-
target: 'agent-1',
|
|
323
|
-
relationship: 'triggers'
|
|
324
|
-
},
|
|
325
|
-
{
|
|
326
|
-
id: 'edge-1',
|
|
327
|
-
source: 'complex-agent',
|
|
328
|
-
target: 'agent-2',
|
|
329
|
-
relationship: 'triggers'
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
id: 'edge-2',
|
|
333
|
-
source: 'complex-agent',
|
|
334
|
-
target: 'workflow-1',
|
|
335
|
-
relationship: 'triggers'
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
id: 'edge-3',
|
|
339
|
-
source: 'complex-agent',
|
|
340
|
-
target: 'integration-1',
|
|
341
|
-
relationship: 'uses'
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
id: 'edge-4',
|
|
345
|
-
source: 'complex-agent',
|
|
346
|
-
target: 'integration-2',
|
|
347
|
-
relationship: 'uses'
|
|
348
|
-
}
|
|
349
|
-
])
|
|
350
|
-
})
|
|
351
|
-
|
|
352
|
-
it('handles empty relationships map', () => {
|
|
353
|
-
const relationships: ResourceRelationships = {}
|
|
354
|
-
|
|
355
|
-
const resources: DeploymentSpec = {
|
|
356
|
-
relationships
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const edges = buildEdges(resources)
|
|
360
|
-
|
|
361
|
-
expect(edges).toHaveLength(0)
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
it('handles undefined relationships', () => {
|
|
365
|
-
const resources: DeploymentSpec = {
|
|
366
|
-
// No relationships field
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const edges = buildEdges(resources)
|
|
370
|
-
|
|
371
|
-
expect(edges).toHaveLength(0)
|
|
372
|
-
})
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
describe('External Resource Edges', () => {
|
|
376
|
-
it('creates "triggers" edge for external -> workflow', () => {
|
|
377
|
-
const external: ExternalResourceDefinition = {
|
|
378
|
-
resourceId: 'external-n8n-sync',
|
|
379
|
-
type: 'external',
|
|
380
|
-
platform: 'n8n',
|
|
381
|
-
name: 'Order Sync',
|
|
382
|
-
description: 'Legacy n8n order sync',
|
|
383
|
-
version: '1.0.0',
|
|
384
|
-
status: 'prod',
|
|
385
|
-
triggers: {
|
|
386
|
-
workflows: ['order-fulfillment']
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const resources: DeploymentSpec = {
|
|
391
|
-
externalResources: [external]
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const edges = buildEdges(resources)
|
|
395
|
-
|
|
396
|
-
expect(edges).toHaveLength(1)
|
|
397
|
-
expect(edges[0]).toEqual({
|
|
398
|
-
id: 'edge-0',
|
|
399
|
-
source: 'external-n8n-sync',
|
|
400
|
-
target: 'order-fulfillment',
|
|
401
|
-
relationship: 'triggers'
|
|
402
|
-
})
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
it('creates "triggers" edge for external -> agent', () => {
|
|
406
|
-
const external: ExternalResourceDefinition = {
|
|
407
|
-
resourceId: 'external-zapier-lead',
|
|
408
|
-
type: 'external',
|
|
409
|
-
platform: 'zapier',
|
|
410
|
-
name: 'Lead Capture',
|
|
411
|
-
description: 'Zapier lead capture',
|
|
412
|
-
version: '1.0.0',
|
|
413
|
-
status: 'prod',
|
|
414
|
-
triggers: {
|
|
415
|
-
agents: ['lead-router-agent']
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const resources: DeploymentSpec = {
|
|
420
|
-
externalResources: [external]
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const edges = buildEdges(resources)
|
|
424
|
-
|
|
425
|
-
expect(edges).toHaveLength(1)
|
|
426
|
-
expect(edges[0]).toEqual({
|
|
427
|
-
id: 'edge-0',
|
|
428
|
-
source: 'external-zapier-lead',
|
|
429
|
-
target: 'lead-router-agent',
|
|
430
|
-
relationship: 'triggers'
|
|
431
|
-
})
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
it('creates "uses" edge for external -> integration', () => {
|
|
435
|
-
const external: ExternalResourceDefinition = {
|
|
436
|
-
resourceId: 'external-make-crm',
|
|
437
|
-
type: 'external',
|
|
438
|
-
platform: 'make',
|
|
439
|
-
name: 'CRM Sync',
|
|
440
|
-
description: 'Make CRM sync',
|
|
441
|
-
version: '1.0.0',
|
|
442
|
-
status: 'prod',
|
|
443
|
-
uses: {
|
|
444
|
-
integrations: ['integration-salesforce']
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const resources: DeploymentSpec = {
|
|
449
|
-
externalResources: [external]
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const edges = buildEdges(resources)
|
|
453
|
-
|
|
454
|
-
expect(edges).toHaveLength(1)
|
|
455
|
-
expect(edges[0]).toEqual({
|
|
456
|
-
id: 'edge-0',
|
|
457
|
-
source: 'external-make-crm',
|
|
458
|
-
target: 'integration-salesforce',
|
|
459
|
-
relationship: 'uses'
|
|
460
|
-
})
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
it('does NOT create edges for triggeredBy (removed field)', () => {
|
|
464
|
-
// This test verifies the field was removed and edges are forward-only
|
|
465
|
-
const external: ExternalResourceDefinition = {
|
|
466
|
-
resourceId: 'external-legacy',
|
|
467
|
-
type: 'external',
|
|
468
|
-
platform: 'n8n',
|
|
469
|
-
name: 'Legacy Automation',
|
|
470
|
-
description: 'Legacy automation',
|
|
471
|
-
version: '1.0.0',
|
|
472
|
-
status: 'dev'
|
|
473
|
-
// No triggeredBy field - it was removed per relationship-consolidation
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const resources: DeploymentSpec = {
|
|
477
|
-
externalResources: [external]
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const edges = buildEdges(resources)
|
|
481
|
-
|
|
482
|
-
// Should produce no edges since external has no forward relationships
|
|
483
|
-
expect(edges).toHaveLength(0)
|
|
484
|
-
})
|
|
485
|
-
|
|
486
|
-
it('handles external resources with no relationships', () => {
|
|
487
|
-
const external: ExternalResourceDefinition = {
|
|
488
|
-
resourceId: 'external-isolated',
|
|
489
|
-
type: 'external',
|
|
490
|
-
platform: 'other',
|
|
491
|
-
name: 'Isolated Automation',
|
|
492
|
-
description: 'No relationships',
|
|
493
|
-
version: '1.0.0',
|
|
494
|
-
status: 'dev'
|
|
495
|
-
// No triggers or uses
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const resources: DeploymentSpec = {
|
|
499
|
-
externalResources: [external]
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
const edges = buildEdges(resources)
|
|
503
|
-
|
|
504
|
-
expect(edges).toHaveLength(0)
|
|
505
|
-
})
|
|
506
|
-
|
|
507
|
-
it('handles external resources with all relationship types', () => {
|
|
508
|
-
const external: ExternalResourceDefinition = {
|
|
509
|
-
resourceId: 'external-complex',
|
|
510
|
-
type: 'external',
|
|
511
|
-
platform: 'n8n',
|
|
512
|
-
name: 'Complex Automation',
|
|
513
|
-
description: 'Complex external automation',
|
|
514
|
-
version: '1.0.0',
|
|
515
|
-
status: 'prod',
|
|
516
|
-
triggers: {
|
|
517
|
-
workflows: ['workflow-1'],
|
|
518
|
-
agents: ['agent-1', 'agent-2']
|
|
519
|
-
},
|
|
520
|
-
uses: {
|
|
521
|
-
integrations: ['integration-1']
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
const resources: DeploymentSpec = {
|
|
526
|
-
externalResources: [external]
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const edges = buildEdges(resources)
|
|
530
|
-
|
|
531
|
-
expect(edges).toHaveLength(4)
|
|
532
|
-
expect(edges).toEqual([
|
|
533
|
-
{
|
|
534
|
-
id: 'edge-0',
|
|
535
|
-
source: 'external-complex',
|
|
536
|
-
target: 'workflow-1',
|
|
537
|
-
relationship: 'triggers'
|
|
538
|
-
},
|
|
539
|
-
{
|
|
540
|
-
id: 'edge-1',
|
|
541
|
-
source: 'external-complex',
|
|
542
|
-
target: 'agent-1',
|
|
543
|
-
relationship: 'triggers'
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
id: 'edge-2',
|
|
547
|
-
source: 'external-complex',
|
|
548
|
-
target: 'agent-2',
|
|
549
|
-
relationship: 'triggers'
|
|
550
|
-
},
|
|
551
|
-
{
|
|
552
|
-
id: 'edge-3',
|
|
553
|
-
source: 'external-complex',
|
|
554
|
-
target: 'integration-1',
|
|
555
|
-
relationship: 'uses'
|
|
556
|
-
}
|
|
557
|
-
])
|
|
558
|
-
})
|
|
559
|
-
})
|
|
560
|
-
|
|
561
|
-
describe('Human Checkpoint Edges', () => {
|
|
562
|
-
it('creates "approval" edge for agent -> humanCheckpoint (requestedBy)', () => {
|
|
563
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
564
|
-
resourceId: 'approval-sales',
|
|
565
|
-
type: 'human',
|
|
566
|
-
name: 'Sales Approval',
|
|
567
|
-
description: 'Sales team approval queue',
|
|
568
|
-
version: '1.0.0',
|
|
569
|
-
status: 'prod',
|
|
570
|
-
requestedBy: {
|
|
571
|
-
agents: ['order-processor-agent']
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
const resources: DeploymentSpec = {
|
|
576
|
-
humanCheckpoints: [humanCheckpoint]
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const edges = buildEdges(resources)
|
|
580
|
-
|
|
581
|
-
expect(edges).toHaveLength(1)
|
|
582
|
-
expect(edges[0]).toEqual({
|
|
583
|
-
id: 'edge-0',
|
|
584
|
-
source: 'order-processor-agent',
|
|
585
|
-
target: 'approval-sales',
|
|
586
|
-
relationship: 'approval'
|
|
587
|
-
})
|
|
588
|
-
})
|
|
589
|
-
|
|
590
|
-
it('creates "approval" edge for workflow -> humanCheckpoint (requestedBy)', () => {
|
|
591
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
592
|
-
resourceId: 'approval-refund',
|
|
593
|
-
type: 'human',
|
|
594
|
-
name: 'Refund Approval',
|
|
595
|
-
description: 'Refund approval queue',
|
|
596
|
-
version: '1.0.0',
|
|
597
|
-
status: 'prod',
|
|
598
|
-
requestedBy: {
|
|
599
|
-
workflows: ['refund-workflow']
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const resources: DeploymentSpec = {
|
|
604
|
-
humanCheckpoints: [humanCheckpoint]
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const edges = buildEdges(resources)
|
|
608
|
-
|
|
609
|
-
expect(edges).toHaveLength(1)
|
|
610
|
-
expect(edges[0]).toEqual({
|
|
611
|
-
id: 'edge-0',
|
|
612
|
-
source: 'refund-workflow',
|
|
613
|
-
target: 'approval-refund',
|
|
614
|
-
relationship: 'approval'
|
|
615
|
-
})
|
|
616
|
-
})
|
|
617
|
-
|
|
618
|
-
it('creates "triggers" edge for humanCheckpoint -> agent (routesTo)', () => {
|
|
619
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
620
|
-
resourceId: 'approval-content',
|
|
621
|
-
type: 'human',
|
|
622
|
-
name: 'Content Approval',
|
|
623
|
-
description: 'Content approval queue',
|
|
624
|
-
version: '1.0.0',
|
|
625
|
-
status: 'prod',
|
|
626
|
-
routesTo: {
|
|
627
|
-
agents: ['publisher-agent']
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const resources: DeploymentSpec = {
|
|
632
|
-
humanCheckpoints: [humanCheckpoint]
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const edges = buildEdges(resources)
|
|
636
|
-
|
|
637
|
-
expect(edges).toHaveLength(1)
|
|
638
|
-
expect(edges[0]).toEqual({
|
|
639
|
-
id: 'edge-0',
|
|
640
|
-
source: 'approval-content',
|
|
641
|
-
target: 'publisher-agent',
|
|
642
|
-
relationship: 'triggers'
|
|
643
|
-
})
|
|
644
|
-
})
|
|
645
|
-
|
|
646
|
-
it('creates "triggers" edge for humanCheckpoint -> workflow (routesTo)', () => {
|
|
647
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
648
|
-
resourceId: 'approval-budget',
|
|
649
|
-
type: 'human',
|
|
650
|
-
name: 'Budget Approval',
|
|
651
|
-
description: 'Budget approval queue',
|
|
652
|
-
version: '1.0.0',
|
|
653
|
-
status: 'prod',
|
|
654
|
-
routesTo: {
|
|
655
|
-
workflows: ['procurement-workflow']
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const resources: DeploymentSpec = {
|
|
660
|
-
humanCheckpoints: [humanCheckpoint]
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
const edges = buildEdges(resources)
|
|
664
|
-
|
|
665
|
-
expect(edges).toHaveLength(1)
|
|
666
|
-
expect(edges[0]).toEqual({
|
|
667
|
-
id: 'edge-0',
|
|
668
|
-
source: 'approval-budget',
|
|
669
|
-
target: 'procurement-workflow',
|
|
670
|
-
relationship: 'triggers'
|
|
671
|
-
})
|
|
672
|
-
})
|
|
673
|
-
|
|
674
|
-
it('handles human checkpoints with only requestedBy', () => {
|
|
675
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
676
|
-
resourceId: 'approval-review-only',
|
|
677
|
-
type: 'human',
|
|
678
|
-
name: 'Review Only',
|
|
679
|
-
description: 'Review only checkpoint',
|
|
680
|
-
version: '1.0.0',
|
|
681
|
-
status: 'dev',
|
|
682
|
-
requestedBy: {
|
|
683
|
-
agents: ['agent-1', 'agent-2'],
|
|
684
|
-
workflows: ['workflow-1']
|
|
685
|
-
}
|
|
686
|
-
// No routesTo
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
const resources: DeploymentSpec = {
|
|
690
|
-
humanCheckpoints: [humanCheckpoint]
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
const edges = buildEdges(resources)
|
|
694
|
-
|
|
695
|
-
expect(edges).toHaveLength(3)
|
|
696
|
-
expect(edges).toEqual([
|
|
697
|
-
{
|
|
698
|
-
id: 'edge-0',
|
|
699
|
-
source: 'agent-1',
|
|
700
|
-
target: 'approval-review-only',
|
|
701
|
-
relationship: 'approval'
|
|
702
|
-
},
|
|
703
|
-
{
|
|
704
|
-
id: 'edge-1',
|
|
705
|
-
source: 'agent-2',
|
|
706
|
-
target: 'approval-review-only',
|
|
707
|
-
relationship: 'approval'
|
|
708
|
-
},
|
|
709
|
-
{
|
|
710
|
-
id: 'edge-2',
|
|
711
|
-
source: 'workflow-1',
|
|
712
|
-
target: 'approval-review-only',
|
|
713
|
-
relationship: 'approval'
|
|
714
|
-
}
|
|
715
|
-
])
|
|
716
|
-
})
|
|
717
|
-
|
|
718
|
-
it('handles human checkpoints with only routesTo', () => {
|
|
719
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
720
|
-
resourceId: 'approval-dispatch-only',
|
|
721
|
-
type: 'human',
|
|
722
|
-
name: 'Dispatch Only',
|
|
723
|
-
description: 'Dispatch only checkpoint',
|
|
724
|
-
version: '1.0.0',
|
|
725
|
-
status: 'dev',
|
|
726
|
-
// No requestedBy
|
|
727
|
-
routesTo: {
|
|
728
|
-
agents: ['agent-3'],
|
|
729
|
-
workflows: ['workflow-2', 'workflow-3']
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const resources: DeploymentSpec = {
|
|
734
|
-
humanCheckpoints: [humanCheckpoint]
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
const edges = buildEdges(resources)
|
|
738
|
-
|
|
739
|
-
expect(edges).toHaveLength(3)
|
|
740
|
-
expect(edges).toEqual([
|
|
741
|
-
{
|
|
742
|
-
id: 'edge-0',
|
|
743
|
-
source: 'approval-dispatch-only',
|
|
744
|
-
target: 'agent-3',
|
|
745
|
-
relationship: 'triggers'
|
|
746
|
-
},
|
|
747
|
-
{
|
|
748
|
-
id: 'edge-1',
|
|
749
|
-
source: 'approval-dispatch-only',
|
|
750
|
-
target: 'workflow-2',
|
|
751
|
-
relationship: 'triggers'
|
|
752
|
-
},
|
|
753
|
-
{
|
|
754
|
-
id: 'edge-2',
|
|
755
|
-
source: 'approval-dispatch-only',
|
|
756
|
-
target: 'workflow-3',
|
|
757
|
-
relationship: 'triggers'
|
|
758
|
-
}
|
|
759
|
-
])
|
|
760
|
-
})
|
|
761
|
-
|
|
762
|
-
it('handles human checkpoints with both requestedBy and routesTo', () => {
|
|
763
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
764
|
-
resourceId: 'approval-full',
|
|
765
|
-
type: 'human',
|
|
766
|
-
name: 'Full Approval',
|
|
767
|
-
description: 'Full approval with both directions',
|
|
768
|
-
version: '1.0.0',
|
|
769
|
-
status: 'prod',
|
|
770
|
-
requestedBy: {
|
|
771
|
-
agents: ['triage-agent'],
|
|
772
|
-
workflows: ['intake-workflow']
|
|
773
|
-
},
|
|
774
|
-
routesTo: {
|
|
775
|
-
agents: ['fulfillment-agent'],
|
|
776
|
-
workflows: ['processing-workflow']
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
const resources: DeploymentSpec = {
|
|
781
|
-
humanCheckpoints: [humanCheckpoint]
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
const edges = buildEdges(resources)
|
|
785
|
-
|
|
786
|
-
expect(edges).toHaveLength(4)
|
|
787
|
-
expect(edges).toEqual([
|
|
788
|
-
{
|
|
789
|
-
id: 'edge-0',
|
|
790
|
-
source: 'triage-agent',
|
|
791
|
-
target: 'approval-full',
|
|
792
|
-
relationship: 'approval'
|
|
793
|
-
},
|
|
794
|
-
{
|
|
795
|
-
id: 'edge-1',
|
|
796
|
-
source: 'intake-workflow',
|
|
797
|
-
target: 'approval-full',
|
|
798
|
-
relationship: 'approval'
|
|
799
|
-
},
|
|
800
|
-
{
|
|
801
|
-
id: 'edge-2',
|
|
802
|
-
source: 'approval-full',
|
|
803
|
-
target: 'fulfillment-agent',
|
|
804
|
-
relationship: 'triggers'
|
|
805
|
-
},
|
|
806
|
-
{
|
|
807
|
-
id: 'edge-3',
|
|
808
|
-
source: 'approval-full',
|
|
809
|
-
target: 'processing-workflow',
|
|
810
|
-
relationship: 'triggers'
|
|
811
|
-
}
|
|
812
|
-
])
|
|
813
|
-
})
|
|
814
|
-
})
|
|
815
|
-
|
|
816
|
-
describe('Edge ID Generation', () => {
|
|
817
|
-
it('generates unique edge IDs across all edge types', () => {
|
|
818
|
-
const trigger: TriggerDefinition = {
|
|
819
|
-
resourceId: 'trigger-1',
|
|
820
|
-
type: 'trigger',
|
|
821
|
-
triggerType: 'webhook',
|
|
822
|
-
name: 'Trigger',
|
|
823
|
-
description: 'Test trigger',
|
|
824
|
-
version: '1.0.0',
|
|
825
|
-
status: 'dev'
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
const relationships: ResourceRelationships = {
|
|
829
|
-
'trigger-1': {
|
|
830
|
-
triggers: { workflows: ['workflow-1'] }
|
|
831
|
-
},
|
|
832
|
-
'agent-1': {
|
|
833
|
-
uses: { integrations: ['integration-1'] }
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
const external: ExternalResourceDefinition = {
|
|
838
|
-
resourceId: 'external-1',
|
|
839
|
-
type: 'external',
|
|
840
|
-
platform: 'n8n',
|
|
841
|
-
name: 'External',
|
|
842
|
-
description: 'Test external',
|
|
843
|
-
version: '1.0.0',
|
|
844
|
-
status: 'dev',
|
|
845
|
-
triggers: { agents: ['agent-2'] }
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
849
|
-
resourceId: 'approval-1',
|
|
850
|
-
type: 'human',
|
|
851
|
-
name: 'Approval',
|
|
852
|
-
description: 'Test approval',
|
|
853
|
-
version: '1.0.0',
|
|
854
|
-
status: 'dev',
|
|
855
|
-
requestedBy: { agents: ['agent-3'] }
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
const resources: DeploymentSpec = {
|
|
859
|
-
triggers: [trigger],
|
|
860
|
-
relationships,
|
|
861
|
-
externalResources: [external],
|
|
862
|
-
humanCheckpoints: [humanCheckpoint]
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
const edges = buildEdges(resources)
|
|
866
|
-
|
|
867
|
-
// Verify all edge IDs are unique and sequential
|
|
868
|
-
const edgeIds = edges.map((e) => e.id)
|
|
869
|
-
expect(edgeIds).toEqual(['edge-0', 'edge-1', 'edge-2', 'edge-3'])
|
|
870
|
-
|
|
871
|
-
// Verify no duplicates
|
|
872
|
-
const uniqueIds = new Set(edgeIds)
|
|
873
|
-
expect(uniqueIds.size).toBe(edgeIds.length)
|
|
874
|
-
})
|
|
875
|
-
|
|
876
|
-
it('maintains sequential edge numbering starting from 0', () => {
|
|
877
|
-
const trigger: TriggerDefinition = {
|
|
878
|
-
resourceId: 'trigger-multi',
|
|
879
|
-
type: 'trigger',
|
|
880
|
-
triggerType: 'webhook',
|
|
881
|
-
name: 'Multi',
|
|
882
|
-
description: 'Multi-target trigger',
|
|
883
|
-
version: '1.0.0',
|
|
884
|
-
status: 'dev'
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
const relationships: ResourceRelationships = {
|
|
888
|
-
'trigger-multi': {
|
|
889
|
-
triggers: {
|
|
890
|
-
workflows: ['w1', 'w2'],
|
|
891
|
-
agents: ['a1', 'a2', 'a3']
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
const resources: DeploymentSpec = {
|
|
897
|
-
triggers: [trigger],
|
|
898
|
-
relationships
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
const edges = buildEdges(resources)
|
|
902
|
-
|
|
903
|
-
expect(edges).toHaveLength(5)
|
|
904
|
-
expect(edges[0].id).toBe('edge-0')
|
|
905
|
-
expect(edges[1].id).toBe('edge-1')
|
|
906
|
-
expect(edges[2].id).toBe('edge-2')
|
|
907
|
-
expect(edges[3].id).toBe('edge-3')
|
|
908
|
-
expect(edges[4].id).toBe('edge-4')
|
|
909
|
-
})
|
|
910
|
-
})
|
|
911
|
-
|
|
912
|
-
describe('Edge Type Consolidation', () => {
|
|
913
|
-
it('only produces edges with types: triggers, uses, approval', () => {
|
|
914
|
-
// Create a comprehensive set of resources to test all edge types
|
|
915
|
-
const trigger: TriggerDefinition = {
|
|
916
|
-
resourceId: 'trigger-1',
|
|
917
|
-
type: 'trigger',
|
|
918
|
-
triggerType: 'webhook',
|
|
919
|
-
name: 'Trigger',
|
|
920
|
-
description: 'Test',
|
|
921
|
-
version: '1.0.0',
|
|
922
|
-
status: 'dev'
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
const relationships: ResourceRelationships = {
|
|
926
|
-
'trigger-1': {
|
|
927
|
-
triggers: { workflows: ['workflow-1'] }
|
|
928
|
-
},
|
|
929
|
-
'agent-1': {
|
|
930
|
-
triggers: { agents: ['agent-2'] },
|
|
931
|
-
uses: { integrations: ['integration-1'] }
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
const external: ExternalResourceDefinition = {
|
|
936
|
-
resourceId: 'external-1',
|
|
937
|
-
type: 'external',
|
|
938
|
-
platform: 'n8n',
|
|
939
|
-
name: 'External',
|
|
940
|
-
description: 'Test',
|
|
941
|
-
version: '1.0.0',
|
|
942
|
-
status: 'dev',
|
|
943
|
-
triggers: { workflows: ['workflow-2'] },
|
|
944
|
-
uses: { integrations: ['integration-2'] }
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
948
|
-
resourceId: 'approval-1',
|
|
949
|
-
type: 'human',
|
|
950
|
-
name: 'Approval',
|
|
951
|
-
description: 'Test',
|
|
952
|
-
version: '1.0.0',
|
|
953
|
-
status: 'dev',
|
|
954
|
-
requestedBy: { agents: ['agent-3'] },
|
|
955
|
-
routesTo: { workflows: ['workflow-3'] }
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
const resources: DeploymentSpec = {
|
|
959
|
-
triggers: [trigger],
|
|
960
|
-
relationships,
|
|
961
|
-
externalResources: [external],
|
|
962
|
-
humanCheckpoints: [humanCheckpoint]
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const edges = buildEdges(resources)
|
|
966
|
-
|
|
967
|
-
// Verify ALL edges have one of the three allowed relationship types
|
|
968
|
-
const relationshipTypes = edges.map((e) => e.relationship)
|
|
969
|
-
const allowedTypes: CommandViewEdge['relationship'][] = ['triggers', 'uses', 'approval']
|
|
970
|
-
|
|
971
|
-
for (const relType of relationshipTypes) {
|
|
972
|
-
expect(allowedTypes).toContain(relType)
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// Count each type
|
|
976
|
-
const typeCount = {
|
|
977
|
-
triggers: relationshipTypes.filter((t) => t === 'triggers').length,
|
|
978
|
-
uses: relationshipTypes.filter((t) => t === 'uses').length,
|
|
979
|
-
approval: relationshipTypes.filter((t) => t === 'approval').length
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
// Verify we have edges of each type (comprehensive test)
|
|
983
|
-
expect(typeCount.triggers).toBeGreaterThan(0)
|
|
984
|
-
expect(typeCount.uses).toBeGreaterThan(0)
|
|
985
|
-
expect(typeCount.approval).toBeGreaterThan(0)
|
|
986
|
-
|
|
987
|
-
// Verify total matches sum (no other edge types)
|
|
988
|
-
expect(edges.length).toBe(typeCount.triggers + typeCount.uses + typeCount.approval)
|
|
989
|
-
})
|
|
990
|
-
|
|
991
|
-
it('does NOT produce edges with type: invokes', () => {
|
|
992
|
-
// This test verifies the old 'invokes' edge type is gone
|
|
993
|
-
const trigger: TriggerDefinition = {
|
|
994
|
-
resourceId: 'trigger-test',
|
|
995
|
-
type: 'trigger',
|
|
996
|
-
triggerType: 'webhook',
|
|
997
|
-
name: 'Test',
|
|
998
|
-
description: 'Test trigger',
|
|
999
|
-
version: '1.0.0',
|
|
1000
|
-
status: 'dev'
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
const relationships: ResourceRelationships = {
|
|
1004
|
-
'trigger-test': {
|
|
1005
|
-
triggers: {
|
|
1006
|
-
workflows: ['workflow-1'],
|
|
1007
|
-
agents: ['agent-1']
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
const resources: DeploymentSpec = {
|
|
1013
|
-
triggers: [trigger],
|
|
1014
|
-
relationships
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
const edges = buildEdges(resources)
|
|
1018
|
-
|
|
1019
|
-
// Verify no edge has relationship 'invokes'
|
|
1020
|
-
const hasInvokesEdge = edges.some((e: CommandViewEdge) => {
|
|
1021
|
-
// Use type assertion to check for the old type
|
|
1022
|
-
return (e.relationship as string) === 'invokes'
|
|
1023
|
-
})
|
|
1024
|
-
|
|
1025
|
-
expect(hasInvokesEdge).toBe(false)
|
|
1026
|
-
|
|
1027
|
-
// All edges should be 'triggers' in this case
|
|
1028
|
-
edges.forEach((edge) => {
|
|
1029
|
-
expect(edge.relationship).toBe('triggers')
|
|
1030
|
-
})
|
|
1031
|
-
})
|
|
1032
|
-
})
|
|
1033
|
-
|
|
1034
|
-
describe('Complex Multi-Resource Scenarios', () => {
|
|
1035
|
-
it('handles organization with all resource types', () => {
|
|
1036
|
-
const trigger: TriggerDefinition = {
|
|
1037
|
-
resourceId: 'trigger-order',
|
|
1038
|
-
type: 'trigger',
|
|
1039
|
-
triggerType: 'webhook',
|
|
1040
|
-
name: 'Order Webhook',
|
|
1041
|
-
description: 'New orders',
|
|
1042
|
-
version: '1.0.0',
|
|
1043
|
-
status: 'prod'
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
const relationships: ResourceRelationships = {
|
|
1047
|
-
'trigger-order': {
|
|
1048
|
-
triggers: { workflows: ['order-workflow'] }
|
|
1049
|
-
},
|
|
1050
|
-
'order-workflow': {
|
|
1051
|
-
triggers: { agents: ['fulfillment-agent'] },
|
|
1052
|
-
uses: { integrations: ['integration-shopify'] }
|
|
1053
|
-
},
|
|
1054
|
-
'fulfillment-agent': {
|
|
1055
|
-
uses: { integrations: ['integration-shipstation'] }
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
const external: ExternalResourceDefinition = {
|
|
1060
|
-
resourceId: 'external-inventory',
|
|
1061
|
-
type: 'external',
|
|
1062
|
-
platform: 'n8n',
|
|
1063
|
-
name: 'Inventory Sync',
|
|
1064
|
-
description: 'Legacy inventory sync',
|
|
1065
|
-
version: '1.0.0',
|
|
1066
|
-
status: 'prod',
|
|
1067
|
-
uses: { integrations: ['integration-postgres'] }
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
1071
|
-
resourceId: 'approval-high-value',
|
|
1072
|
-
type: 'human',
|
|
1073
|
-
name: 'High Value Orders',
|
|
1074
|
-
description: 'Manual approval for high value',
|
|
1075
|
-
version: '1.0.0',
|
|
1076
|
-
status: 'prod',
|
|
1077
|
-
requestedBy: { agents: ['fulfillment-agent'] },
|
|
1078
|
-
routesTo: { workflows: ['special-handling-workflow'] }
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
const resources: DeploymentSpec = {
|
|
1082
|
-
triggers: [trigger],
|
|
1083
|
-
relationships,
|
|
1084
|
-
externalResources: [external],
|
|
1085
|
-
humanCheckpoints: [humanCheckpoint]
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
const edges = buildEdges(resources)
|
|
1089
|
-
|
|
1090
|
-
// Verify we got edges from all sources
|
|
1091
|
-
const sourceIds = new Set(edges.map((e) => e.source))
|
|
1092
|
-
expect(sourceIds).toContain('trigger-order')
|
|
1093
|
-
expect(sourceIds).toContain('order-workflow')
|
|
1094
|
-
expect(sourceIds).toContain('fulfillment-agent')
|
|
1095
|
-
expect(sourceIds).toContain('external-inventory')
|
|
1096
|
-
expect(sourceIds).toContain('approval-high-value')
|
|
1097
|
-
|
|
1098
|
-
// Verify relationship type distribution
|
|
1099
|
-
const relationshipTypes = edges.map((e) => e.relationship)
|
|
1100
|
-
expect(relationshipTypes).toContain('triggers')
|
|
1101
|
-
expect(relationshipTypes).toContain('uses')
|
|
1102
|
-
expect(relationshipTypes).toContain('approval')
|
|
1103
|
-
})
|
|
1104
|
-
|
|
1105
|
-
it('handles empty organization resources', () => {
|
|
1106
|
-
const resources: DeploymentSpec = {}
|
|
1107
|
-
|
|
1108
|
-
const edges = buildEdges(resources)
|
|
1109
|
-
|
|
1110
|
-
expect(edges).toHaveLength(0)
|
|
1111
|
-
expect(edges).toEqual([])
|
|
1112
|
-
})
|
|
1113
|
-
|
|
1114
|
-
it('handles organization with only empty arrays', () => {
|
|
1115
|
-
const resources: DeploymentSpec = {
|
|
1116
|
-
triggers: [],
|
|
1117
|
-
externalResources: [],
|
|
1118
|
-
humanCheckpoints: [],
|
|
1119
|
-
relationships: {}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
const edges = buildEdges(resources)
|
|
1123
|
-
|
|
1124
|
-
expect(edges).toHaveLength(0)
|
|
1125
|
-
})
|
|
1126
|
-
})
|
|
1127
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* Test: Registry Serialization - buildEdges
|
|
3
|
+
* Tests for buildEdges function that creates Command View edges from resource relationships
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect } from 'vitest'
|
|
7
|
+
import { buildEdges } from '../serialization'
|
|
8
|
+
import type { DeploymentSpec } from '../resource-registry'
|
|
9
|
+
import type {
|
|
10
|
+
TriggerDefinition,
|
|
11
|
+
ExternalResourceDefinition,
|
|
12
|
+
HumanCheckpointDefinition,
|
|
13
|
+
ResourceRelationships
|
|
14
|
+
} from '../types'
|
|
15
|
+
import type { CommandViewEdge } from '../command-view'
|
|
16
|
+
|
|
17
|
+
describe('buildEdges', () => {
|
|
18
|
+
describe('Trigger Edges', () => {
|
|
19
|
+
it('creates "triggers" edge for trigger -> workflow', () => {
|
|
20
|
+
const trigger: TriggerDefinition = {
|
|
21
|
+
resourceId: 'trigger-webhook',
|
|
22
|
+
type: 'trigger',
|
|
23
|
+
triggerType: 'webhook',
|
|
24
|
+
name: 'Order Webhook',
|
|
25
|
+
description: 'Webhook for new orders',
|
|
26
|
+
version: '1.0.0',
|
|
27
|
+
status: 'prod'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const relationships: ResourceRelationships = {
|
|
31
|
+
'trigger-webhook': {
|
|
32
|
+
triggers: { workflows: ['order-workflow'] }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const resources: DeploymentSpec = {
|
|
37
|
+
triggers: [trigger],
|
|
38
|
+
relationships
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const edges = buildEdges(resources)
|
|
42
|
+
|
|
43
|
+
expect(edges).toHaveLength(1)
|
|
44
|
+
expect(edges[0]).toEqual({
|
|
45
|
+
id: 'edge-0',
|
|
46
|
+
source: 'trigger-webhook',
|
|
47
|
+
target: 'order-workflow',
|
|
48
|
+
relationship: 'triggers'
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('creates "triggers" edge for trigger -> agent', () => {
|
|
53
|
+
const trigger: TriggerDefinition = {
|
|
54
|
+
resourceId: 'trigger-schedule',
|
|
55
|
+
type: 'trigger',
|
|
56
|
+
triggerType: 'schedule',
|
|
57
|
+
name: 'Daily Report',
|
|
58
|
+
description: 'Daily report trigger',
|
|
59
|
+
version: '1.0.0',
|
|
60
|
+
status: 'prod',
|
|
61
|
+
schedule: '0 6 * * *'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const relationships: ResourceRelationships = {
|
|
65
|
+
'trigger-schedule': {
|
|
66
|
+
triggers: { agents: ['report-agent'] }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const resources: DeploymentSpec = {
|
|
71
|
+
triggers: [trigger],
|
|
72
|
+
relationships
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const edges = buildEdges(resources)
|
|
76
|
+
|
|
77
|
+
expect(edges).toHaveLength(1)
|
|
78
|
+
expect(edges[0]).toEqual({
|
|
79
|
+
id: 'edge-0',
|
|
80
|
+
source: 'trigger-schedule',
|
|
81
|
+
target: 'report-agent',
|
|
82
|
+
relationship: 'triggers'
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('creates "triggers" edge for trigger -> externalResource (via relationships)', () => {
|
|
87
|
+
// Note: trigger -> externalResource edges are created via ResourceRelationships
|
|
88
|
+
// The triggers.externalResources field doesn't exist - use relationships instead
|
|
89
|
+
const trigger: TriggerDefinition = {
|
|
90
|
+
resourceId: 'trigger-event',
|
|
91
|
+
type: 'trigger',
|
|
92
|
+
triggerType: 'event',
|
|
93
|
+
name: 'Low Stock Event',
|
|
94
|
+
description: 'Triggers on low stock',
|
|
95
|
+
version: '1.0.0',
|
|
96
|
+
status: 'prod',
|
|
97
|
+
eventType: 'low-stock-alert'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Note: ResourceRelationships.triggers only supports agents/workflows
|
|
101
|
+
// For external resources, edges come from ExternalResourceDefinition.triggers (external -> internal)
|
|
102
|
+
// There's no mechanism for trigger -> external edges in the current model
|
|
103
|
+
const resources: DeploymentSpec = {
|
|
104
|
+
triggers: [trigger]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const edges = buildEdges(resources)
|
|
108
|
+
|
|
109
|
+
// No edges created - trigger -> external relationships are not supported
|
|
110
|
+
// External resources declare what THEY trigger (external -> internal), not the reverse
|
|
111
|
+
expect(edges).toHaveLength(0)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('handles triggers with multiple targets', () => {
|
|
115
|
+
const trigger: TriggerDefinition = {
|
|
116
|
+
resourceId: 'trigger-multi',
|
|
117
|
+
type: 'trigger',
|
|
118
|
+
triggerType: 'webhook',
|
|
119
|
+
name: 'Multi Target Trigger',
|
|
120
|
+
description: 'Triggers multiple resources',
|
|
121
|
+
version: '1.0.0',
|
|
122
|
+
status: 'prod'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const relationships: ResourceRelationships = {
|
|
126
|
+
'trigger-multi': {
|
|
127
|
+
triggers: {
|
|
128
|
+
workflows: ['workflow-1', 'workflow-2'],
|
|
129
|
+
agents: ['agent-1']
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const resources: DeploymentSpec = {
|
|
135
|
+
triggers: [trigger],
|
|
136
|
+
relationships
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const edges = buildEdges(resources)
|
|
140
|
+
|
|
141
|
+
// Note: ResourceRelationships only supports agents/workflows, not externalResources
|
|
142
|
+
expect(edges).toHaveLength(3)
|
|
143
|
+
expect(edges).toEqual([
|
|
144
|
+
{
|
|
145
|
+
id: 'edge-0',
|
|
146
|
+
source: 'trigger-multi',
|
|
147
|
+
target: 'agent-1',
|
|
148
|
+
relationship: 'triggers'
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'edge-1',
|
|
152
|
+
source: 'trigger-multi',
|
|
153
|
+
target: 'workflow-1',
|
|
154
|
+
relationship: 'triggers'
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: 'edge-2',
|
|
158
|
+
source: 'trigger-multi',
|
|
159
|
+
target: 'workflow-2',
|
|
160
|
+
relationship: 'triggers'
|
|
161
|
+
}
|
|
162
|
+
])
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('handles triggers with no targets (empty relationships)', () => {
|
|
166
|
+
const trigger: TriggerDefinition = {
|
|
167
|
+
resourceId: 'trigger-empty',
|
|
168
|
+
type: 'trigger',
|
|
169
|
+
triggerType: 'manual',
|
|
170
|
+
name: 'Manual Trigger',
|
|
171
|
+
description: 'Manual trigger with no targets',
|
|
172
|
+
version: '1.0.0',
|
|
173
|
+
status: 'dev'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const relationships: ResourceRelationships = {
|
|
177
|
+
'trigger-empty': {
|
|
178
|
+
triggers: {
|
|
179
|
+
workflows: [],
|
|
180
|
+
agents: []
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const resources: DeploymentSpec = {
|
|
186
|
+
triggers: [trigger],
|
|
187
|
+
relationships
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const edges = buildEdges(resources)
|
|
191
|
+
|
|
192
|
+
expect(edges).toHaveLength(0)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('handles triggers with no relationships declared', () => {
|
|
196
|
+
const trigger: TriggerDefinition = {
|
|
197
|
+
resourceId: 'trigger-no-targets',
|
|
198
|
+
type: 'trigger',
|
|
199
|
+
triggerType: 'webhook',
|
|
200
|
+
name: 'No Targets Trigger',
|
|
201
|
+
description: 'Trigger without relationships',
|
|
202
|
+
version: '1.0.0',
|
|
203
|
+
status: 'dev'
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const resources: DeploymentSpec = {
|
|
207
|
+
triggers: [trigger]
|
|
208
|
+
// No relationships declared for this trigger
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const edges = buildEdges(resources)
|
|
212
|
+
|
|
213
|
+
expect(edges).toHaveLength(0)
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
describe('Relationship Edges', () => {
|
|
218
|
+
it('creates "triggers" edge for resource -> agent relationship', () => {
|
|
219
|
+
const relationships: ResourceRelationships = {
|
|
220
|
+
'order-processor': {
|
|
221
|
+
triggers: {
|
|
222
|
+
agents: ['fulfillment-agent']
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const resources: DeploymentSpec = {
|
|
228
|
+
relationships
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const edges = buildEdges(resources)
|
|
232
|
+
|
|
233
|
+
expect(edges).toHaveLength(1)
|
|
234
|
+
expect(edges[0]).toEqual({
|
|
235
|
+
id: 'edge-0',
|
|
236
|
+
source: 'order-processor',
|
|
237
|
+
target: 'fulfillment-agent',
|
|
238
|
+
relationship: 'triggers'
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('creates "triggers" edge for resource -> workflow relationship', () => {
|
|
243
|
+
const relationships: ResourceRelationships = {
|
|
244
|
+
'triage-agent': {
|
|
245
|
+
triggers: {
|
|
246
|
+
workflows: ['escalation-workflow']
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const resources: DeploymentSpec = {
|
|
252
|
+
relationships
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const edges = buildEdges(resources)
|
|
256
|
+
|
|
257
|
+
expect(edges).toHaveLength(1)
|
|
258
|
+
expect(edges[0]).toEqual({
|
|
259
|
+
id: 'edge-0',
|
|
260
|
+
source: 'triage-agent',
|
|
261
|
+
target: 'escalation-workflow',
|
|
262
|
+
relationship: 'triggers'
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('creates "uses" edge for resource -> integration relationship', () => {
|
|
267
|
+
const relationships: ResourceRelationships = {
|
|
268
|
+
'data-sync-workflow': {
|
|
269
|
+
uses: {
|
|
270
|
+
integrations: ['integration-postgres', 'integration-shopify']
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const resources: DeploymentSpec = {
|
|
276
|
+
relationships
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const edges = buildEdges(resources)
|
|
280
|
+
|
|
281
|
+
expect(edges).toHaveLength(2)
|
|
282
|
+
expect(edges).toEqual([
|
|
283
|
+
{
|
|
284
|
+
id: 'edge-0',
|
|
285
|
+
source: 'data-sync-workflow',
|
|
286
|
+
target: 'integration-postgres',
|
|
287
|
+
relationship: 'uses'
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'edge-1',
|
|
291
|
+
source: 'data-sync-workflow',
|
|
292
|
+
target: 'integration-shopify',
|
|
293
|
+
relationship: 'uses'
|
|
294
|
+
}
|
|
295
|
+
])
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('handles relationships with all relationship types', () => {
|
|
299
|
+
const relationships: ResourceRelationships = {
|
|
300
|
+
'complex-agent': {
|
|
301
|
+
triggers: {
|
|
302
|
+
agents: ['agent-1', 'agent-2'],
|
|
303
|
+
workflows: ['workflow-1']
|
|
304
|
+
},
|
|
305
|
+
uses: {
|
|
306
|
+
integrations: ['integration-1', 'integration-2']
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const resources: DeploymentSpec = {
|
|
312
|
+
relationships
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const edges = buildEdges(resources)
|
|
316
|
+
|
|
317
|
+
expect(edges).toHaveLength(5)
|
|
318
|
+
expect(edges).toEqual([
|
|
319
|
+
{
|
|
320
|
+
id: 'edge-0',
|
|
321
|
+
source: 'complex-agent',
|
|
322
|
+
target: 'agent-1',
|
|
323
|
+
relationship: 'triggers'
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: 'edge-1',
|
|
327
|
+
source: 'complex-agent',
|
|
328
|
+
target: 'agent-2',
|
|
329
|
+
relationship: 'triggers'
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: 'edge-2',
|
|
333
|
+
source: 'complex-agent',
|
|
334
|
+
target: 'workflow-1',
|
|
335
|
+
relationship: 'triggers'
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 'edge-3',
|
|
339
|
+
source: 'complex-agent',
|
|
340
|
+
target: 'integration-1',
|
|
341
|
+
relationship: 'uses'
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
id: 'edge-4',
|
|
345
|
+
source: 'complex-agent',
|
|
346
|
+
target: 'integration-2',
|
|
347
|
+
relationship: 'uses'
|
|
348
|
+
}
|
|
349
|
+
])
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('handles empty relationships map', () => {
|
|
353
|
+
const relationships: ResourceRelationships = {}
|
|
354
|
+
|
|
355
|
+
const resources: DeploymentSpec = {
|
|
356
|
+
relationships
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const edges = buildEdges(resources)
|
|
360
|
+
|
|
361
|
+
expect(edges).toHaveLength(0)
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('handles undefined relationships', () => {
|
|
365
|
+
const resources: DeploymentSpec = {
|
|
366
|
+
// No relationships field
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const edges = buildEdges(resources)
|
|
370
|
+
|
|
371
|
+
expect(edges).toHaveLength(0)
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
describe('External Resource Edges', () => {
|
|
376
|
+
it('creates "triggers" edge for external -> workflow', () => {
|
|
377
|
+
const external: ExternalResourceDefinition = {
|
|
378
|
+
resourceId: 'external-n8n-sync',
|
|
379
|
+
type: 'external',
|
|
380
|
+
platform: 'n8n',
|
|
381
|
+
name: 'Order Sync',
|
|
382
|
+
description: 'Legacy n8n order sync',
|
|
383
|
+
version: '1.0.0',
|
|
384
|
+
status: 'prod',
|
|
385
|
+
triggers: {
|
|
386
|
+
workflows: ['order-fulfillment']
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const resources: DeploymentSpec = {
|
|
391
|
+
externalResources: [external]
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const edges = buildEdges(resources)
|
|
395
|
+
|
|
396
|
+
expect(edges).toHaveLength(1)
|
|
397
|
+
expect(edges[0]).toEqual({
|
|
398
|
+
id: 'edge-0',
|
|
399
|
+
source: 'external-n8n-sync',
|
|
400
|
+
target: 'order-fulfillment',
|
|
401
|
+
relationship: 'triggers'
|
|
402
|
+
})
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it('creates "triggers" edge for external -> agent', () => {
|
|
406
|
+
const external: ExternalResourceDefinition = {
|
|
407
|
+
resourceId: 'external-zapier-lead',
|
|
408
|
+
type: 'external',
|
|
409
|
+
platform: 'zapier',
|
|
410
|
+
name: 'Lead Capture',
|
|
411
|
+
description: 'Zapier lead capture',
|
|
412
|
+
version: '1.0.0',
|
|
413
|
+
status: 'prod',
|
|
414
|
+
triggers: {
|
|
415
|
+
agents: ['lead-router-agent']
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const resources: DeploymentSpec = {
|
|
420
|
+
externalResources: [external]
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const edges = buildEdges(resources)
|
|
424
|
+
|
|
425
|
+
expect(edges).toHaveLength(1)
|
|
426
|
+
expect(edges[0]).toEqual({
|
|
427
|
+
id: 'edge-0',
|
|
428
|
+
source: 'external-zapier-lead',
|
|
429
|
+
target: 'lead-router-agent',
|
|
430
|
+
relationship: 'triggers'
|
|
431
|
+
})
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('creates "uses" edge for external -> integration', () => {
|
|
435
|
+
const external: ExternalResourceDefinition = {
|
|
436
|
+
resourceId: 'external-make-crm',
|
|
437
|
+
type: 'external',
|
|
438
|
+
platform: 'make',
|
|
439
|
+
name: 'CRM Sync',
|
|
440
|
+
description: 'Make CRM sync',
|
|
441
|
+
version: '1.0.0',
|
|
442
|
+
status: 'prod',
|
|
443
|
+
uses: {
|
|
444
|
+
integrations: ['integration-salesforce']
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const resources: DeploymentSpec = {
|
|
449
|
+
externalResources: [external]
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const edges = buildEdges(resources)
|
|
453
|
+
|
|
454
|
+
expect(edges).toHaveLength(1)
|
|
455
|
+
expect(edges[0]).toEqual({
|
|
456
|
+
id: 'edge-0',
|
|
457
|
+
source: 'external-make-crm',
|
|
458
|
+
target: 'integration-salesforce',
|
|
459
|
+
relationship: 'uses'
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
it('does NOT create edges for triggeredBy (removed field)', () => {
|
|
464
|
+
// This test verifies the field was removed and edges are forward-only
|
|
465
|
+
const external: ExternalResourceDefinition = {
|
|
466
|
+
resourceId: 'external-legacy',
|
|
467
|
+
type: 'external',
|
|
468
|
+
platform: 'n8n',
|
|
469
|
+
name: 'Legacy Automation',
|
|
470
|
+
description: 'Legacy automation',
|
|
471
|
+
version: '1.0.0',
|
|
472
|
+
status: 'dev'
|
|
473
|
+
// No triggeredBy field - it was removed per relationship-consolidation
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const resources: DeploymentSpec = {
|
|
477
|
+
externalResources: [external]
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const edges = buildEdges(resources)
|
|
481
|
+
|
|
482
|
+
// Should produce no edges since external has no forward relationships
|
|
483
|
+
expect(edges).toHaveLength(0)
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
it('handles external resources with no relationships', () => {
|
|
487
|
+
const external: ExternalResourceDefinition = {
|
|
488
|
+
resourceId: 'external-isolated',
|
|
489
|
+
type: 'external',
|
|
490
|
+
platform: 'other',
|
|
491
|
+
name: 'Isolated Automation',
|
|
492
|
+
description: 'No relationships',
|
|
493
|
+
version: '1.0.0',
|
|
494
|
+
status: 'dev'
|
|
495
|
+
// No triggers or uses
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const resources: DeploymentSpec = {
|
|
499
|
+
externalResources: [external]
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const edges = buildEdges(resources)
|
|
503
|
+
|
|
504
|
+
expect(edges).toHaveLength(0)
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
it('handles external resources with all relationship types', () => {
|
|
508
|
+
const external: ExternalResourceDefinition = {
|
|
509
|
+
resourceId: 'external-complex',
|
|
510
|
+
type: 'external',
|
|
511
|
+
platform: 'n8n',
|
|
512
|
+
name: 'Complex Automation',
|
|
513
|
+
description: 'Complex external automation',
|
|
514
|
+
version: '1.0.0',
|
|
515
|
+
status: 'prod',
|
|
516
|
+
triggers: {
|
|
517
|
+
workflows: ['workflow-1'],
|
|
518
|
+
agents: ['agent-1', 'agent-2']
|
|
519
|
+
},
|
|
520
|
+
uses: {
|
|
521
|
+
integrations: ['integration-1']
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const resources: DeploymentSpec = {
|
|
526
|
+
externalResources: [external]
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const edges = buildEdges(resources)
|
|
530
|
+
|
|
531
|
+
expect(edges).toHaveLength(4)
|
|
532
|
+
expect(edges).toEqual([
|
|
533
|
+
{
|
|
534
|
+
id: 'edge-0',
|
|
535
|
+
source: 'external-complex',
|
|
536
|
+
target: 'workflow-1',
|
|
537
|
+
relationship: 'triggers'
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
id: 'edge-1',
|
|
541
|
+
source: 'external-complex',
|
|
542
|
+
target: 'agent-1',
|
|
543
|
+
relationship: 'triggers'
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
id: 'edge-2',
|
|
547
|
+
source: 'external-complex',
|
|
548
|
+
target: 'agent-2',
|
|
549
|
+
relationship: 'triggers'
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
id: 'edge-3',
|
|
553
|
+
source: 'external-complex',
|
|
554
|
+
target: 'integration-1',
|
|
555
|
+
relationship: 'uses'
|
|
556
|
+
}
|
|
557
|
+
])
|
|
558
|
+
})
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
describe('Human Checkpoint Edges', () => {
|
|
562
|
+
it('creates "approval" edge for agent -> humanCheckpoint (requestedBy)', () => {
|
|
563
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
564
|
+
resourceId: 'approval-sales',
|
|
565
|
+
type: 'human',
|
|
566
|
+
name: 'Sales Approval',
|
|
567
|
+
description: 'Sales team approval queue',
|
|
568
|
+
version: '1.0.0',
|
|
569
|
+
status: 'prod',
|
|
570
|
+
requestedBy: {
|
|
571
|
+
agents: ['order-processor-agent']
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const resources: DeploymentSpec = {
|
|
576
|
+
humanCheckpoints: [humanCheckpoint]
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const edges = buildEdges(resources)
|
|
580
|
+
|
|
581
|
+
expect(edges).toHaveLength(1)
|
|
582
|
+
expect(edges[0]).toEqual({
|
|
583
|
+
id: 'edge-0',
|
|
584
|
+
source: 'order-processor-agent',
|
|
585
|
+
target: 'approval-sales',
|
|
586
|
+
relationship: 'approval'
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
it('creates "approval" edge for workflow -> humanCheckpoint (requestedBy)', () => {
|
|
591
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
592
|
+
resourceId: 'approval-refund',
|
|
593
|
+
type: 'human',
|
|
594
|
+
name: 'Refund Approval',
|
|
595
|
+
description: 'Refund approval queue',
|
|
596
|
+
version: '1.0.0',
|
|
597
|
+
status: 'prod',
|
|
598
|
+
requestedBy: {
|
|
599
|
+
workflows: ['refund-workflow']
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const resources: DeploymentSpec = {
|
|
604
|
+
humanCheckpoints: [humanCheckpoint]
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const edges = buildEdges(resources)
|
|
608
|
+
|
|
609
|
+
expect(edges).toHaveLength(1)
|
|
610
|
+
expect(edges[0]).toEqual({
|
|
611
|
+
id: 'edge-0',
|
|
612
|
+
source: 'refund-workflow',
|
|
613
|
+
target: 'approval-refund',
|
|
614
|
+
relationship: 'approval'
|
|
615
|
+
})
|
|
616
|
+
})
|
|
617
|
+
|
|
618
|
+
it('creates "triggers" edge for humanCheckpoint -> agent (routesTo)', () => {
|
|
619
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
620
|
+
resourceId: 'approval-content',
|
|
621
|
+
type: 'human',
|
|
622
|
+
name: 'Content Approval',
|
|
623
|
+
description: 'Content approval queue',
|
|
624
|
+
version: '1.0.0',
|
|
625
|
+
status: 'prod',
|
|
626
|
+
routesTo: {
|
|
627
|
+
agents: ['publisher-agent']
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const resources: DeploymentSpec = {
|
|
632
|
+
humanCheckpoints: [humanCheckpoint]
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const edges = buildEdges(resources)
|
|
636
|
+
|
|
637
|
+
expect(edges).toHaveLength(1)
|
|
638
|
+
expect(edges[0]).toEqual({
|
|
639
|
+
id: 'edge-0',
|
|
640
|
+
source: 'approval-content',
|
|
641
|
+
target: 'publisher-agent',
|
|
642
|
+
relationship: 'triggers'
|
|
643
|
+
})
|
|
644
|
+
})
|
|
645
|
+
|
|
646
|
+
it('creates "triggers" edge for humanCheckpoint -> workflow (routesTo)', () => {
|
|
647
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
648
|
+
resourceId: 'approval-budget',
|
|
649
|
+
type: 'human',
|
|
650
|
+
name: 'Budget Approval',
|
|
651
|
+
description: 'Budget approval queue',
|
|
652
|
+
version: '1.0.0',
|
|
653
|
+
status: 'prod',
|
|
654
|
+
routesTo: {
|
|
655
|
+
workflows: ['procurement-workflow']
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const resources: DeploymentSpec = {
|
|
660
|
+
humanCheckpoints: [humanCheckpoint]
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const edges = buildEdges(resources)
|
|
664
|
+
|
|
665
|
+
expect(edges).toHaveLength(1)
|
|
666
|
+
expect(edges[0]).toEqual({
|
|
667
|
+
id: 'edge-0',
|
|
668
|
+
source: 'approval-budget',
|
|
669
|
+
target: 'procurement-workflow',
|
|
670
|
+
relationship: 'triggers'
|
|
671
|
+
})
|
|
672
|
+
})
|
|
673
|
+
|
|
674
|
+
it('handles human checkpoints with only requestedBy', () => {
|
|
675
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
676
|
+
resourceId: 'approval-review-only',
|
|
677
|
+
type: 'human',
|
|
678
|
+
name: 'Review Only',
|
|
679
|
+
description: 'Review only checkpoint',
|
|
680
|
+
version: '1.0.0',
|
|
681
|
+
status: 'dev',
|
|
682
|
+
requestedBy: {
|
|
683
|
+
agents: ['agent-1', 'agent-2'],
|
|
684
|
+
workflows: ['workflow-1']
|
|
685
|
+
}
|
|
686
|
+
// No routesTo
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const resources: DeploymentSpec = {
|
|
690
|
+
humanCheckpoints: [humanCheckpoint]
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const edges = buildEdges(resources)
|
|
694
|
+
|
|
695
|
+
expect(edges).toHaveLength(3)
|
|
696
|
+
expect(edges).toEqual([
|
|
697
|
+
{
|
|
698
|
+
id: 'edge-0',
|
|
699
|
+
source: 'agent-1',
|
|
700
|
+
target: 'approval-review-only',
|
|
701
|
+
relationship: 'approval'
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
id: 'edge-1',
|
|
705
|
+
source: 'agent-2',
|
|
706
|
+
target: 'approval-review-only',
|
|
707
|
+
relationship: 'approval'
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
id: 'edge-2',
|
|
711
|
+
source: 'workflow-1',
|
|
712
|
+
target: 'approval-review-only',
|
|
713
|
+
relationship: 'approval'
|
|
714
|
+
}
|
|
715
|
+
])
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
it('handles human checkpoints with only routesTo', () => {
|
|
719
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
720
|
+
resourceId: 'approval-dispatch-only',
|
|
721
|
+
type: 'human',
|
|
722
|
+
name: 'Dispatch Only',
|
|
723
|
+
description: 'Dispatch only checkpoint',
|
|
724
|
+
version: '1.0.0',
|
|
725
|
+
status: 'dev',
|
|
726
|
+
// No requestedBy
|
|
727
|
+
routesTo: {
|
|
728
|
+
agents: ['agent-3'],
|
|
729
|
+
workflows: ['workflow-2', 'workflow-3']
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const resources: DeploymentSpec = {
|
|
734
|
+
humanCheckpoints: [humanCheckpoint]
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const edges = buildEdges(resources)
|
|
738
|
+
|
|
739
|
+
expect(edges).toHaveLength(3)
|
|
740
|
+
expect(edges).toEqual([
|
|
741
|
+
{
|
|
742
|
+
id: 'edge-0',
|
|
743
|
+
source: 'approval-dispatch-only',
|
|
744
|
+
target: 'agent-3',
|
|
745
|
+
relationship: 'triggers'
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
id: 'edge-1',
|
|
749
|
+
source: 'approval-dispatch-only',
|
|
750
|
+
target: 'workflow-2',
|
|
751
|
+
relationship: 'triggers'
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
id: 'edge-2',
|
|
755
|
+
source: 'approval-dispatch-only',
|
|
756
|
+
target: 'workflow-3',
|
|
757
|
+
relationship: 'triggers'
|
|
758
|
+
}
|
|
759
|
+
])
|
|
760
|
+
})
|
|
761
|
+
|
|
762
|
+
it('handles human checkpoints with both requestedBy and routesTo', () => {
|
|
763
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
764
|
+
resourceId: 'approval-full',
|
|
765
|
+
type: 'human',
|
|
766
|
+
name: 'Full Approval',
|
|
767
|
+
description: 'Full approval with both directions',
|
|
768
|
+
version: '1.0.0',
|
|
769
|
+
status: 'prod',
|
|
770
|
+
requestedBy: {
|
|
771
|
+
agents: ['triage-agent'],
|
|
772
|
+
workflows: ['intake-workflow']
|
|
773
|
+
},
|
|
774
|
+
routesTo: {
|
|
775
|
+
agents: ['fulfillment-agent'],
|
|
776
|
+
workflows: ['processing-workflow']
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const resources: DeploymentSpec = {
|
|
781
|
+
humanCheckpoints: [humanCheckpoint]
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const edges = buildEdges(resources)
|
|
785
|
+
|
|
786
|
+
expect(edges).toHaveLength(4)
|
|
787
|
+
expect(edges).toEqual([
|
|
788
|
+
{
|
|
789
|
+
id: 'edge-0',
|
|
790
|
+
source: 'triage-agent',
|
|
791
|
+
target: 'approval-full',
|
|
792
|
+
relationship: 'approval'
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
id: 'edge-1',
|
|
796
|
+
source: 'intake-workflow',
|
|
797
|
+
target: 'approval-full',
|
|
798
|
+
relationship: 'approval'
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
id: 'edge-2',
|
|
802
|
+
source: 'approval-full',
|
|
803
|
+
target: 'fulfillment-agent',
|
|
804
|
+
relationship: 'triggers'
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
id: 'edge-3',
|
|
808
|
+
source: 'approval-full',
|
|
809
|
+
target: 'processing-workflow',
|
|
810
|
+
relationship: 'triggers'
|
|
811
|
+
}
|
|
812
|
+
])
|
|
813
|
+
})
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
describe('Edge ID Generation', () => {
|
|
817
|
+
it('generates unique edge IDs across all edge types', () => {
|
|
818
|
+
const trigger: TriggerDefinition = {
|
|
819
|
+
resourceId: 'trigger-1',
|
|
820
|
+
type: 'trigger',
|
|
821
|
+
triggerType: 'webhook',
|
|
822
|
+
name: 'Trigger',
|
|
823
|
+
description: 'Test trigger',
|
|
824
|
+
version: '1.0.0',
|
|
825
|
+
status: 'dev'
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
const relationships: ResourceRelationships = {
|
|
829
|
+
'trigger-1': {
|
|
830
|
+
triggers: { workflows: ['workflow-1'] }
|
|
831
|
+
},
|
|
832
|
+
'agent-1': {
|
|
833
|
+
uses: { integrations: ['integration-1'] }
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const external: ExternalResourceDefinition = {
|
|
838
|
+
resourceId: 'external-1',
|
|
839
|
+
type: 'external',
|
|
840
|
+
platform: 'n8n',
|
|
841
|
+
name: 'External',
|
|
842
|
+
description: 'Test external',
|
|
843
|
+
version: '1.0.0',
|
|
844
|
+
status: 'dev',
|
|
845
|
+
triggers: { agents: ['agent-2'] }
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
849
|
+
resourceId: 'approval-1',
|
|
850
|
+
type: 'human',
|
|
851
|
+
name: 'Approval',
|
|
852
|
+
description: 'Test approval',
|
|
853
|
+
version: '1.0.0',
|
|
854
|
+
status: 'dev',
|
|
855
|
+
requestedBy: { agents: ['agent-3'] }
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const resources: DeploymentSpec = {
|
|
859
|
+
triggers: [trigger],
|
|
860
|
+
relationships,
|
|
861
|
+
externalResources: [external],
|
|
862
|
+
humanCheckpoints: [humanCheckpoint]
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const edges = buildEdges(resources)
|
|
866
|
+
|
|
867
|
+
// Verify all edge IDs are unique and sequential
|
|
868
|
+
const edgeIds = edges.map((e) => e.id)
|
|
869
|
+
expect(edgeIds).toEqual(['edge-0', 'edge-1', 'edge-2', 'edge-3'])
|
|
870
|
+
|
|
871
|
+
// Verify no duplicates
|
|
872
|
+
const uniqueIds = new Set(edgeIds)
|
|
873
|
+
expect(uniqueIds.size).toBe(edgeIds.length)
|
|
874
|
+
})
|
|
875
|
+
|
|
876
|
+
it('maintains sequential edge numbering starting from 0', () => {
|
|
877
|
+
const trigger: TriggerDefinition = {
|
|
878
|
+
resourceId: 'trigger-multi',
|
|
879
|
+
type: 'trigger',
|
|
880
|
+
triggerType: 'webhook',
|
|
881
|
+
name: 'Multi',
|
|
882
|
+
description: 'Multi-target trigger',
|
|
883
|
+
version: '1.0.0',
|
|
884
|
+
status: 'dev'
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const relationships: ResourceRelationships = {
|
|
888
|
+
'trigger-multi': {
|
|
889
|
+
triggers: {
|
|
890
|
+
workflows: ['w1', 'w2'],
|
|
891
|
+
agents: ['a1', 'a2', 'a3']
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const resources: DeploymentSpec = {
|
|
897
|
+
triggers: [trigger],
|
|
898
|
+
relationships
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const edges = buildEdges(resources)
|
|
902
|
+
|
|
903
|
+
expect(edges).toHaveLength(5)
|
|
904
|
+
expect(edges[0].id).toBe('edge-0')
|
|
905
|
+
expect(edges[1].id).toBe('edge-1')
|
|
906
|
+
expect(edges[2].id).toBe('edge-2')
|
|
907
|
+
expect(edges[3].id).toBe('edge-3')
|
|
908
|
+
expect(edges[4].id).toBe('edge-4')
|
|
909
|
+
})
|
|
910
|
+
})
|
|
911
|
+
|
|
912
|
+
describe('Edge Type Consolidation', () => {
|
|
913
|
+
it('only produces edges with types: triggers, uses, approval', () => {
|
|
914
|
+
// Create a comprehensive set of resources to test all edge types
|
|
915
|
+
const trigger: TriggerDefinition = {
|
|
916
|
+
resourceId: 'trigger-1',
|
|
917
|
+
type: 'trigger',
|
|
918
|
+
triggerType: 'webhook',
|
|
919
|
+
name: 'Trigger',
|
|
920
|
+
description: 'Test',
|
|
921
|
+
version: '1.0.0',
|
|
922
|
+
status: 'dev'
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const relationships: ResourceRelationships = {
|
|
926
|
+
'trigger-1': {
|
|
927
|
+
triggers: { workflows: ['workflow-1'] }
|
|
928
|
+
},
|
|
929
|
+
'agent-1': {
|
|
930
|
+
triggers: { agents: ['agent-2'] },
|
|
931
|
+
uses: { integrations: ['integration-1'] }
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const external: ExternalResourceDefinition = {
|
|
936
|
+
resourceId: 'external-1',
|
|
937
|
+
type: 'external',
|
|
938
|
+
platform: 'n8n',
|
|
939
|
+
name: 'External',
|
|
940
|
+
description: 'Test',
|
|
941
|
+
version: '1.0.0',
|
|
942
|
+
status: 'dev',
|
|
943
|
+
triggers: { workflows: ['workflow-2'] },
|
|
944
|
+
uses: { integrations: ['integration-2'] }
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
948
|
+
resourceId: 'approval-1',
|
|
949
|
+
type: 'human',
|
|
950
|
+
name: 'Approval',
|
|
951
|
+
description: 'Test',
|
|
952
|
+
version: '1.0.0',
|
|
953
|
+
status: 'dev',
|
|
954
|
+
requestedBy: { agents: ['agent-3'] },
|
|
955
|
+
routesTo: { workflows: ['workflow-3'] }
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const resources: DeploymentSpec = {
|
|
959
|
+
triggers: [trigger],
|
|
960
|
+
relationships,
|
|
961
|
+
externalResources: [external],
|
|
962
|
+
humanCheckpoints: [humanCheckpoint]
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const edges = buildEdges(resources)
|
|
966
|
+
|
|
967
|
+
// Verify ALL edges have one of the three allowed relationship types
|
|
968
|
+
const relationshipTypes = edges.map((e) => e.relationship)
|
|
969
|
+
const allowedTypes: CommandViewEdge['relationship'][] = ['triggers', 'uses', 'approval']
|
|
970
|
+
|
|
971
|
+
for (const relType of relationshipTypes) {
|
|
972
|
+
expect(allowedTypes).toContain(relType)
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Count each type
|
|
976
|
+
const typeCount = {
|
|
977
|
+
triggers: relationshipTypes.filter((t) => t === 'triggers').length,
|
|
978
|
+
uses: relationshipTypes.filter((t) => t === 'uses').length,
|
|
979
|
+
approval: relationshipTypes.filter((t) => t === 'approval').length
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Verify we have edges of each type (comprehensive test)
|
|
983
|
+
expect(typeCount.triggers).toBeGreaterThan(0)
|
|
984
|
+
expect(typeCount.uses).toBeGreaterThan(0)
|
|
985
|
+
expect(typeCount.approval).toBeGreaterThan(0)
|
|
986
|
+
|
|
987
|
+
// Verify total matches sum (no other edge types)
|
|
988
|
+
expect(edges.length).toBe(typeCount.triggers + typeCount.uses + typeCount.approval)
|
|
989
|
+
})
|
|
990
|
+
|
|
991
|
+
it('does NOT produce edges with type: invokes', () => {
|
|
992
|
+
// This test verifies the old 'invokes' edge type is gone
|
|
993
|
+
const trigger: TriggerDefinition = {
|
|
994
|
+
resourceId: 'trigger-test',
|
|
995
|
+
type: 'trigger',
|
|
996
|
+
triggerType: 'webhook',
|
|
997
|
+
name: 'Test',
|
|
998
|
+
description: 'Test trigger',
|
|
999
|
+
version: '1.0.0',
|
|
1000
|
+
status: 'dev'
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const relationships: ResourceRelationships = {
|
|
1004
|
+
'trigger-test': {
|
|
1005
|
+
triggers: {
|
|
1006
|
+
workflows: ['workflow-1'],
|
|
1007
|
+
agents: ['agent-1']
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const resources: DeploymentSpec = {
|
|
1013
|
+
triggers: [trigger],
|
|
1014
|
+
relationships
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const edges = buildEdges(resources)
|
|
1018
|
+
|
|
1019
|
+
// Verify no edge has relationship 'invokes'
|
|
1020
|
+
const hasInvokesEdge = edges.some((e: CommandViewEdge) => {
|
|
1021
|
+
// Use type assertion to check for the old type
|
|
1022
|
+
return (e.relationship as string) === 'invokes'
|
|
1023
|
+
})
|
|
1024
|
+
|
|
1025
|
+
expect(hasInvokesEdge).toBe(false)
|
|
1026
|
+
|
|
1027
|
+
// All edges should be 'triggers' in this case
|
|
1028
|
+
edges.forEach((edge) => {
|
|
1029
|
+
expect(edge.relationship).toBe('triggers')
|
|
1030
|
+
})
|
|
1031
|
+
})
|
|
1032
|
+
})
|
|
1033
|
+
|
|
1034
|
+
describe('Complex Multi-Resource Scenarios', () => {
|
|
1035
|
+
it('handles organization with all resource types', () => {
|
|
1036
|
+
const trigger: TriggerDefinition = {
|
|
1037
|
+
resourceId: 'trigger-order',
|
|
1038
|
+
type: 'trigger',
|
|
1039
|
+
triggerType: 'webhook',
|
|
1040
|
+
name: 'Order Webhook',
|
|
1041
|
+
description: 'New orders',
|
|
1042
|
+
version: '1.0.0',
|
|
1043
|
+
status: 'prod'
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const relationships: ResourceRelationships = {
|
|
1047
|
+
'trigger-order': {
|
|
1048
|
+
triggers: { workflows: ['order-workflow'] }
|
|
1049
|
+
},
|
|
1050
|
+
'order-workflow': {
|
|
1051
|
+
triggers: { agents: ['fulfillment-agent'] },
|
|
1052
|
+
uses: { integrations: ['integration-shopify'] }
|
|
1053
|
+
},
|
|
1054
|
+
'fulfillment-agent': {
|
|
1055
|
+
uses: { integrations: ['integration-shipstation'] }
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const external: ExternalResourceDefinition = {
|
|
1060
|
+
resourceId: 'external-inventory',
|
|
1061
|
+
type: 'external',
|
|
1062
|
+
platform: 'n8n',
|
|
1063
|
+
name: 'Inventory Sync',
|
|
1064
|
+
description: 'Legacy inventory sync',
|
|
1065
|
+
version: '1.0.0',
|
|
1066
|
+
status: 'prod',
|
|
1067
|
+
uses: { integrations: ['integration-postgres'] }
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
const humanCheckpoint: HumanCheckpointDefinition = {
|
|
1071
|
+
resourceId: 'approval-high-value',
|
|
1072
|
+
type: 'human',
|
|
1073
|
+
name: 'High Value Orders',
|
|
1074
|
+
description: 'Manual approval for high value',
|
|
1075
|
+
version: '1.0.0',
|
|
1076
|
+
status: 'prod',
|
|
1077
|
+
requestedBy: { agents: ['fulfillment-agent'] },
|
|
1078
|
+
routesTo: { workflows: ['special-handling-workflow'] }
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const resources: DeploymentSpec = {
|
|
1082
|
+
triggers: [trigger],
|
|
1083
|
+
relationships,
|
|
1084
|
+
externalResources: [external],
|
|
1085
|
+
humanCheckpoints: [humanCheckpoint]
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const edges = buildEdges(resources)
|
|
1089
|
+
|
|
1090
|
+
// Verify we got edges from all sources
|
|
1091
|
+
const sourceIds = new Set(edges.map((e) => e.source))
|
|
1092
|
+
expect(sourceIds).toContain('trigger-order')
|
|
1093
|
+
expect(sourceIds).toContain('order-workflow')
|
|
1094
|
+
expect(sourceIds).toContain('fulfillment-agent')
|
|
1095
|
+
expect(sourceIds).toContain('external-inventory')
|
|
1096
|
+
expect(sourceIds).toContain('approval-high-value')
|
|
1097
|
+
|
|
1098
|
+
// Verify relationship type distribution
|
|
1099
|
+
const relationshipTypes = edges.map((e) => e.relationship)
|
|
1100
|
+
expect(relationshipTypes).toContain('triggers')
|
|
1101
|
+
expect(relationshipTypes).toContain('uses')
|
|
1102
|
+
expect(relationshipTypes).toContain('approval')
|
|
1103
|
+
})
|
|
1104
|
+
|
|
1105
|
+
it('handles empty organization resources', () => {
|
|
1106
|
+
const resources: DeploymentSpec = {}
|
|
1107
|
+
|
|
1108
|
+
const edges = buildEdges(resources)
|
|
1109
|
+
|
|
1110
|
+
expect(edges).toHaveLength(0)
|
|
1111
|
+
expect(edges).toEqual([])
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
it('handles organization with only empty arrays', () => {
|
|
1115
|
+
const resources: DeploymentSpec = {
|
|
1116
|
+
triggers: [],
|
|
1117
|
+
externalResources: [],
|
|
1118
|
+
humanCheckpoints: [],
|
|
1119
|
+
relationships: {}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const edges = buildEdges(resources)
|
|
1123
|
+
|
|
1124
|
+
expect(edges).toHaveLength(0)
|
|
1125
|
+
})
|
|
1126
|
+
})
|
|
1127
|
+
})
|