@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,215 @@
|
|
|
1
|
+
import { enableShadowCapture } from "./shadow.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { Effect, Schema } from "effect";
|
|
4
|
+
import { joinColumnNames, joinTableName, quoteIdentifier as qi, quoteLiteral as ql, tableName, } from "../migrate/storage.js";
|
|
5
|
+
import { REPLICATION_RECORD_BYTES, validateReplicationSelection, } from "./model.js";
|
|
6
|
+
import { REPLICATION_TARGET_FILLS, REPLICATION_TARGET_METADATA, snapshotExpression, } from "./schema.js";
|
|
7
|
+
import { lockReplicationLifecycle, shareReplicationLifecycle } from "./transaction.js";
|
|
8
|
+
import { initialReplicationLineage } from "./lineage.js";
|
|
9
|
+
import { scheduleReplicationScans, replicationScanScopes, advanceReplicationScan, } from "./scans.js";
|
|
10
|
+
/** An initial scan cannot be created with the requested state or record projection. */
|
|
11
|
+
export class ReplicationBaselineFailed extends Schema.TaggedError()("ReplicationBaselineFailed", { message: Schema.String, cause: Schema.optionalKey(Schema.Unknown) }) {
|
|
12
|
+
}
|
|
13
|
+
const targetSql = `SELECT generation::text, source_id::text, from_revision::text, manifest,
|
|
14
|
+
selection, protocol, baseline_complete, backfill_complete, initial_revision::text,
|
|
15
|
+
${REPLICATION_TARGET_FILLS}, ${REPLICATION_TARGET_METADATA}, replay_complete, baseline_cursor
|
|
16
|
+
FROM _ontology_replication_targets WHERE generation = $1::uuid`;
|
|
17
|
+
/** Enable capture in a short fenced transaction before scanning any business records. */
|
|
18
|
+
export const prepareReplicationBaseline = (sql, input) => sql.withTransaction(Effect.gen(function* () {
|
|
19
|
+
yield* sql.unsafe("SET LOCAL statement_timeout = '30s'");
|
|
20
|
+
yield* sql.unsafe("SET LOCAL lock_timeout = '5s'");
|
|
21
|
+
yield* lockReplicationLifecycle(sql);
|
|
22
|
+
const [state] = yield* sql.unsafe("SELECT source_id::text, revision::text, enabled, version FROM _ontology_replication WHERE id");
|
|
23
|
+
if (!state || state.version < 4) {
|
|
24
|
+
return yield* new ReplicationBaselineFailed({
|
|
25
|
+
message: "Run the ontology SDK migration before enabling replication.",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const [existing] = yield* sql.unsafe(targetSql, [input.generation]);
|
|
29
|
+
if (existing) {
|
|
30
|
+
return existing;
|
|
31
|
+
}
|
|
32
|
+
const [catalog] = yield* sql.unsafe("SELECT hash FROM _ontology WHERE id");
|
|
33
|
+
if (catalog?.hash !== input.manifest.hash) {
|
|
34
|
+
return yield* new ReplicationBaselineFailed({
|
|
35
|
+
message: "The ontology changed before replication started; retry with the current manifest.",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
yield* Effect.try({
|
|
39
|
+
try: () => validateReplicationSelection(input.manifest, input.selection),
|
|
40
|
+
catch: (cause) => new ReplicationBaselineFailed({ message: "Invalid replication projection.", cause }),
|
|
41
|
+
});
|
|
42
|
+
// Drain raw/pre-upgrade writers only at enablement; online pages never lock business tables.
|
|
43
|
+
const tables = input.manifest.entities
|
|
44
|
+
.filter((e) => e.source === undefined && !e.builtIn)
|
|
45
|
+
.flatMap((e) => [
|
|
46
|
+
tableName(e.name),
|
|
47
|
+
...e.relationships
|
|
48
|
+
.filter((r) => r.cardinality === "many")
|
|
49
|
+
.map((r) => joinTableName(e.name, r.name)),
|
|
50
|
+
]);
|
|
51
|
+
if (tables.length > 0) {
|
|
52
|
+
yield* sql.unsafe(`LOCK TABLE ${tables.map(qi).join(", ")} IN SHARE MODE`);
|
|
53
|
+
}
|
|
54
|
+
if (!state.enabled && state.version >= 6) {
|
|
55
|
+
yield* sql.unsafe("UPDATE _ontology_replication_targets SET capturing=false");
|
|
56
|
+
}
|
|
57
|
+
const source = input.newSource ? randomUUID() : state.source_id;
|
|
58
|
+
yield* sql.unsafe(`INSERT INTO _ontology_replication_targets
|
|
59
|
+
(generation, source_id, from_revision, manifest, selection, protocol, baseline_complete)
|
|
60
|
+
VALUES ($1::uuid, $2::uuid, 0, $3::jsonb, $4::jsonb, 4, false)`, [input.generation, source, JSON.stringify(input.manifest), JSON.stringify(input.selection)]);
|
|
61
|
+
yield* sql.unsafe(`UPDATE _ontology_replication SET source_id = $1::uuid, selection = $2::jsonb,
|
|
62
|
+
enabled = true, exclusions = $3::jsonb, active_generation = $4::uuid WHERE id`, [
|
|
63
|
+
source,
|
|
64
|
+
JSON.stringify(Object.fromEntries(input.selection.map((s) => [s.entityId, s]))),
|
|
65
|
+
JSON.stringify(input.exclusions ?? []),
|
|
66
|
+
input.generation,
|
|
67
|
+
]);
|
|
68
|
+
if (state.version >= 5) {
|
|
69
|
+
yield* sql.unsafe(`UPDATE _ontology_replication_targets SET lineage = $2::jsonb, scans_complete = false
|
|
70
|
+
WHERE generation = $1::uuid`, [input.generation, JSON.stringify(initialReplicationLineage(input))]);
|
|
71
|
+
yield* scheduleReplicationScans(sql, input.generation, replicationScanScopes(input), {
|
|
72
|
+
initial: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const [target] = yield* sql.unsafe(targetSql, [input.generation]);
|
|
76
|
+
if (state.version >= 6) {
|
|
77
|
+
yield* enableShadowCapture(sql, target, replicationScanScopes(target));
|
|
78
|
+
}
|
|
79
|
+
return target;
|
|
80
|
+
}));
|
|
81
|
+
const BASELINE_PAGE_SIZE = 250;
|
|
82
|
+
function baselineSources(target) {
|
|
83
|
+
return target.selection.flatMap((item) => {
|
|
84
|
+
const entity = target.manifest.entities.find((e) => e.id === item.entityId);
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
entityId: entity.id,
|
|
88
|
+
kind: "entity",
|
|
89
|
+
records: `SELECT r.id::text AS record_key,
|
|
90
|
+
(SELECT jsonb_object_agg(key, value) FROM jsonb_each(${snapshotExpression(entity, "r")})
|
|
91
|
+
WHERE key = 'id' OR ${ql(JSON.stringify(item.fields))}::jsonb ? key) AS payload
|
|
92
|
+
FROM ${qi(tableName(entity.name))} r
|
|
93
|
+
WHERE r.id >= COALESCE(NULLIF($2::text, '')::uuid, '00000000-0000-0000-0000-000000000000'::uuid)
|
|
94
|
+
AND ($2::text = '' OR r.id > NULLIF($2::text, '')::uuid)
|
|
95
|
+
ORDER BY r.id LIMIT ${BASELINE_PAGE_SIZE}`,
|
|
96
|
+
},
|
|
97
|
+
...entity.relationships
|
|
98
|
+
.filter((r) => item.relationships?.includes(r.name))
|
|
99
|
+
.map((relationship) => {
|
|
100
|
+
const columns = joinColumnNames({
|
|
101
|
+
entity: entity.name,
|
|
102
|
+
relationship: relationship.name,
|
|
103
|
+
target: relationship.to,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
entityId: entity.id,
|
|
107
|
+
kind: `edge:${relationship.name}`,
|
|
108
|
+
records: `SELECT ${qi(columns.source)}::text || ':' || ${qi(columns.target)}::text AS record_key,
|
|
109
|
+
jsonb_build_object('source_id', ${qi(columns.source)}::text,
|
|
110
|
+
'target_id', ${qi(columns.target)}::text) AS payload
|
|
111
|
+
FROM ${qi(joinTableName(entity.name, relationship.name))}
|
|
112
|
+
WHERE (${qi(columns.source)}, ${qi(columns.target)}) >= (
|
|
113
|
+
COALESCE(NULLIF(split_part($2::text, ':', 1), '')::uuid, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
114
|
+
COALESCE(NULLIF(split_part($2::text, ':', 2), '')::uuid, '00000000-0000-0000-0000-000000000000'::uuid))
|
|
115
|
+
AND ($2::text = '' OR (${qi(columns.source)}, ${qi(columns.target)}) > (
|
|
116
|
+
NULLIF(split_part($2::text, ':', 1), '')::uuid, NULLIF(split_part($2::text, ':', 2), '')::uuid))
|
|
117
|
+
ORDER BY ${qi(columns.source)}, ${qi(columns.target)} LIMIT ${BASELINE_PAGE_SIZE}`,
|
|
118
|
+
};
|
|
119
|
+
}),
|
|
120
|
+
];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Commit one keyset page and its cursor together. Schema 5 locks source rows before allocating
|
|
125
|
+
* snapshot revisions; older schemas retain their revision-zero baseline protocol. A failed page
|
|
126
|
+
* retries without changing earlier pages, and capture covers inserts/deletes across pages.
|
|
127
|
+
*/
|
|
128
|
+
export const advanceReplicationBaseline = (sql, generation) => Effect.gen(function* () {
|
|
129
|
+
const [version] = yield* sql.unsafe("SELECT version FROM _ontology_replication WHERE id");
|
|
130
|
+
if ((version?.version ?? 4) >= 5) {
|
|
131
|
+
yield* advanceReplicationScan(sql, generation);
|
|
132
|
+
const [target] = yield* sql.unsafe(targetSql, [generation]);
|
|
133
|
+
return target;
|
|
134
|
+
}
|
|
135
|
+
const progress = yield* sql.withTransaction(Effect.gen(function* () {
|
|
136
|
+
yield* sql.unsafe("SET LOCAL statement_timeout = '30s'");
|
|
137
|
+
yield* sql.unsafe("SET LOCAL lock_timeout = '5s'");
|
|
138
|
+
yield* shareReplicationLifecycle(sql);
|
|
139
|
+
const [target] = yield* sql.unsafe(`${targetSql} FOR NO KEY UPDATE`, [generation]);
|
|
140
|
+
if (!target) {
|
|
141
|
+
return yield* new ReplicationBaselineFailed({
|
|
142
|
+
message: "The replication target is missing.",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (target.baseline_complete !== false) {
|
|
146
|
+
return { target, finish: false };
|
|
147
|
+
}
|
|
148
|
+
const [state] = yield* sql.unsafe("SELECT enabled, active_generation::text FROM _ontology_replication WHERE id");
|
|
149
|
+
if (!state?.enabled || state.active_generation !== generation) {
|
|
150
|
+
return yield* new ReplicationBaselineFailed({
|
|
151
|
+
message: "Resume the active replication generation before scanning its baseline.",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const index = target.baseline_cursor.index ?? 0;
|
|
155
|
+
const source = baselineSources(target)[index];
|
|
156
|
+
if (!source) {
|
|
157
|
+
return { target, finish: true };
|
|
158
|
+
}
|
|
159
|
+
// Materialize once in PostgreSQL; only the small progress result crosses the transport.
|
|
160
|
+
const [page] = yield* sql.unsafe(`WITH page AS MATERIALIZED (
|
|
161
|
+
SELECT * FROM (${source.records}) records
|
|
162
|
+
), valid AS (
|
|
163
|
+
SELECT NOT EXISTS(SELECT 1 FROM page WHERE octet_length(payload::text) > ${REPLICATION_RECORD_BYTES}) AS ok
|
|
164
|
+
), inserted AS (
|
|
165
|
+
INSERT INTO _ontology_replication_baselines (generation, event)
|
|
166
|
+
SELECT $1::uuid, jsonb_build_object('source_id', $3::text, 'entity_id', $4::text,
|
|
167
|
+
'record_key', record_key, 'record_kind', $5::text, 'revision', '0',
|
|
168
|
+
'deleted', false, 'schema_hash', $6::text, 'payload', payload::text)
|
|
169
|
+
FROM page WHERE (SELECT ok FROM valid) RETURNING id
|
|
170
|
+
), queued AS (
|
|
171
|
+
INSERT INTO _ontology_replication_delivery (generation, event_id, revision, is_baseline)
|
|
172
|
+
SELECT $1::uuid, id, 0, true FROM inserted RETURNING event_id
|
|
173
|
+
) SELECT (SELECT count(*)::integer FROM queued) AS count,
|
|
174
|
+
(SELECT max(record_key) FROM page) AS key, NOT (SELECT ok FROM valid) AS oversized`, [
|
|
175
|
+
generation,
|
|
176
|
+
target.baseline_cursor.key ?? "",
|
|
177
|
+
target.source_id,
|
|
178
|
+
source.entityId,
|
|
179
|
+
source.kind,
|
|
180
|
+
target.manifest.hash,
|
|
181
|
+
]);
|
|
182
|
+
if (page.oversized) {
|
|
183
|
+
return yield* new ReplicationBaselineFailed({
|
|
184
|
+
message: `A projected baseline record exceeds ${REPLICATION_RECORD_BYTES} bytes; ` +
|
|
185
|
+
"exclude large fields before enabling replication.",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const cursor = page.count < BASELINE_PAGE_SIZE ? { index: index + 1 } : { index, key: page.key };
|
|
189
|
+
yield* sql.unsafe("UPDATE _ontology_replication_targets SET baseline_cursor = $2::jsonb WHERE generation = $1::uuid", [generation, JSON.stringify(cursor)]);
|
|
190
|
+
return { target, finish: false };
|
|
191
|
+
}));
|
|
192
|
+
if (!progress.finish) {
|
|
193
|
+
return progress.target;
|
|
194
|
+
}
|
|
195
|
+
// Do not upgrade a shared lock: start a fresh transaction with the legacy singleton fence.
|
|
196
|
+
return yield* sql.withTransaction(Effect.gen(function* () {
|
|
197
|
+
yield* sql.unsafe("SET LOCAL lock_timeout = '5s'");
|
|
198
|
+
yield* lockReplicationLifecycle(sql);
|
|
199
|
+
const [state] = yield* sql.unsafe("SELECT enabled, active_generation::text FROM _ontology_replication WHERE id");
|
|
200
|
+
if (!state?.enabled || state.active_generation !== generation) {
|
|
201
|
+
return yield* new ReplicationBaselineFailed({
|
|
202
|
+
message: "Resume the active replication generation before completing its baseline.",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
yield* sql.unsafe(`UPDATE _ontology_replication_targets SET baseline_complete = true,
|
|
206
|
+
initial_revision = (SELECT CASE WHEN is_called THEN last_value ELSE 0 END FROM _ontology_replication_revision)
|
|
207
|
+
WHERE generation = $1::uuid AND NOT baseline_complete`, [generation]);
|
|
208
|
+
const [target] = yield* sql.unsafe(targetSql, [generation]);
|
|
209
|
+
return target;
|
|
210
|
+
}));
|
|
211
|
+
});
|
|
212
|
+
/** Prepare once, then advance a bounded page; subsequent calls resume the same durable target. */
|
|
213
|
+
export const createReplicationBaseline = (sql, input) => prepareReplicationBaseline(sql, input).pipe(Effect.flatMap((target) => target.baseline_complete === false
|
|
214
|
+
? advanceReplicationBaseline(sql, target.generation)
|
|
215
|
+
: Effect.succeed(target)));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReplicationTarget } from "./store.js";
|
|
2
|
+
export declare const REPLICATION_TABLE_SCHEMA: readonly [...{
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
mode: string;
|
|
6
|
+
}[], {
|
|
7
|
+
readonly name: "revision";
|
|
8
|
+
readonly type: "NUMERIC";
|
|
9
|
+
readonly mode: "REQUIRED";
|
|
10
|
+
}, {
|
|
11
|
+
readonly name: "deleted";
|
|
12
|
+
readonly type: "BOOLEAN";
|
|
13
|
+
readonly mode: "REQUIRED";
|
|
14
|
+
}, {
|
|
15
|
+
readonly name: "payload";
|
|
16
|
+
readonly type: "JSON";
|
|
17
|
+
readonly mode: "REQUIRED";
|
|
18
|
+
}];
|
|
19
|
+
/**
|
|
20
|
+
* Project typed fields before ranking, so a caller reading a few columns can prune JSON subfields
|
|
21
|
+
* instead of carrying whole historical payloads through the window. Keep tombstones until after
|
|
22
|
+
* ranking. The struct separates business field names from the private ranking/deletion columns.
|
|
23
|
+
* Events captured before a field was added lack its key and read as its fill.
|
|
24
|
+
*/
|
|
25
|
+
export declare function replicationViews(project: string, target: ReplicationTarget): {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly query: string;
|
|
28
|
+
}[];
|
|
29
|
+
/**
|
|
30
|
+
* A single BigQuery statement observes one selector snapshot across every entity/edge join.
|
|
31
|
+
* Each branch has a literal generation predicate for clustered pruning. Unsupported new columns
|
|
32
|
+
* fail honestly until their complete epoch is selected; they never synthesize null coverage.
|
|
33
|
+
*/
|
|
34
|
+
export declare function shadowReplicationViews(project: string, epochs: readonly import("./shadow.js").ShadowEpoch[], policy?: ReplicationTarget | undefined): {
|
|
35
|
+
name: string;
|
|
36
|
+
query: string;
|
|
37
|
+
}[];
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { permittedReplicationField } from "./identities.js";
|
|
2
|
+
import { queryFields } from "../query.js";
|
|
3
|
+
export const REPLICATION_TABLE_SCHEMA = [
|
|
4
|
+
...[
|
|
5
|
+
"event_id",
|
|
6
|
+
"source_id",
|
|
7
|
+
"generation",
|
|
8
|
+
"entity_id",
|
|
9
|
+
"record_key",
|
|
10
|
+
"record_kind",
|
|
11
|
+
"schema_hash",
|
|
12
|
+
].map((name) => ({ name, type: "STRING", mode: "REQUIRED" })),
|
|
13
|
+
{ name: "revision", type: "NUMERIC", mode: "REQUIRED" },
|
|
14
|
+
{ name: "deleted", type: "BOOLEAN", mode: "REQUIRED" },
|
|
15
|
+
{ name: "payload", type: "JSON", mode: "REQUIRED" },
|
|
16
|
+
];
|
|
17
|
+
// A quoted BigQuery string cannot contain a raw line break.
|
|
18
|
+
const literal = (value) => `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n").replaceAll("\r", "\\r")}'`;
|
|
19
|
+
const identifier = (value) => {
|
|
20
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value)) {
|
|
21
|
+
throw new Error("Invalid BigQuery replication identifier.");
|
|
22
|
+
}
|
|
23
|
+
return `\`${value}\``;
|
|
24
|
+
};
|
|
25
|
+
/** A fill in the form the captured branch takes before its cast: JSON, or JSON_VALUE's string. */
|
|
26
|
+
const fill = (storage, value) => storage === "json"
|
|
27
|
+
? `PARSE_JSON(${literal(JSON.stringify(value))})`
|
|
28
|
+
: literal(typeof value === "string" ? value : JSON.stringify(value));
|
|
29
|
+
const projectedValue = (storage, binding) => {
|
|
30
|
+
const fallback = Object.hasOwn(binding, "fill")
|
|
31
|
+
? fill(storage, binding.fill)
|
|
32
|
+
: `CAST(NULL AS ${storage === "json" ? "JSON" : "STRING"})`;
|
|
33
|
+
if (binding.key === null) {
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
const path = literal(`$.${binding.key}`);
|
|
37
|
+
const captured = storage === "json" ? `JSON_QUERY(payload, ${path})` : `JSON_VALUE(payload, ${path})`;
|
|
38
|
+
return Object.hasOwn(binding, "fill")
|
|
39
|
+
? `IF(JSON_QUERY(payload, ${path}) IS NULL, ${fallback}, ${captured})`
|
|
40
|
+
: captured;
|
|
41
|
+
};
|
|
42
|
+
/** CASE chooses one schema incarnation; explicit JSON null never falls back to an older key. */
|
|
43
|
+
function lineageValue(target, entity, field, storage) {
|
|
44
|
+
const epochs = target.lineage?.epochs;
|
|
45
|
+
if (!epochs?.length) {
|
|
46
|
+
const fills = target.fills?.[entity] ?? {};
|
|
47
|
+
return projectedValue(storage, {
|
|
48
|
+
key: field,
|
|
49
|
+
...(Object.hasOwn(fills, field) ? { fill: fills[field] } : {}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const groups = [];
|
|
53
|
+
for (const epoch of epochs) {
|
|
54
|
+
const value = projectedValue(storage, epoch.fields[entity]?.[field] ?? { key: null });
|
|
55
|
+
const previous = groups.at(-1);
|
|
56
|
+
if (previous?.value === value) {
|
|
57
|
+
previous.through = epoch.throughRevision;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
groups.push({ value, through: epoch.throughRevision });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (groups.length === 1) {
|
|
64
|
+
return groups[0].value;
|
|
65
|
+
}
|
|
66
|
+
return `CASE ${groups
|
|
67
|
+
.slice(0, -1)
|
|
68
|
+
.map((group) => `WHEN revision <= CAST(${literal(group.through)} AS NUMERIC) THEN ${group.value}`)
|
|
69
|
+
.join(" ")} ELSE ${groups.at(-1).value} END`;
|
|
70
|
+
}
|
|
71
|
+
/** Field storage read through SAFE_CAST. */
|
|
72
|
+
const castTypes = {
|
|
73
|
+
integer: "INT64",
|
|
74
|
+
number: "FLOAT64",
|
|
75
|
+
boolean: "BOOL",
|
|
76
|
+
date: "DATE",
|
|
77
|
+
timestamp: "TIMESTAMP",
|
|
78
|
+
};
|
|
79
|
+
/** The BigQuery type a projected field reads as: JSON_QUERY yields JSON, JSON_VALUE yields STRING. */
|
|
80
|
+
const columnType = (storage) => storage === "json" ? "JSON" : (castTypes[storage] ?? "STRING");
|
|
81
|
+
/**
|
|
82
|
+
* Project typed fields before ranking, so a caller reading a few columns can prune JSON subfields
|
|
83
|
+
* instead of carrying whole historical payloads through the window. Keep tombstones until after
|
|
84
|
+
* ranking. The struct separates business field names from the private ranking/deletion columns.
|
|
85
|
+
* Events captured before a field was added lack its key and read as its fill.
|
|
86
|
+
*/
|
|
87
|
+
export function replicationViews(project, target) {
|
|
88
|
+
identifier(project);
|
|
89
|
+
const result = [];
|
|
90
|
+
const latest = (entity, kind, columns) => {
|
|
91
|
+
const minimum = target.lineage?.minimumRevisions[entity]?.[kind];
|
|
92
|
+
return `SELECT record.* FROM (
|
|
93
|
+
SELECT STRUCT(${columns}) AS record, deleted,
|
|
94
|
+
ROW_NUMBER() OVER (PARTITION BY record_key ORDER BY revision DESC, event_id DESC) AS position
|
|
95
|
+
FROM \`${project}.ontology_internal.events\`
|
|
96
|
+
WHERE generation = ${literal(target.generation)} AND source_id = ${literal(target.source_id)}
|
|
97
|
+
AND entity_id = ${literal(entity)} AND record_kind = ${literal(kind)}
|
|
98
|
+
${minimum === undefined ? "" : `AND revision > CAST(${literal(minimum)} AS NUMERIC)`}
|
|
99
|
+
) WHERE position = 1 AND NOT deleted`;
|
|
100
|
+
};
|
|
101
|
+
for (const selection of target.selection) {
|
|
102
|
+
const entity = target.manifest.entities.find((e) => e.id === selection.entityId);
|
|
103
|
+
const columns = queryFields(entity)
|
|
104
|
+
.filter((field) => field.name === "id" || selection.fields.includes(field.name))
|
|
105
|
+
.map((field) => {
|
|
106
|
+
const type = castTypes[field.storage];
|
|
107
|
+
const value = lineageValue(target, entity.id, field.name, field.storage);
|
|
108
|
+
return `${type ? `SAFE_CAST(${value} AS ${type})` : value} AS ${identifier(field.name)}`;
|
|
109
|
+
});
|
|
110
|
+
result.push({ name: entity.name, query: latest(entity.id, "entity", columns.join(", ")) });
|
|
111
|
+
for (const relationship of selection.relationships ?? []) {
|
|
112
|
+
result.push({
|
|
113
|
+
name: `${entity.name}_${relationship}`,
|
|
114
|
+
query: latest(entity.id, `edge:${relationship}`, "JSON_VALUE(payload, '$.source_id') AS source_id, JSON_VALUE(payload, '$.target_id') AS target_id"),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (new Set(result.map((v) => v.name.toLowerCase())).size !== result.length) {
|
|
119
|
+
throw new Error("Replicated entity and relationship view names collide.");
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A single BigQuery statement observes one selector snapshot across every entity/edge join.
|
|
125
|
+
* Each branch has a literal generation predicate for clustered pruning. Unsupported new columns
|
|
126
|
+
* fail honestly until their complete epoch is selected; they never synthesize null coverage.
|
|
127
|
+
*/
|
|
128
|
+
export function shadowReplicationViews(project, epochs, policy = epochs.at(-1)?.target) {
|
|
129
|
+
identifier(project);
|
|
130
|
+
const scopes = new Map();
|
|
131
|
+
// A placeholder is typed like the column it stands in for; an untyped NULL reads as INT64.
|
|
132
|
+
const types = new Map();
|
|
133
|
+
for (const epoch of epochs) {
|
|
134
|
+
for (const route of epoch.routes) {
|
|
135
|
+
const entity = route.target.manifest.entities.find((item) => item.id === route.entityId);
|
|
136
|
+
const name = route.kind === "entity" ? entity.name : `${entity.name}_${route.kind.slice(5)}`;
|
|
137
|
+
const view = replicationViews(project, route.target).find((view) => view.name === name);
|
|
138
|
+
if (!view) {
|
|
139
|
+
throw new Error("Replication route has no projection.");
|
|
140
|
+
}
|
|
141
|
+
const fields = route.kind === "entity"
|
|
142
|
+
? queryFields(entity).filter((field) => field.name === "id" ||
|
|
143
|
+
route.target.selection
|
|
144
|
+
.find((item) => item.entityId === entity.id)
|
|
145
|
+
?.fields.includes(field.name))
|
|
146
|
+
: [];
|
|
147
|
+
const columns = route.kind === "entity" ? fields.map((field) => field.name) : ["source_id", "target_id"];
|
|
148
|
+
for (const field of fields) {
|
|
149
|
+
// Use the latest incarnation's type; older property identities remain policy-gated.
|
|
150
|
+
types.set(`${name}.${field.name}`, columnType(field.storage));
|
|
151
|
+
}
|
|
152
|
+
const entries = scopes.get(name) ?? [];
|
|
153
|
+
entries.push({
|
|
154
|
+
epoch: epoch.id,
|
|
155
|
+
query: view.query,
|
|
156
|
+
columns,
|
|
157
|
+
entityId: route.entityId,
|
|
158
|
+
kind: route.kind,
|
|
159
|
+
generation: route.target.generation,
|
|
160
|
+
target: route.target,
|
|
161
|
+
});
|
|
162
|
+
scopes.set(name, entries);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return [...scopes].map(([name, branches]) => {
|
|
166
|
+
const columns = [...new Set(branches.flatMap((branch) => branch.columns))];
|
|
167
|
+
const empty = (column) => `CAST(NULL AS ${types.get(`${name}.${column}`) ?? "STRING"})`;
|
|
168
|
+
const missing = `NOT EXISTS(SELECT 1 FROM \`${project}.ontology_internal.serving_selector\` selector
|
|
169
|
+
JOIN \`${project}.ontology_internal.epoch_routes\` route ON route.epoch=selector.epoch
|
|
170
|
+
WHERE selector.singleton AND (${branches
|
|
171
|
+
.map((branch) => `(selector.epoch=${literal(branch.epoch)} AND route.entity_id=${literal(branch.entityId)}
|
|
172
|
+
AND route.record_kind=${literal(branch.kind)} AND route.generation=${literal(branch.generation)})`)
|
|
173
|
+
.join(" OR ")}))`;
|
|
174
|
+
// COUNT(*) can prune projected errors; the fallback row predicate must fail too.
|
|
175
|
+
const unavailableColumns = columns
|
|
176
|
+
.map((column) => `IF(${missing}, ERROR('ontology_unavailable'), ${empty(column)}) AS ${identifier(column)}`)
|
|
177
|
+
.join(", ");
|
|
178
|
+
const permitted = (branch, column) => {
|
|
179
|
+
const selected = policy?.selection.find((item) => item.entityId === branch.entityId);
|
|
180
|
+
if (!selected) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (branch.kind !== "entity") {
|
|
184
|
+
return (policy !== undefined &&
|
|
185
|
+
permittedReplicationField(branch.target, policy, branch.entityId, branch.kind));
|
|
186
|
+
}
|
|
187
|
+
return (policy !== undefined &&
|
|
188
|
+
permittedReplicationField(branch.target, policy, branch.entityId, column));
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
name,
|
|
192
|
+
query: branches
|
|
193
|
+
.map((branch) => `SELECT ${columns
|
|
194
|
+
.map((column) => branch.columns.includes(column) && permitted(branch, column)
|
|
195
|
+
? `data.${identifier(column)}`
|
|
196
|
+
: `IF(EXISTS(SELECT 1 FROM \`${project}.ontology_internal.serving_selector\`
|
|
197
|
+
WHERE singleton AND epoch=${literal(branch.epoch)}),
|
|
198
|
+
ERROR('ontology_coverage_unavailable'), ${empty(column)}) AS ${identifier(column)}`)
|
|
199
|
+
.join(", ")}
|
|
200
|
+
FROM (${branch.query}) data WHERE EXISTS (SELECT 1 FROM \`${project}.ontology_internal.serving_selector\` selector
|
|
201
|
+
JOIN \`${project}.ontology_internal.epoch_routes\` route ON route.epoch=selector.epoch
|
|
202
|
+
WHERE selector.singleton AND selector.epoch=${literal(branch.epoch)}
|
|
203
|
+
AND route.entity_id=${literal(branch.entityId)} AND route.record_kind=${literal(branch.kind)}
|
|
204
|
+
AND route.generation=${literal(branch.generation)})`)
|
|
205
|
+
.join("\nUNION ALL\n") +
|
|
206
|
+
`\nUNION ALL SELECT ${unavailableColumns}
|
|
207
|
+
FROM (SELECT 1) unavailable
|
|
208
|
+
WHERE IF(${missing}, ERROR('ontology_unavailable'), FALSE)`,
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReplicationTarget } from "./baseline.js";
|
|
2
|
+
import type { ShadowEpoch } from "./shadow.js";
|
|
3
|
+
/** Coverage is proven by identity against the selected epoch and the current export policy. */
|
|
4
|
+
export declare function shadowQueryCoverage(project: string, epoch: ShadowEpoch, policy: ReplicationTarget, requested: ReplicationTarget): {
|
|
5
|
+
entities: {
|
|
6
|
+
entityId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
fields: string[];
|
|
9
|
+
}[];
|
|
10
|
+
views: {
|
|
11
|
+
name: string;
|
|
12
|
+
query: string;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { replicationViews } from "./bigquery.js";
|
|
2
|
+
import { replicationIdentities } from "./identities.js";
|
|
3
|
+
const identifier = (name) => {
|
|
4
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
5
|
+
throw new Error("replication_coverage_identifier_invalid");
|
|
6
|
+
}
|
|
7
|
+
return `\`${name}\``;
|
|
8
|
+
};
|
|
9
|
+
/** Coverage is proven by identity against the selected epoch and the current export policy. */
|
|
10
|
+
export function shadowQueryCoverage(project, epoch, policy, requested) {
|
|
11
|
+
const requestIds = replicationIdentities(requested);
|
|
12
|
+
const policyIds = replicationIdentities(policy);
|
|
13
|
+
const entities = [];
|
|
14
|
+
const views = [];
|
|
15
|
+
for (const selection of requested.selection) {
|
|
16
|
+
const route = epoch.routes.find((item) => item.entityId === selection.entityId && item.kind === "entity");
|
|
17
|
+
const declaration = requested.manifest.entities.find((item) => item.id === selection.entityId);
|
|
18
|
+
if (!route || !declaration) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const actualIds = replicationIdentities(route.target)[selection.entityId] ?? {};
|
|
22
|
+
const actualSelection = route.target.selection.find((item) => item.entityId === selection.entityId);
|
|
23
|
+
const fields = [...new Set(["id", ...selection.fields])].flatMap((field) => {
|
|
24
|
+
const identity = requestIds[selection.entityId]?.[field];
|
|
25
|
+
const actual = ["id", ...(actualSelection?.fields ?? [])].find((name) => identity !== undefined &&
|
|
26
|
+
actualIds[name] === identity &&
|
|
27
|
+
[
|
|
28
|
+
"id",
|
|
29
|
+
...(policy.selection.find((item) => item.entityId === selection.entityId)?.fields ??
|
|
30
|
+
[]),
|
|
31
|
+
].some((permitted) => policyIds[selection.entityId]?.[permitted] === identity) &&
|
|
32
|
+
policy.selection.some((item) => item.entityId === selection.entityId));
|
|
33
|
+
return actual ? [{ field, actual }] : [];
|
|
34
|
+
});
|
|
35
|
+
const actualName = route.target.manifest.entities.find((item) => item.id === selection.entityId).name;
|
|
36
|
+
const query = replicationViews(project, route.target).find((item) => item.name === actualName).query;
|
|
37
|
+
if (!fields.length) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
entities.push({
|
|
41
|
+
entityId: selection.entityId,
|
|
42
|
+
name: declaration.name,
|
|
43
|
+
fields: fields.map((item) => item.field),
|
|
44
|
+
});
|
|
45
|
+
views.push({
|
|
46
|
+
name: declaration.name,
|
|
47
|
+
query: `SELECT ${fields
|
|
48
|
+
.map(({ field, actual }) => `data.${identifier(actual)} AS ${identifier(field)}`)
|
|
49
|
+
.join(", ")} FROM (${query}) data`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return { entities, views };
|
|
53
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { SqlClient } from "effect/unstable/sql";
|
|
3
|
+
import type { RetainedStorage } from "../migrate/model.js";
|
|
4
|
+
import type { ReplicationTarget } from "./baseline.js";
|
|
5
|
+
import { type ReplicationScanScope } from "./scans.js";
|
|
6
|
+
/** Freeze an import's source order before migrations change declaration order or storage names. */
|
|
7
|
+
export declare const prepareReplicationEvolution: (sql: SqlClient.SqlClient) => Effect.Effect<void, import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
8
|
+
export declare function replicationRecoveryScopes(current: ReplicationTarget, target: ReplicationTarget, retained?: RetainedStorage): {
|
|
9
|
+
scopes: ReplicationScanScope[];
|
|
10
|
+
resets: ReplicationScanScope[];
|
|
11
|
+
};
|
|
12
|
+
export declare const refreshReplicationProjection: (sql: SqlClient.SqlClient, current: ReplicationTarget, target: ReplicationTarget, retained?: RetainedStorage, options?: {
|
|
13
|
+
readonly resuming?: boolean;
|
|
14
|
+
}) => Effect.Effect<void, import("effect/unstable/sql/SqlError").SqlError | import("./baseline.js").ReplicationBaselineFailed, never>;
|
|
15
|
+
/** Pending and replayable envelopes outlive storage explicitly dropped by a supported migration. */
|
|
16
|
+
export declare const retainReplicationHistory: (sql: SqlClient.SqlClient, table: string) => Effect.Effect<void, import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
17
|
+
export declare const renameReplicationHistory: (sql: SqlClient.SqlClient, previous: string, next: string) => Effect.Effect<void, import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
18
|
+
/** Physical cleanup may proceed after cutover; only storage still needed by serving is protected. */
|
|
19
|
+
export declare const dropsServingReplicationStorage: (sql: SqlClient.SqlClient, input: {
|
|
20
|
+
readonly current: import("../manifest.js").OntologyManifest;
|
|
21
|
+
readonly target: ReplicationTarget;
|
|
22
|
+
readonly version: number;
|
|
23
|
+
readonly retained?: RetainedStorage;
|
|
24
|
+
readonly steps: readonly import("../migrate/model.js").MigrationStep[];
|
|
25
|
+
}) => Effect.Effect<boolean, import("effect/unstable/sql/SqlError").SqlError, never>;
|