@segmentstream/ontology 0.0.0-stage → 0.92.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/LICENSE +201 -0
- package/NOTICE +2 -0
- package/README.md +1655 -2
- package/dist/action.d.ts +193 -0
- package/dist/action.js +41 -0
- package/dist/agent-chat.d.ts +44 -0
- package/dist/agent-chat.js +91 -0
- package/dist/agent-task-contract.d.ts +64 -0
- package/dist/agent-task-contract.js +15 -0
- package/dist/agent-task.d.ts +39 -0
- package/dist/agent-task.js +112 -0
- package/dist/analyze.d.ts +215 -0
- package/dist/analyze.js +2438 -0
- package/dist/app-feedback.d.ts +167 -0
- package/dist/app-feedback.js +176 -0
- package/dist/authoring-input.d.ts +3 -0
- package/dist/authoring-input.js +107 -0
- package/dist/authoring-names.d.ts +4 -0
- package/dist/authoring-names.js +13 -0
- package/dist/authoring-records.d.ts +35 -0
- package/dist/authoring-records.js +264 -0
- package/dist/authoring.d.ts +15 -0
- package/dist/authoring.js +331 -0
- package/dist/bin/check.d.ts +30 -0
- package/dist/bin/check.js +99 -0
- package/dist/bin/load.d.ts +48 -0
- package/dist/bin/load.js +139 -0
- package/dist/bin/main.d.ts +31 -0
- package/dist/bin/main.js +121 -0
- package/dist/bin/migrate.d.ts +57 -0
- package/dist/bin/migrate.js +170 -0
- package/dist/bin/output.d.ts +31 -0
- package/dist/bin/output.js +84 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +31 -0
- package/dist/blob.d.ts +52 -0
- package/dist/blob.js +87 -0
- package/dist/channel-session.d.ts +16 -0
- package/dist/channel-session.js +16 -0
- package/dist/chat.d.ts +43 -0
- package/dist/chat.js +34 -0
- package/dist/confirmation.d.ts +46 -0
- package/dist/confirmation.js +41 -0
- package/dist/connection.d.ts +213 -0
- package/dist/connection.js +289 -0
- package/dist/deployment.d.ts +35 -0
- package/dist/deployment.js +76 -0
- package/dist/diagnostics.d.ts +27 -0
- package/dist/diagnostics.js +106 -0
- package/dist/emails.d.ts +70 -0
- package/dist/emails.js +221 -0
- package/dist/entity.d.ts +331 -0
- package/dist/entity.js +153 -0
- package/dist/errors.d.ts +154 -0
- package/dist/errors.js +109 -0
- package/dist/feedback-config.d.ts +24 -0
- package/dist/feedback-config.js +126 -0
- package/dist/files.d.ts +39 -0
- package/dist/files.js +4 -0
- package/dist/groups.d.ts +95 -0
- package/dist/groups.js +280 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +70 -0
- package/dist/internal/diagnostics.js +594 -0
- package/dist/legacy.d.ts +160 -0
- package/dist/legacy.js +270 -0
- package/dist/manifest-compatibility.d.ts +8 -0
- package/dist/manifest-compatibility.js +87 -0
- package/dist/manifest.d.ts +3329 -0
- package/dist/manifest.js +757 -0
- package/dist/me.d.ts +46 -0
- package/dist/me.js +109 -0
- package/dist/metric-filters.d.ts +48 -0
- package/dist/metric-filters.js +252 -0
- package/dist/metric-routes.d.ts +132 -0
- package/dist/metric-routes.js +498 -0
- package/dist/metric.d.ts +172 -0
- package/dist/metric.js +132 -0
- package/dist/migrate/apply.d.ts +94 -0
- package/dist/migrate/apply.js +526 -0
- package/dist/migrate/errors.d.ts +46 -0
- package/dist/migrate/errors.js +38 -0
- package/dist/migrate/index.d.ts +10 -0
- package/dist/migrate/index.js +7 -0
- package/dist/migrate/model.d.ts +2311 -0
- package/dist/migrate/model.js +148 -0
- package/dist/migrate/plan.d.ts +51 -0
- package/dist/migrate/plan.js +1856 -0
- package/dist/migrate/preview.d.ts +13 -0
- package/dist/migrate/preview.js +65 -0
- package/dist/migrate/storage.d.ts +226 -0
- package/dist/migrate/storage.js +774 -0
- package/dist/model-metrics.d.ts +22 -0
- package/dist/model-metrics.js +545 -0
- package/dist/names.d.ts +49 -0
- package/dist/names.js +110 -0
- package/dist/navigation-artifact.d.ts +13 -0
- package/dist/navigation-artifact.js +11 -0
- package/dist/navigation-catalog.d.ts +55 -0
- package/dist/navigation-catalog.js +297 -0
- package/dist/nitro-navigation.d.ts +3 -0
- package/dist/nitro-navigation.js +64 -0
- package/dist/nitro-workflows.d.ts +108 -0
- package/dist/nitro-workflows.js +286 -0
- package/dist/nitro.d.ts +40 -0
- package/dist/nitro.js +50 -0
- package/dist/ontology.d.ts +41 -0
- package/dist/ontology.js +46 -0
- package/dist/permissions.d.ts +198 -0
- package/dist/permissions.js +272 -0
- package/dist/property.d.ts +188 -0
- package/dist/property.js +301 -0
- package/dist/query.d.ts +245 -0
- package/dist/query.js +615 -0
- package/dist/relationship.d.ts +99 -0
- package/dist/relationship.js +33 -0
- package/dist/render.d.ts +81 -0
- package/dist/render.js +512 -0
- package/dist/replication/baseline.d.ts +51 -0
- package/dist/replication/baseline.js +215 -0
- package/dist/replication/bigquery.d.ts +37 -0
- package/dist/replication/bigquery.js +211 -0
- package/dist/replication/coverage.d.ts +14 -0
- package/dist/replication/coverage.js +53 -0
- package/dist/replication/evolution.d.ts +25 -0
- package/dist/replication/evolution.js +184 -0
- package/dist/replication/identities.d.ts +6 -0
- package/dist/replication/identities.js +47 -0
- package/dist/replication/legacy-baseline.d.ts +27 -0
- package/dist/replication/legacy-baseline.js +109 -0
- package/dist/replication/legacy-store.d.ts +19 -0
- package/dist/replication/legacy-store.js +63 -0
- package/dist/replication/lineage.d.ts +36 -0
- package/dist/replication/lineage.js +72 -0
- package/dist/replication/migrate.d.ts +21 -0
- package/dist/replication/migrate.js +84 -0
- package/dist/replication/model.d.ts +52 -0
- package/dist/replication/model.js +99 -0
- package/dist/replication/scans.d.ts +34 -0
- package/dist/replication/scans.js +158 -0
- package/dist/replication/schema.d.ts +11 -0
- package/dist/replication/schema.js +226 -0
- package/dist/replication/shadow-schema.d.ts +4 -0
- package/dist/replication/shadow-schema.js +61 -0
- package/dist/replication/shadow.d.ts +127 -0
- package/dist/replication/shadow.js +614 -0
- package/dist/replication/source-replacement.d.ts +33 -0
- package/dist/replication/source-replacement.js +76 -0
- package/dist/replication/store.d.ts +36 -0
- package/dist/replication/store.js +382 -0
- package/dist/replication/transaction.d.ts +23 -0
- package/dist/replication/transaction.js +88 -0
- package/dist/role.d.ts +61 -0
- package/dist/role.js +76 -0
- package/dist/runtime/action-deadlocks.d.ts +21 -0
- package/dist/runtime/action-deadlocks.js +130 -0
- package/dist/runtime/action-transactions.d.ts +35 -0
- package/dist/runtime/action-transactions.js +54 -0
- package/dist/runtime/agent-counters.d.ts +26 -0
- package/dist/runtime/agent-counters.js +98 -0
- package/dist/runtime/agent-tasks.d.ts +63 -0
- package/dist/runtime/agent-tasks.js +120 -0
- package/dist/runtime/agents.d.ts +15 -0
- package/dist/runtime/agents.js +457 -0
- package/dist/runtime/batch-diagnostics.d.ts +22 -0
- package/dist/runtime/batch-diagnostics.js +89 -0
- package/dist/runtime/batch.d.ts +52 -0
- package/dist/runtime/batch.js +534 -0
- package/dist/runtime/channel-session-revocation.d.ts +3 -0
- package/dist/runtime/channel-session-revocation.js +5 -0
- package/dist/runtime/channel-sessions.d.ts +4 -0
- package/dist/runtime/channel-sessions.js +61 -0
- package/dist/runtime/confirmations.d.ts +29 -0
- package/dist/runtime/confirmations.js +57 -0
- package/dist/runtime/cursor.d.ts +23 -0
- package/dist/runtime/cursor.js +100 -0
- package/dist/runtime/deployment.d.ts +7 -0
- package/dist/runtime/deployment.js +4 -0
- package/dist/runtime/errors.d.ts +118 -0
- package/dist/runtime/errors.js +109 -0
- package/dist/runtime/federation-tables.d.ts +2 -0
- package/dist/runtime/federation-tables.js +20 -0
- package/dist/runtime/federation.d.ts +42 -0
- package/dist/runtime/federation.js +222 -0
- package/dist/runtime/files.d.ts +39 -0
- package/dist/runtime/files.js +44 -0
- package/dist/runtime/generated-actions.d.ts +37 -0
- package/dist/runtime/generated-actions.js +307 -0
- package/dist/runtime/history.d.ts +91 -0
- package/dist/runtime/history.js +77 -0
- package/dist/runtime/ids.d.ts +6 -0
- package/dist/runtime/ids.js +39 -0
- package/dist/runtime/member-tables.d.ts +8 -0
- package/dist/runtime/member-tables.js +33 -0
- package/dist/runtime/member-writes.d.ts +78 -0
- package/dist/runtime/member-writes.js +202 -0
- package/dist/runtime/metric-query.d.ts +19 -0
- package/dist/runtime/metric-query.js +411 -0
- package/dist/runtime/model.d.ts +67 -0
- package/dist/runtime/model.js +150 -0
- package/dist/runtime/new-user-hook.d.ts +6 -0
- package/dist/runtime/new-user-hook.js +37 -0
- package/dist/runtime/oauth.d.ts +9 -0
- package/dist/runtime/oauth.js +299 -0
- package/dist/runtime/pipeline.d.ts +155 -0
- package/dist/runtime/pipeline.js +597 -0
- package/dist/runtime/postgres-tls.d.ts +10 -0
- package/dist/runtime/postgres-tls.js +14 -0
- package/dist/runtime/principals.d.ts +302 -0
- package/dist/runtime/principals.js +1307 -0
- package/dist/runtime/reads.d.ts +55 -0
- package/dist/runtime/reads.js +313 -0
- package/dist/runtime/records.d.ts +42 -0
- package/dist/runtime/records.js +245 -0
- package/dist/runtime/resolve-agent-identity.d.ts +6 -0
- package/dist/runtime/resolve-agent-identity.js +181 -0
- package/dist/runtime/service-binding.d.ts +18 -0
- package/dist/runtime/service-binding.js +266 -0
- package/dist/runtime/sign-in-links.d.ts +68 -0
- package/dist/runtime/sign-in-links.js +147 -0
- package/dist/runtime/sql-runtime.d.ts +131 -0
- package/dist/runtime/sql-runtime.js +362 -0
- package/dist/runtime/statement.d.ts +18 -0
- package/dist/runtime/statement.js +31 -0
- package/dist/runtime/user-identities.d.ts +66 -0
- package/dist/runtime/user-identities.js +106 -0
- package/dist/runtime/user-identity-sql.d.ts +86 -0
- package/dist/runtime/user-identity-sql.js +297 -0
- package/dist/runtime/user-roles.d.ts +16 -0
- package/dist/runtime/user-roles.js +32 -0
- package/dist/runtime/validation.d.ts +16 -0
- package/dist/runtime/validation.js +26 -0
- package/dist/runtime/where.d.ts +57 -0
- package/dist/runtime/where.js +404 -0
- package/dist/runtime/workflow-admission.d.ts +49 -0
- package/dist/runtime/workflow-admission.js +92 -0
- package/dist/runtime/workflow-argument-codec.d.ts +10 -0
- package/dist/runtime/workflow-argument-codec.js +120 -0
- package/dist/runtime/workflow-arguments.d.ts +17 -0
- package/dist/runtime/workflow-arguments.js +99 -0
- package/dist/runtime/workflow-starts.d.ts +203 -0
- package/dist/runtime/workflow-starts.js +493 -0
- package/dist/runtime/workflow-users.d.ts +9 -0
- package/dist/runtime/workflow-users.js +17 -0
- package/dist/runtime/workload.d.ts +25 -0
- package/dist/runtime/workload.js +24 -0
- package/dist/runtime/writes.d.ts +108 -0
- package/dist/runtime/writes.js +491 -0
- package/dist/runtime-source.d.ts +22 -0
- package/dist/runtime-source.js +28 -0
- package/dist/runtime.d.ts +310 -0
- package/dist/runtime.js +31 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.js +107 -0
- package/dist/service/agent/attachments.d.ts +33 -0
- package/dist/service/agent/attachments.js +228 -0
- package/dist/service/agent/authentication.d.ts +22 -0
- package/dist/service/agent/authentication.js +58 -0
- package/dist/service/agent/confirmations.d.ts +5 -0
- package/dist/service/agent/confirmations.js +73 -0
- package/dist/service/agent/conversation-routes.d.ts +14 -0
- package/dist/service/agent/conversation-routes.js +45 -0
- package/dist/service/agent/conversations.d.ts +40 -0
- package/dist/service/agent/conversations.js +69 -0
- package/dist/service/agent/files.d.ts +12 -0
- package/dist/service/agent/files.js +69 -0
- package/dist/service/agent/protocol.d.ts +90 -0
- package/dist/service/agent/protocol.js +114 -0
- package/dist/service/agent/server.d.ts +28 -0
- package/dist/service/agent/server.js +374 -0
- package/dist/service/agent/task-caller.d.ts +21 -0
- package/dist/service/agent/task-caller.js +35 -0
- package/dist/service/agent/task-start.d.ts +53 -0
- package/dist/service/agent/task-start.js +161 -0
- package/dist/service/agent/tasks.d.ts +40 -0
- package/dist/service/agent/tasks.js +155 -0
- package/dist/service/api.d.ts +50 -0
- package/dist/service/api.js +288 -0
- package/dist/service/authentication.d.ts +227 -0
- package/dist/service/authentication.js +767 -0
- package/dist/service/caller-context.d.ts +24 -0
- package/dist/service/caller-context.js +6 -0
- package/dist/service/catalog.d.ts +42 -0
- package/dist/service/catalog.js +66 -0
- package/dist/service/channel/provider.d.ts +10 -0
- package/dist/service/channel/provider.js +1 -0
- package/dist/service/channel/providers/telegram.d.ts +18 -0
- package/dist/service/channel/providers/telegram.js +108 -0
- package/dist/service/channel/sessions.d.ts +5 -0
- package/dist/service/channel/sessions.js +53 -0
- package/dist/service/channel/sign-in.d.ts +7 -0
- package/dist/service/channel/sign-in.js +78 -0
- package/dist/service/chat/archive.d.ts +64 -0
- package/dist/service/chat/archive.js +62 -0
- package/dist/service/chat/attribution.d.ts +26 -0
- package/dist/service/chat/attribution.js +28 -0
- package/dist/service/chat/delivery.d.ts +18 -0
- package/dist/service/chat/delivery.js +251 -0
- package/dist/service/chat/diagnostics.d.ts +2 -0
- package/dist/service/chat/diagnostics.js +35 -0
- package/dist/service/chat/envelope.d.ts +96 -0
- package/dist/service/chat/envelope.js +39 -0
- package/dist/service/chat/idempotency.d.ts +19 -0
- package/dist/service/chat/idempotency.js +37 -0
- package/dist/service/chat/providers/telegram.d.ts +36 -0
- package/dist/service/chat/providers/telegram.js +133 -0
- package/dist/service/chat/reads.d.ts +25 -0
- package/dist/service/chat/reads.js +75 -0
- package/dist/service/chat/resolve.d.ts +34 -0
- package/dist/service/chat/resolve.js +150 -0
- package/dist/service/chat/routes.d.ts +23 -0
- package/dist/service/chat/routes.js +75 -0
- package/dist/service/chat/schemas.d.ts +43 -0
- package/dist/service/chat/schemas.js +31 -0
- package/dist/service/config.d.ts +218 -0
- package/dist/service/config.js +265 -0
- package/dist/service/connection-stream.d.ts +10 -0
- package/dist/service/connection-stream.js +191 -0
- package/dist/service/connections.d.ts +60 -0
- package/dist/service/connections.js +271 -0
- package/dist/service/decisions.d.ts +35 -0
- package/dist/service/decisions.js +63 -0
- package/dist/service/descriptions.d.ts +8 -0
- package/dist/service/descriptions.js +20 -0
- package/dist/service/discovery-schemas.d.ts +92 -0
- package/dist/service/discovery-schemas.js +59 -0
- package/dist/service/discovery-search.d.ts +30 -0
- package/dist/service/discovery-search.js +95 -0
- package/dist/service/discovery.d.ts +37 -0
- package/dist/service/discovery.js +436 -0
- package/dist/service/docs.d.ts +9 -0
- package/dist/service/docs.js +197 -0
- package/dist/service/errors.d.ts +145 -0
- package/dist/service/errors.js +704 -0
- package/dist/service/federation-provider.d.ts +36 -0
- package/dist/service/federation-provider.js +110 -0
- package/dist/service/federation-server.d.ts +20 -0
- package/dist/service/federation-server.js +249 -0
- package/dist/service/federation-status.d.ts +40 -0
- package/dist/service/federation-status.js +151 -0
- package/dist/service/handlers.d.ts +18 -0
- package/dist/service/handlers.js +287 -0
- package/dist/service/mcp/confirmation.d.ts +68 -0
- package/dist/service/mcp/confirmation.js +178 -0
- package/dist/service/mcp/find-input.d.ts +12 -0
- package/dist/service/mcp/find-input.js +122 -0
- package/dist/service/mcp/input-schema.d.ts +6 -0
- package/dist/service/mcp/input-schema.js +126 -0
- package/dist/service/mcp/listing.d.ts +26 -0
- package/dist/service/mcp/listing.js +92 -0
- package/dist/service/mcp/metric-tools.d.ts +85 -0
- package/dist/service/mcp/metric-tools.js +258 -0
- package/dist/service/mcp/request-log.d.ts +5 -0
- package/dist/service/mcp/request-log.js +53 -0
- package/dist/service/mcp/runtime-policy.d.ts +27 -0
- package/dist/service/mcp/runtime-policy.js +62 -0
- package/dist/service/mcp/server.d.ts +52 -0
- package/dist/service/mcp/server.js +861 -0
- package/dist/service/mcp/tools.d.ts +99 -0
- package/dist/service/mcp/tools.js +938 -0
- package/dist/service/member-file-routes.d.ts +22 -0
- package/dist/service/member-file-routes.js +52 -0
- package/dist/service/member-files.d.ts +245 -0
- package/dist/service/member-files.js +258 -0
- package/dist/service/metric-query-platform.d.ts +77 -0
- package/dist/service/metric-query-platform.js +198 -0
- package/dist/service/metric-query-scopes.d.ts +43 -0
- package/dist/service/metric-query-scopes.js +147 -0
- package/dist/service/metric-query.d.ts +48 -0
- package/dist/service/metric-query.js +200 -0
- package/dist/service/name-suggestions.d.ts +3 -0
- package/dist/service/name-suggestions.js +38 -0
- package/dist/service/navigation-schemas.d.ts +80 -0
- package/dist/service/navigation-schemas.js +49 -0
- package/dist/service/navigation.d.ts +20 -0
- package/dist/service/navigation.js +165 -0
- package/dist/service/oauth/protocol.d.ts +70 -0
- package/dist/service/oauth/protocol.js +157 -0
- package/dist/service/oauth/refresh-replay.d.ts +2 -0
- package/dist/service/oauth/refresh-replay.js +20 -0
- package/dist/service/oauth/server.d.ts +28 -0
- package/dist/service/oauth/server.js +769 -0
- package/dist/service/operations.d.ts +170 -0
- package/dist/service/operations.js +392 -0
- package/dist/service/plan.d.ts +57 -0
- package/dist/service/plan.js +96 -0
- package/dist/service/rate-limit.d.ts +22 -0
- package/dist/service/rate-limit.js +52 -0
- package/dist/service/record-reads.d.ts +21 -0
- package/dist/service/record-reads.js +155 -0
- package/dist/service/replication.d.ts +5 -0
- package/dist/service/replication.js +39 -0
- package/dist/service/runtime-contract.d.ts +508 -0
- package/dist/service/runtime-contract.js +18 -0
- package/dist/service/runtime-identity.d.ts +71 -0
- package/dist/service/runtime-identity.js +122 -0
- package/dist/service/schemas.d.ts +961 -0
- package/dist/service/schemas.js +639 -0
- package/dist/service/service.d.ts +21 -0
- package/dist/service/service.js +526 -0
- package/dist/service/warehouse-platform.d.ts +17 -0
- package/dist/service/warehouse-platform.js +122 -0
- package/dist/service/warehouse-query.d.ts +24 -0
- package/dist/service/warehouse-query.js +280 -0
- package/dist/service/webhooks.d.ts +106 -0
- package/dist/service/webhooks.js +233 -0
- package/dist/service/workflow-admission-provider.d.ts +31 -0
- package/dist/service/workflow-admission-provider.js +205 -0
- package/dist/service/workflow-argument-serialization.d.ts +2 -0
- package/dist/service/workflow-argument-serialization.js +14 -0
- package/dist/service/workflow-callback.d.ts +22 -0
- package/dist/service/workflow-callback.js +75 -0
- package/dist/service/workflow-deployment.d.ts +8 -0
- package/dist/service/workflow-deployment.js +131 -0
- package/dist/service/workflow-diagnostics.d.ts +23 -0
- package/dist/service/workflow-diagnostics.js +263 -0
- package/dist/service/workflow-errors.d.ts +26 -0
- package/dist/service/workflow-errors.js +181 -0
- package/dist/service/workflow-logging.d.ts +2 -0
- package/dist/service/workflow-logging.js +50 -0
- package/dist/service/workflow-queue-fence.d.ts +6 -0
- package/dist/service/workflow-queue-fence.js +66 -0
- package/dist/service/workflows.d.ts +200 -0
- package/dist/service/workflows.js +1549 -0
- package/dist/sign-in.d.ts +58 -0
- package/dist/sign-in.js +57 -0
- package/dist/source.d.ts +30 -0
- package/dist/source.js +107 -0
- package/dist/start-recovery-contract.d.ts +49 -0
- package/dist/start-recovery-contract.js +26 -0
- package/dist/start-recovery.d.ts +27 -0
- package/dist/start-recovery.js +81 -0
- package/dist/storage/api.d.ts +4 -0
- package/dist/storage/api.js +40 -0
- package/dist/storage/attachments.d.ts +146 -0
- package/dist/storage/attachments.js +733 -0
- package/dist/storage/authorization.d.ts +38 -0
- package/dist/storage/authorization.js +83 -0
- package/dist/storage/file-access.d.ts +17 -0
- package/dist/storage/file-access.js +8 -0
- package/dist/storage/limits.d.ts +12 -0
- package/dist/storage/limits.js +17 -0
- package/dist/storage/maintenance-auth.d.ts +20 -0
- package/dist/storage/maintenance-auth.js +24 -0
- package/dist/storage/maintenance.d.ts +11 -0
- package/dist/storage/maintenance.js +67 -0
- package/dist/storage/neon.d.ts +4 -0
- package/dist/storage/neon.js +318 -0
- package/dist/storage/operator.d.ts +11 -0
- package/dist/storage/operator.js +30 -0
- package/dist/storage/provider.d.ts +93 -0
- package/dist/storage/provider.js +36 -0
- package/dist/storage/sessions.d.ts +184 -0
- package/dist/storage/sessions.js +688 -0
- package/dist/storage/tables.d.ts +54 -0
- package/dist/storage/tables.js +205 -0
- package/dist/storage/transfers.d.ts +16 -0
- package/dist/storage/transfers.js +153 -0
- package/dist/storage/writes.d.ts +7 -0
- package/dist/storage/writes.js +88 -0
- package/dist/telegram.d.ts +35 -0
- package/dist/telegram.js +173 -0
- package/dist/user-identity.d.ts +28 -0
- package/dist/user-identity.js +106 -0
- package/dist/user.d.ts +75 -0
- package/dist/user.js +111 -0
- package/dist/warehouse-backed.d.ts +6 -0
- package/dist/warehouse-backed.js +495 -0
- package/dist/webhook-token.d.ts +11 -0
- package/dist/webhook-token.js +18 -0
- package/dist/webhooks.d.ts +40 -0
- package/dist/webhooks.js +55 -0
- package/dist/wire.d.ts +10 -0
- package/dist/wire.js +35 -0
- package/dist/workflow-runtime.d.ts +116 -0
- package/dist/workflow-runtime.js +109 -0
- package/dist/workflow-topics.d.ts +28 -0
- package/dist/workflow-topics.js +28 -0
- package/dist/workflow.d.ts +284 -0
- package/dist/workflow.js +259 -0
- package/dist/workflows.d.ts +24 -0
- package/dist/workflows.js +18 -0
- package/package.json +127 -4
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { Context, Duration, Effect, Option, Schema } from "effect";
|
|
2
|
+
import { NO_INPUT } from "../analyze.js";
|
|
3
|
+
import { actionInputToWire } from "../authoring-input.js";
|
|
4
|
+
import { nameToWire } from "../authoring-names.js";
|
|
5
|
+
import { editsFromWire, generatedResultFromWire, recordFromWire } from "../authoring-records.js";
|
|
6
|
+
import { principalFromWire } from "../authoring.js";
|
|
7
|
+
import { isActionTrigger } from "../manifest.js";
|
|
8
|
+
import { referenceTarget } from "../property.js";
|
|
9
|
+
import { NIL_RECORD_ID, WORKFLOW_STARTS_TABLE } from "../storage/tables.js";
|
|
10
|
+
import { isWorkflowTrigger, } from "../workflow.js";
|
|
11
|
+
import { TriggerInputInvalid } from "./errors.js";
|
|
12
|
+
import { canonicalInstant } from "./records.js";
|
|
13
|
+
import { validate } from "./validation.js";
|
|
14
|
+
import { decodeWorkflowArguments, encodeWorkflowArguments } from "./workflow-arguments.js";
|
|
15
|
+
/** Starts that fail this many times are left for an operator: the automatic sweep skips them. */
|
|
16
|
+
export const MAX_START_ATTEMPTS = 10;
|
|
17
|
+
/** How long a pending row waits before a sweep takes it: its own request starts it first. */
|
|
18
|
+
export const SWEEP_DELAY_SECONDS = 30;
|
|
19
|
+
/** The most rows one sweep starts. */
|
|
20
|
+
export const SWEEP_BATCH = 100;
|
|
21
|
+
/** How long started rows are kept, for the starts listing, before the sweep deletes them. */
|
|
22
|
+
export const STARTED_RETENTION_DAYS = 30;
|
|
23
|
+
/** How many starts run at once, after an action's commit and in a sweep. */
|
|
24
|
+
const START_CONCURRENCY = 10;
|
|
25
|
+
/** How long one start may take before it counts as failed and its row stays pending. */
|
|
26
|
+
const START_TIMEOUT = Duration.seconds(15);
|
|
27
|
+
/** The Workflow SDK refused or did not answer a start; the row stays pending. */
|
|
28
|
+
export class WorkflowStartFailed extends Schema.TaggedError()("WorkflowStartFailed", {
|
|
29
|
+
reason: Schema.Literals([
|
|
30
|
+
"unknown_workflow",
|
|
31
|
+
"not_compiled",
|
|
32
|
+
"no_recovery_workflow",
|
|
33
|
+
"invalid_input",
|
|
34
|
+
"engine",
|
|
35
|
+
"timeout",
|
|
36
|
+
]),
|
|
37
|
+
message: Schema.String,
|
|
38
|
+
cause: Schema.optionalKey(Schema.Unknown),
|
|
39
|
+
}) {
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Starts the run of a pending row through the Workflow SDK, on the ontology's queue, with
|
|
43
|
+
* `startedBy` naming the trigger, the action and the record, and arms the recovery run that starts
|
|
44
|
+
* what a request leaves pending. The ontology service provides it; a runtime without one leaves its
|
|
45
|
+
* rows pending for an operator's sweep.
|
|
46
|
+
*/
|
|
47
|
+
export class WorkflowStarter extends Context.Service()("@segmentstream/ontology/WorkflowStarter") {
|
|
48
|
+
}
|
|
49
|
+
/** The `startedBy` of a run an action trigger started. */
|
|
50
|
+
export function triggerContext(start) {
|
|
51
|
+
return {
|
|
52
|
+
startedBy: {
|
|
53
|
+
kind: "trigger",
|
|
54
|
+
triggerKind: "action",
|
|
55
|
+
trigger: start.trigger,
|
|
56
|
+
actionId: start.actionId,
|
|
57
|
+
...(start.recordId === null ? {} : { recordId: start.recordId }),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const resolvedTriggers = new WeakMap();
|
|
62
|
+
/**
|
|
63
|
+
* The action triggers of the workflows that passed `check`, paired with their declarations. Built
|
|
64
|
+
* once per ontology.
|
|
65
|
+
*/
|
|
66
|
+
export function actionTriggers(ontology) {
|
|
67
|
+
const cached = resolvedTriggers.get(ontology);
|
|
68
|
+
if (cached !== undefined) {
|
|
69
|
+
return cached;
|
|
70
|
+
}
|
|
71
|
+
const declarations = ontology.config.workflows ?? [];
|
|
72
|
+
const resolved = ontology.workflows.flatMap((manifest) => {
|
|
73
|
+
const declaration = declarations.find((candidate) => nameToWire(candidate.name) === manifest.name);
|
|
74
|
+
if (declaration === undefined) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const codec = declaration.input === undefined ? NO_INPUT : actionInputToWire(declaration.input);
|
|
78
|
+
return manifest.triggers.filter(isActionTrigger).flatMap((trigger) => {
|
|
79
|
+
const authored = declaration.triggers.find((candidate) => isWorkflowTrigger(candidate) &&
|
|
80
|
+
candidate.kind === "action" &&
|
|
81
|
+
nameToWire(candidate.name) === trigger.name);
|
|
82
|
+
return authored === undefined || authored.kind !== "action"
|
|
83
|
+
? []
|
|
84
|
+
: [
|
|
85
|
+
{
|
|
86
|
+
workflow: manifest.name,
|
|
87
|
+
trigger: `${manifest.name}.${trigger.name}`,
|
|
88
|
+
entity: trigger.entity,
|
|
89
|
+
action: trigger.action,
|
|
90
|
+
when: authored.when,
|
|
91
|
+
input: authored.input,
|
|
92
|
+
codec,
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
resolvedTriggers.set(ontology, resolved);
|
|
98
|
+
return resolved;
|
|
99
|
+
}
|
|
100
|
+
/** Whether the ontology declares any action trigger, so that `_workflow_starts` exists. */
|
|
101
|
+
export function hasActionTriggers(ontology) {
|
|
102
|
+
return actionTriggers(ontology).length > 0;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The triggers that fire for an action of `entity` run by `principal`: on that action or on the
|
|
106
|
+
* entity, except the triggers of the workflow whose user runs it, so that a workflow never starts
|
|
107
|
+
* itself.
|
|
108
|
+
*/
|
|
109
|
+
export function matchingTriggers(ontology, entity, action, principal) {
|
|
110
|
+
return actionTriggers(ontology).filter((trigger) => trigger.entity === entity &&
|
|
111
|
+
(trigger.action === undefined || trigger.action === action) &&
|
|
112
|
+
trigger.workflow !== principal.workflow?.name);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The input field of an action that holds an id of `entity`, when exactly one top-level field is a
|
|
116
|
+
* reference to it; its name as the decoded input carries it.
|
|
117
|
+
*/
|
|
118
|
+
function namingField(action, entity) {
|
|
119
|
+
const fields = action.input.fields;
|
|
120
|
+
const naming = Object.entries(fields ?? {}).filter(([, schema]) => referenceTarget(schema) === entity);
|
|
121
|
+
return naming.length === 1 ? naming[0][0] : undefined;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The records each trigger is evaluated for. The record the input names, once for the action, when
|
|
125
|
+
* one input field holds an id of the entity and the action edited that record; otherwise every
|
|
126
|
+
* edited record of the entity, each on its own. An action that edits none fires nothing.
|
|
127
|
+
*/
|
|
128
|
+
function selectRecords(evaluation) {
|
|
129
|
+
const edited = [
|
|
130
|
+
...new Set(evaluation.edits.filter((edit) => edit.entity === evaluation.entity).map((edit) => edit.id)),
|
|
131
|
+
];
|
|
132
|
+
const field = namingField(evaluation.definition, evaluation.entity);
|
|
133
|
+
const value = field === undefined || typeof evaluation.input !== "object" || evaluation.input === null
|
|
134
|
+
? undefined
|
|
135
|
+
: evaluation.input[field];
|
|
136
|
+
const named = typeof value === "string"
|
|
137
|
+
? edited.find((id) => id.toLowerCase() === value.toLowerCase())
|
|
138
|
+
: undefined;
|
|
139
|
+
return named === undefined ? { named: false, ids: edited } : { named: true, ids: [named] };
|
|
140
|
+
}
|
|
141
|
+
function authoredDefinition(ontology, entity) {
|
|
142
|
+
return ontology.config.entities.find((candidate) => nameToWire(candidate.name) === entity)
|
|
143
|
+
?.definition;
|
|
144
|
+
}
|
|
145
|
+
/** A trigger function threw, or `when` answered something other than a boolean: a defect. */
|
|
146
|
+
export class TriggerFunctionFailed extends Error {
|
|
147
|
+
constructor(trigger, part, cause) {
|
|
148
|
+
super(`The ${part} of action trigger ${trigger} failed.`, { cause });
|
|
149
|
+
this.name = "TriggerFunctionFailed";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function issuesOf(error) {
|
|
153
|
+
return [{ path: [], message: error instanceof Error ? error.message : String(error) }];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Evaluates the matching triggers inside the action's transaction: selects the records, runs
|
|
157
|
+
* `when`, maps and validates the input with the workflow's input schema. A function that throws is
|
|
158
|
+
* a defect, which rolls the action back; an input the workflow does not accept fails the action
|
|
159
|
+
* with {@link TriggerInputInvalid}. The starts it answers are in the wire form the rows store.
|
|
160
|
+
*/
|
|
161
|
+
export function evaluateTriggers(triggers, evaluation) {
|
|
162
|
+
return Effect.gen(function* () {
|
|
163
|
+
if (triggers.length === 0) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const { named, ids } = selectRecords(evaluation);
|
|
167
|
+
if (ids.length === 0) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
const { ontology } = evaluation;
|
|
171
|
+
const definition = authoredDefinition(ontology, evaluation.entity);
|
|
172
|
+
const event = {
|
|
173
|
+
action: `${evaluation.entity}.${evaluation.action}`,
|
|
174
|
+
actionId: evaluation.actionId,
|
|
175
|
+
at: evaluation.at,
|
|
176
|
+
principal: principalFromWire(evaluation.principal),
|
|
177
|
+
input: evaluation.generated ? recordFromWire(evaluation.input, definition) : evaluation.input,
|
|
178
|
+
result: evaluation.generated
|
|
179
|
+
? generatedResultFromWire(evaluation.result, evaluation.action, definition)
|
|
180
|
+
: evaluation.result,
|
|
181
|
+
edits: editsFromWire(evaluation.edits, ontology.config.entities),
|
|
182
|
+
};
|
|
183
|
+
const authored = (record) => record === undefined ? undefined : recordFromWire(record, definition);
|
|
184
|
+
const starts = [];
|
|
185
|
+
for (const id of ids) {
|
|
186
|
+
const held = evaluation.held.get(id);
|
|
187
|
+
const pair = held ??
|
|
188
|
+
(yield* evaluation.load(id).pipe(Effect.map((current) => ({ before: current, after: current })), Effect.orDie));
|
|
189
|
+
const record = { record: id, before: authored(pair.before), after: authored(pair.after) };
|
|
190
|
+
for (const trigger of triggers) {
|
|
191
|
+
if (trigger.when !== undefined) {
|
|
192
|
+
const fires = yield* Effect.try({
|
|
193
|
+
try: () => trigger.when(event, record),
|
|
194
|
+
catch: (cause) => new TriggerFunctionFailed(trigger.trigger, "when", cause),
|
|
195
|
+
}).pipe(Effect.orDie);
|
|
196
|
+
if (typeof fires !== "boolean") {
|
|
197
|
+
return yield* Effect.die(new TriggerFunctionFailed(trigger.trigger, "when", new TypeError("when must return a boolean synchronously.")));
|
|
198
|
+
}
|
|
199
|
+
if (!fires) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const mapped = trigger.input === undefined
|
|
204
|
+
? {}
|
|
205
|
+
: yield* Effect.try({
|
|
206
|
+
try: () => trigger.input(event, record),
|
|
207
|
+
catch: (cause) => new TriggerFunctionFailed(trigger.trigger, "input", cause),
|
|
208
|
+
}).pipe(Effect.orDie);
|
|
209
|
+
const input = yield* encodeInput(trigger, mapped);
|
|
210
|
+
starts.push({
|
|
211
|
+
trigger: trigger.trigger,
|
|
212
|
+
workflow: trigger.workflow,
|
|
213
|
+
actionId: evaluation.actionId,
|
|
214
|
+
recordId: named ? null : id,
|
|
215
|
+
input,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return starts;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/** The mapping's value in the workflow input's wire form, validated as a start would decode it. */
|
|
223
|
+
function encodeInput(trigger, mapped) {
|
|
224
|
+
return Effect.gen(function* () {
|
|
225
|
+
const invalid = (issues) => new TriggerInputInvalid({
|
|
226
|
+
trigger: trigger.trigger,
|
|
227
|
+
message: `The input action trigger ${trigger.trigger} maps is not one its workflow accepts.`,
|
|
228
|
+
issues,
|
|
229
|
+
});
|
|
230
|
+
const encoded = yield* Schema.encodeUnknownEffect(trigger.codec)(mapped, { errors: "all", onExcessProperty: "error" }).pipe(Effect.mapError((error) => invalid(issuesOf(error))));
|
|
231
|
+
const decoded = validate(trigger.codec, encoded);
|
|
232
|
+
if (!decoded.ok) {
|
|
233
|
+
return yield* invalid(decoded.issues);
|
|
234
|
+
}
|
|
235
|
+
return JSON.parse(JSON.stringify(encoded));
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/** Rows per INSERT: five parameters each, well below PostgreSQL's limit. */
|
|
239
|
+
const INSERT_CHUNK = 1_000;
|
|
240
|
+
/** Inserts the starts in the action's transaction, so they commit with its records and history. */
|
|
241
|
+
export function insertStarts(sql, starts) {
|
|
242
|
+
return Effect.gen(function* () {
|
|
243
|
+
const capture = Option.getOrUndefined(yield* Effect.serviceOption(WorkflowStarter))?.capture;
|
|
244
|
+
for (let offset = 0; offset < starts.length; offset += INSERT_CHUNK) {
|
|
245
|
+
const chunk = starts.slice(offset, offset + INSERT_CHUNK);
|
|
246
|
+
const params = [];
|
|
247
|
+
const values = chunk.map((start) => {
|
|
248
|
+
const bound = capture?.(start);
|
|
249
|
+
params.push(start.trigger, start.actionId, start.recordId, start.workflow, JSON.stringify(start.input), process.env.VERCEL_DEPLOYMENT_ID ?? null, bound?.workflowId ?? null, bound ? JSON.stringify(encodeWorkflowArguments(bound.arguments)) : null, bound ? JSON.stringify(bound.attributes) : null);
|
|
250
|
+
const at = params.length - 9;
|
|
251
|
+
return (`($${at + 1}, $${at + 2}::uuid, $${at + 3}::uuid, $${at + 4}, $${at + 5}::jsonb, ` +
|
|
252
|
+
`$${at + 6},$${at + 7},` +
|
|
253
|
+
`$${at + 8}::jsonb,$${at + 9}::jsonb)`);
|
|
254
|
+
});
|
|
255
|
+
yield* sql.unsafe(`INSERT INTO "${WORKFLOW_STARTS_TABLE}" ("trigger", "action_id", "record_id", "workflow", "input", ` +
|
|
256
|
+
`admission_deployment_id,provider_workflow_id,arguments,admission_attributes) ` +
|
|
257
|
+
`VALUES ${values.join(", ")}`, params);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/** Runs the starts, a few at a time; each failure is kept, never raised. */
|
|
262
|
+
function attempt(starter, starts) {
|
|
263
|
+
return Effect.forEach(starts, (start) => starter.start(start).pipe(Effect.timeoutOrElse({
|
|
264
|
+
duration: START_TIMEOUT,
|
|
265
|
+
orElse: () => Effect.fail(new WorkflowStartFailed({
|
|
266
|
+
reason: "timeout",
|
|
267
|
+
message: `The Workflow SDK did not accept the run within ${Duration.format(START_TIMEOUT)}.`,
|
|
268
|
+
})),
|
|
269
|
+
}), Effect.result, Effect.map((result) => ({ start, result }))), { concurrency: START_CONCURRENCY }).pipe(Effect.map((results) => ({
|
|
270
|
+
started: results.flatMap(({ start, result }) => result._tag === "Success" ? [{ start, runId: result.success }] : []),
|
|
271
|
+
failed: results.flatMap(({ start, result }) => result._tag === "Failure" ? [{ start, error: result.failure }] : []),
|
|
272
|
+
})));
|
|
273
|
+
}
|
|
274
|
+
/** `(trigger, action_id, record_id)` of each start as a VALUES list, from `$offset + 1`. */
|
|
275
|
+
function keys(starts, params, extra) {
|
|
276
|
+
return starts
|
|
277
|
+
.map((start) => {
|
|
278
|
+
params.push(start.trigger, start.actionId, start.recordId);
|
|
279
|
+
const columns = [
|
|
280
|
+
`$${params.length - 2}::text`,
|
|
281
|
+
`$${params.length - 1}::uuid`,
|
|
282
|
+
`$${params.length}::uuid`,
|
|
283
|
+
];
|
|
284
|
+
if (extra !== undefined) {
|
|
285
|
+
params.push(extra(start));
|
|
286
|
+
columns.push(`$${params.length}::text`);
|
|
287
|
+
}
|
|
288
|
+
return `(${columns.join(", ")})`;
|
|
289
|
+
})
|
|
290
|
+
.join(", ");
|
|
291
|
+
}
|
|
292
|
+
const SAME_ROW = `s."trigger" = v."trigger" AND s."action_id" = v."action_id" ` +
|
|
293
|
+
`AND COALESCE(s."record_id", '${NIL_RECORD_ID}'::uuid) = COALESCE(v."record_id", '${NIL_RECORD_ID}'::uuid)`;
|
|
294
|
+
/**
|
|
295
|
+
* Records what the starts did: the run id of each start the SDK accepted, and one more attempt on
|
|
296
|
+
* each that failed, logged with the request id. A start that reaches {@link MAX_START_ATTEMPTS} is
|
|
297
|
+
* logged as abandoned.
|
|
298
|
+
*/
|
|
299
|
+
function record(sql, attempted, requestId) {
|
|
300
|
+
return Effect.gen(function* () {
|
|
301
|
+
if (attempted.started.length > 0) {
|
|
302
|
+
const params = [];
|
|
303
|
+
const runIds = new Map(attempted.started.map(({ start, runId }) => [start, runId]));
|
|
304
|
+
const values = keys(attempted.started.map(({ start }) => start), params, (start) => runIds.get(start));
|
|
305
|
+
yield* sql.unsafe(`UPDATE "${WORKFLOW_STARTS_TABLE}" AS s SET "run_id" = v."run_id", ` +
|
|
306
|
+
`"started_at" = now(), submission_lease_until=NULL ` +
|
|
307
|
+
`FROM (VALUES ${values}) AS v ("trigger", "action_id", "record_id", "run_id") ` +
|
|
308
|
+
`WHERE ${SAME_ROW} AND s."run_id" IS NULL`, params);
|
|
309
|
+
}
|
|
310
|
+
if (attempted.failed.length === 0) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const params = [];
|
|
314
|
+
const values = keys(attempted.failed.map(({ start }) => start), params);
|
|
315
|
+
const counted = yield* sql.unsafe(`UPDATE "${WORKFLOW_STARTS_TABLE}" AS s SET "attempts" = s."attempts" + 1, submission_lease_until=NULL ` +
|
|
316
|
+
`FROM (VALUES ${values}) AS v ("trigger", "action_id", "record_id") ` +
|
|
317
|
+
`WHERE ${SAME_ROW} AND s."run_id" IS NULL ` +
|
|
318
|
+
`RETURNING s."trigger", s."action_id"::text AS "action_id", s."record_id"::text AS "record_id", s."attempts"`, params);
|
|
319
|
+
const attemptsOf = new Map(counted.map((row) => [
|
|
320
|
+
`${row.trigger}|${row.action_id}|${row.record_id ?? ""}`,
|
|
321
|
+
Number(row.attempts),
|
|
322
|
+
]));
|
|
323
|
+
for (const { start, error } of attempted.failed) {
|
|
324
|
+
const attempts = attemptsOf.get(`${start.trigger}|${start.actionId}|${start.recordId ?? ""}`);
|
|
325
|
+
const annotations = {
|
|
326
|
+
requestId,
|
|
327
|
+
trigger: start.trigger,
|
|
328
|
+
workflow: start.workflow,
|
|
329
|
+
actionId: start.actionId,
|
|
330
|
+
recordId: start.recordId,
|
|
331
|
+
attempts: attempts ?? null,
|
|
332
|
+
reason: error.reason,
|
|
333
|
+
};
|
|
334
|
+
yield* Effect.logWarning("ontology.workflow.trigger_start_failed", error).pipe(Effect.annotateLogs(annotations));
|
|
335
|
+
if (attempts === MAX_START_ATTEMPTS) {
|
|
336
|
+
yield* Effect.logError("ontology.workflow.trigger_start_abandoned").pipe(Effect.annotateLogs(annotations));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Starts the rows an action's transaction inserted, after its commit, in the same request. A
|
|
343
|
+
* failure is logged and leaves the row pending for the sweep; nothing here fails the action.
|
|
344
|
+
*/
|
|
345
|
+
export function startCommitted(sql, starter, starts, requestId) {
|
|
346
|
+
if (starts.length === 0) {
|
|
347
|
+
return Effect.void;
|
|
348
|
+
}
|
|
349
|
+
if (starter === undefined) {
|
|
350
|
+
return Effect.logDebug("ontology.workflow.trigger_start_deferred").pipe(Effect.annotateLogs({ requestId, pending: starts.length }));
|
|
351
|
+
}
|
|
352
|
+
return attempt(starter, starts).pipe(Effect.flatMap((attempted) => record(sql, attempted, requestId)), Effect.catchCause((cause) => Effect.logError("ontology.workflow.trigger_start_failed", cause).pipe(Effect.annotateLogs({ requestId, pending: starts.length }))));
|
|
353
|
+
}
|
|
354
|
+
/** The database's clock as the sweep reads it, ISO 8601 with milliseconds. */
|
|
355
|
+
const SWEEP_CLOCK = `to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`;
|
|
356
|
+
/** A sweep's result when the database has no `_workflow_starts` table: nothing to do. */
|
|
357
|
+
function nothingToSweep() {
|
|
358
|
+
return { started: 0, failed: 0, abandoned: 0, waiting: 0, at: new Date().toISOString() };
|
|
359
|
+
}
|
|
360
|
+
/** Whether the database has `_workflow_starts`: a manifest with an action trigger was migrated. */
|
|
361
|
+
function hasStartsTable(sql) {
|
|
362
|
+
return sql
|
|
363
|
+
.unsafe(`SELECT to_regclass('"${WORKFLOW_STARTS_TABLE}"') IS NOT NULL AS "present"`)
|
|
364
|
+
.pipe(Effect.map(([row]) => row?.present === true));
|
|
365
|
+
}
|
|
366
|
+
function pendingOf(row) {
|
|
367
|
+
return {
|
|
368
|
+
trigger: row.trigger,
|
|
369
|
+
workflow: row.workflow,
|
|
370
|
+
actionId: row.action_id,
|
|
371
|
+
recordId: row.record_id,
|
|
372
|
+
input: typeof row.input === "string" ? JSON.parse(row.input) : row.input,
|
|
373
|
+
...(row.reservation ? { reservation: row.reservation } : {}),
|
|
374
|
+
...(row.arguments ? { arguments: decodeWorkflowArguments(row.arguments) } : {}),
|
|
375
|
+
...(row.provider_workflow_id ? { workflowId: row.provider_workflow_id } : {}),
|
|
376
|
+
...(row.admission_attributes ? { attributes: row.admission_attributes } : {}),
|
|
377
|
+
...(row.admission_deployment_id ? { deploymentId: row.admission_deployment_id } : {}),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* The sweep: starts up to {@link SWEEP_BATCH} rows pending for more than
|
|
382
|
+
* {@link SWEEP_DELAY_SECONDS}, oldest first, locked with `SKIP LOCKED` so that concurrent sweeps
|
|
383
|
+
* take different rows, and records each outcome before the locks are released. Rows that failed
|
|
384
|
+
* {@link MAX_START_ATTEMPTS} times are taken only with `retryAbandoned`. Started rows older than
|
|
385
|
+
* {@link STARTED_RETENTION_DAYS} days are deleted. `waiting` counts the pending rows an automatic
|
|
386
|
+
* sweep still takes, young ones included, created before `before` when it is given: the recovery
|
|
387
|
+
* run's horizon.
|
|
388
|
+
*/
|
|
389
|
+
export function sweepStarts(sql, starter, options) {
|
|
390
|
+
return Effect.gen(function* () {
|
|
391
|
+
if (!(yield* hasStartsTable(sql))) {
|
|
392
|
+
return nothingToSweep();
|
|
393
|
+
}
|
|
394
|
+
const selected = yield* sql.withTransaction(Effect.gen(function* () {
|
|
395
|
+
const rows = yield* sql.unsafe(`SELECT "trigger", "action_id"::text AS "action_id", "record_id"::text AS "record_id", ` +
|
|
396
|
+
`"workflow", "input",reservation,arguments,provider_workflow_id,admission_attributes,` +
|
|
397
|
+
`admission_deployment_id FROM "${WORKFLOW_STARTS_TABLE}" ` +
|
|
398
|
+
`WHERE "run_id" IS NULL AND "created_at" < now() - make_interval(secs => $1) ` +
|
|
399
|
+
`AND (submission_lease_until IS NULL OR submission_lease_until < clock_timestamp()) ` +
|
|
400
|
+
`AND ($2 OR "attempts" < $3) ORDER BY "created_at" LIMIT $4 FOR UPDATE SKIP LOCKED`, [SWEEP_DELAY_SECONDS, options.retryAbandoned, MAX_START_ATTEMPTS, SWEEP_BATCH]);
|
|
401
|
+
for (const row of rows) {
|
|
402
|
+
yield* sql.unsafe(`UPDATE _workflow_starts SET submission_lease_until=clock_timestamp()+interval '30 seconds'
|
|
403
|
+
WHERE trigger=$1 AND action_id=$2::uuid AND record_id IS NOT DISTINCT FROM $3::uuid`, [row.trigger, row.action_id, row.record_id]);
|
|
404
|
+
}
|
|
405
|
+
return rows;
|
|
406
|
+
}));
|
|
407
|
+
if (selected.length > 0 && starter === undefined) {
|
|
408
|
+
return yield* Effect.die(new Error("The sweep needs the ontology service's starter."));
|
|
409
|
+
}
|
|
410
|
+
const attempted = selected.length === 0
|
|
411
|
+
? { started: [], failed: [] }
|
|
412
|
+
: yield* attempt(starter, selected.map(pendingOf));
|
|
413
|
+
// Provider submission is outside the lease transaction. Concurrent/reclaimed attempts use the
|
|
414
|
+
// same persisted reservation rather than generating another provider run.
|
|
415
|
+
yield* record(sql, attempted, options.requestId);
|
|
416
|
+
yield* sql.unsafe(`DELETE FROM "${WORKFLOW_STARTS_TABLE}" WHERE "run_id" IS NOT NULL ` +
|
|
417
|
+
`AND "started_at" < now() - make_interval(days => $1)`, [STARTED_RETENTION_DAYS]);
|
|
418
|
+
const [left] = yield* sql.unsafe(`SELECT count(*) FILTER (WHERE "attempts" >= $1)::text AS "abandoned", ` +
|
|
419
|
+
`count(*) FILTER (WHERE "attempts" < $1 ` +
|
|
420
|
+
`AND ($2::timestamptz IS NULL OR "created_at" < $2::timestamptz))::text AS "waiting", ` +
|
|
421
|
+
`${SWEEP_CLOCK} AS "at" FROM "${WORKFLOW_STARTS_TABLE}" WHERE "run_id" IS NULL`, [MAX_START_ATTEMPTS, options.before ?? null]);
|
|
422
|
+
return {
|
|
423
|
+
started: attempted.started.length,
|
|
424
|
+
failed: attempted.failed.length,
|
|
425
|
+
abandoned: Number(left?.abandoned ?? 0),
|
|
426
|
+
waiting: Number(left?.waiting ?? 0),
|
|
427
|
+
at: left?.at ?? new Date().toISOString(),
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
/** The starts listing's cursor does not decode: it was not one this listing gave. */
|
|
432
|
+
export class InvalidStartsCursor extends Schema.TaggedError()("InvalidStartsCursor", { message: Schema.String }) {
|
|
433
|
+
}
|
|
434
|
+
const Cursor = Schema.Tuple([
|
|
435
|
+
Schema.String,
|
|
436
|
+
Schema.String,
|
|
437
|
+
Schema.String,
|
|
438
|
+
Schema.NullOr(Schema.String),
|
|
439
|
+
]);
|
|
440
|
+
function encodeCursor(row) {
|
|
441
|
+
return Buffer.from(JSON.stringify([row.created_cursor, row.trigger, row.action_id, row.record_id])).toString("base64url");
|
|
442
|
+
}
|
|
443
|
+
function decodeCursor(cursor) {
|
|
444
|
+
return Effect.try({
|
|
445
|
+
try: () => JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")),
|
|
446
|
+
catch: () => new InvalidStartsCursor({ message: "The cursor is not one this listing gave." }),
|
|
447
|
+
}).pipe(Effect.flatMap((value) => Schema.decodeUnknownEffect(Cursor)(value).pipe(Effect.mapError(() => new InvalidStartsCursor({ message: "The cursor is not one this listing gave." })))));
|
|
448
|
+
}
|
|
449
|
+
const TIMESTAMP = `'YYYY-MM-DD"T"HH24:MI:SS.US"Z"'`;
|
|
450
|
+
/**
|
|
451
|
+
* A page of a workflow's starts, newest first: all of them, or only the pending or the started
|
|
452
|
+
* ones. A database without `_workflow_starts` has none.
|
|
453
|
+
*/
|
|
454
|
+
export function listStarts(sql, input) {
|
|
455
|
+
return Effect.gen(function* () {
|
|
456
|
+
const cursor = input.after === undefined ? undefined : yield* decodeCursor(input.after);
|
|
457
|
+
if (!(yield* hasStartsTable(sql))) {
|
|
458
|
+
return { items: [], nextCursor: null };
|
|
459
|
+
}
|
|
460
|
+
const params = [input.workflow];
|
|
461
|
+
const conditions = [`"workflow" = $1`];
|
|
462
|
+
if (input.pending !== undefined) {
|
|
463
|
+
conditions.push(input.pending ? `"run_id" IS NULL` : `"run_id" IS NOT NULL`);
|
|
464
|
+
}
|
|
465
|
+
if (cursor !== undefined) {
|
|
466
|
+
params.push(cursor[0], cursor[1], cursor[2], cursor[3] ?? NIL_RECORD_ID);
|
|
467
|
+
conditions.push(`("created_at", "trigger", "action_id", COALESCE("record_id", '${NIL_RECORD_ID}'::uuid)) < ` +
|
|
468
|
+
`($2::timestamptz, $3, $4::uuid, $5::uuid)`);
|
|
469
|
+
}
|
|
470
|
+
params.push(input.limit + 1);
|
|
471
|
+
const rows = yield* sql.unsafe(`SELECT "trigger", "action_id"::text AS "action_id", "record_id"::text AS "record_id", ` +
|
|
472
|
+
`to_char("created_at" AT TIME ZONE 'UTC', ${TIMESTAMP}) AS "created_at", ` +
|
|
473
|
+
`"created_at"::text AS "created_cursor", "run_id", ` +
|
|
474
|
+
`to_char("started_at" AT TIME ZONE 'UTC', ${TIMESTAMP}) AS "started_at", "attempts" ` +
|
|
475
|
+
`FROM "${WORKFLOW_STARTS_TABLE}" WHERE ${conditions.join(" AND ")} ` +
|
|
476
|
+
`ORDER BY "created_at" DESC, "trigger" DESC, "action_id" DESC, ` +
|
|
477
|
+
`COALESCE("record_id", '${NIL_RECORD_ID}'::uuid) DESC LIMIT $${params.length}`, params);
|
|
478
|
+
const page = rows.slice(0, input.limit);
|
|
479
|
+
const last = page.at(-1);
|
|
480
|
+
return {
|
|
481
|
+
items: page.map((row) => ({
|
|
482
|
+
trigger: row.trigger,
|
|
483
|
+
actionId: row.action_id,
|
|
484
|
+
recordId: row.record_id,
|
|
485
|
+
createdAt: canonicalInstant(row.created_at),
|
|
486
|
+
runId: row.run_id,
|
|
487
|
+
startedAt: row.started_at === null ? null : canonicalInstant(row.started_at),
|
|
488
|
+
attempts: Number(row.attempts),
|
|
489
|
+
})),
|
|
490
|
+
nextCursor: rows.length > input.limit && last !== undefined ? encodeCursor(last) : null,
|
|
491
|
+
};
|
|
492
|
+
});
|
|
493
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who signs workflow users in: no one. A workflow's runs execute in the ontology service's own
|
|
3
|
+
* process, which binds the runtime to the workflow's user; no token names them.
|
|
4
|
+
*/
|
|
5
|
+
export declare const WORKFLOW_USER_PROVIDER = "segmentstream-ontology-workflow";
|
|
6
|
+
/** The identity subject of a workflow's user: `workflows:<workspace id>:<workflow name>`. */
|
|
7
|
+
export declare function workflowSubject(workspaceId: string, workflow: string): string;
|
|
8
|
+
/** The workflow a workflow user's identity subject names, whatever the workspace. */
|
|
9
|
+
export declare function workflowOfSubject(subject: unknown): string | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who signs workflow users in: no one. A workflow's runs execute in the ontology service's own
|
|
3
|
+
* process, which binds the runtime to the workflow's user; no token names them.
|
|
4
|
+
*/
|
|
5
|
+
export const WORKFLOW_USER_PROVIDER = "segmentstream-ontology-workflow";
|
|
6
|
+
/** The identity subject of a workflow's user: `workflows:<workspace id>:<workflow name>`. */
|
|
7
|
+
export function workflowSubject(workspaceId, workflow) {
|
|
8
|
+
return `workflows:${workspaceId}:${workflow}`;
|
|
9
|
+
}
|
|
10
|
+
/** The workflow a workflow user's identity subject names, whatever the workspace. */
|
|
11
|
+
export function workflowOfSubject(subject) {
|
|
12
|
+
if (typeof subject !== "string" || !subject.startsWith("workflows:")) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const parts = subject.split(":");
|
|
16
|
+
return parts.length === 3 && parts[1] !== "" && parts[2] !== "" ? parts[2] : undefined;
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** Verified platform claims retained with each write; never supplied by request headers. */
|
|
3
|
+
export declare const WorkloadExecution: Schema.Struct<{
|
|
4
|
+
readonly issuer: Schema.String;
|
|
5
|
+
readonly audience: Schema.String;
|
|
6
|
+
readonly organizationId: Schema.String;
|
|
7
|
+
readonly workspaceId: Schema.String;
|
|
8
|
+
readonly subject: Schema.String;
|
|
9
|
+
readonly deploymentId: Schema.String;
|
|
10
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
11
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
12
|
+
readonly ontologyOrigin: Schema.String;
|
|
13
|
+
readonly environment: Schema.Literals<readonly ["production", "preview"]>;
|
|
14
|
+
readonly jobName: Schema.NullOr<Schema.String>;
|
|
15
|
+
readonly runId: Schema.String;
|
|
16
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
17
|
+
readonly attemptId: Schema.String;
|
|
18
|
+
readonly generation: Schema.Number;
|
|
19
|
+
readonly tokenId: Schema.String;
|
|
20
|
+
readonly issuedAt: Schema.Number;
|
|
21
|
+
readonly expiresAt: Schema.Number;
|
|
22
|
+
}>;
|
|
23
|
+
export type WorkloadExecution = typeof WorkloadExecution.Type;
|
|
24
|
+
/** Who signs the pipeline worker in: no one. Its requests authenticate with workload tokens. */
|
|
25
|
+
export declare const PIPELINE_WORKER_PROVIDER = "segmentstream-ontology-workload";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** Verified platform claims retained with each write; never supplied by request headers. */
|
|
3
|
+
export const WorkloadExecution = Schema.Struct({
|
|
4
|
+
issuer: Schema.String,
|
|
5
|
+
audience: Schema.String,
|
|
6
|
+
organizationId: Schema.String,
|
|
7
|
+
workspaceId: Schema.String,
|
|
8
|
+
subject: Schema.String,
|
|
9
|
+
deploymentId: Schema.String,
|
|
10
|
+
ontologyDeploymentId: Schema.String,
|
|
11
|
+
servingOntologyDeploymentId: Schema.String,
|
|
12
|
+
ontologyOrigin: Schema.String,
|
|
13
|
+
environment: Schema.Literals(["production", "preview"]),
|
|
14
|
+
jobName: Schema.NullOr(Schema.String),
|
|
15
|
+
runId: Schema.String,
|
|
16
|
+
executionId: Schema.NullOr(Schema.String),
|
|
17
|
+
attemptId: Schema.String,
|
|
18
|
+
generation: Schema.Number,
|
|
19
|
+
tokenId: Schema.String,
|
|
20
|
+
issuedAt: Schema.Number,
|
|
21
|
+
expiresAt: Schema.Number,
|
|
22
|
+
});
|
|
23
|
+
/** Who signs the pipeline worker in: no one. Its requests authenticate with workload tokens. */
|
|
24
|
+
export const PIPELINE_WORKER_PROVIDER = "segmentstream-ontology-workload";
|