@electric-ax/agents-server 0.3.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 +177 -0
- package/dist/chunk-Cl8Af3a2.js +11 -0
- package/dist/entrypoint.js +7319 -0
- package/dist/index.cjs +7090 -0
- package/dist/index.d.cts +4262 -0
- package/dist/index.d.ts +4263 -0
- package/dist/index.js +7053 -0
- package/drizzle/0000_baseline.sql +97 -0
- package/drizzle/0001_entity_tags_and_bridges.sql +45 -0
- package/drizzle/0002_tag_outbox_hardening.sql +14 -0
- package/drizzle/0003_entity_manifest_sources.sql +11 -0
- package/drizzle/0004_tenant_scoping.sql +139 -0
- package/drizzle/0005_pull_wake_control_plane.sql +156 -0
- package/drizzle/meta/0000_snapshot.json +593 -0
- package/drizzle/meta/_journal.json +48 -0
- package/package.json +89 -0
- package/src/authenticated-user-format.ts +17 -0
- package/src/claim-write-token-store.ts +74 -0
- package/src/db/index.ts +53 -0
- package/src/db/schema.ts +490 -0
- package/src/dev-asserted-auth.ts +46 -0
- package/src/dispatch-policy-schema.ts +52 -0
- package/src/electric-agents/adapter-types.ts +70 -0
- package/src/electric-agents/default-entity-schemas.ts +1 -0
- package/src/electric-agents/schema-validator.ts +143 -0
- package/src/electric-agents-http.ts +46 -0
- package/src/electric-agents-types.ts +335 -0
- package/src/entity-bridge-manager.ts +694 -0
- package/src/entity-manager.ts +2601 -0
- package/src/entity-projector.ts +765 -0
- package/src/entity-registry.ts +1162 -0
- package/src/entrypoint-lib.ts +295 -0
- package/src/entrypoint.ts +11 -0
- package/src/host.ts +323 -0
- package/src/index.ts +49 -0
- package/src/manifest-side-effects.ts +183 -0
- package/src/routing/agent-ui-router.ts +81 -0
- package/src/routing/context.ts +35 -0
- package/src/routing/cron-router.ts +45 -0
- package/src/routing/dispatch-policy.ts +248 -0
- package/src/routing/durable-streams-router.ts +407 -0
- package/src/routing/durable-streams-routing-adapter.ts +96 -0
- package/src/routing/electric-proxy-router.ts +61 -0
- package/src/routing/entities-router.ts +484 -0
- package/src/routing/entity-types-router.ts +229 -0
- package/src/routing/global-router.ts +33 -0
- package/src/routing/hooks.ts +123 -0
- package/src/routing/internal-router.ts +741 -0
- package/src/routing/oss-server-router.ts +56 -0
- package/src/routing/runners-router.ts +416 -0
- package/src/routing/schema.ts +141 -0
- package/src/routing/stream-append.ts +196 -0
- package/src/routing/tenant-stream-paths.ts +26 -0
- package/src/runtime-registry.ts +49 -0
- package/src/runtime.ts +537 -0
- package/src/scheduler.ts +788 -0
- package/src/schema-validation.ts +15 -0
- package/src/server.ts +374 -0
- package/src/standalone-runtime.ts +188 -0
- package/src/stream-client.ts +842 -0
- package/src/tag-stream-outbox-drainer.ts +188 -0
- package/src/tenant.ts +25 -0
- package/src/tracing.ts +57 -0
- package/src/utils/electric-url.ts +15 -0
- package/src/utils/log.ts +95 -0
- package/src/utils/server-utils.ts +245 -0
- package/src/utils/webhook-url.ts +33 -0
- package/src/wake-registry.ts +946 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library-safe top-level HTTP router for agents-server.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { AutoRouter, withParams } from 'itty-router'
|
|
6
|
+
import { durableStreamsRouter } from './durable-streams-router.js'
|
|
7
|
+
import { internalRouter } from './internal-router.js'
|
|
8
|
+
import {
|
|
9
|
+
applyCors,
|
|
10
|
+
errorMapper,
|
|
11
|
+
otelEndSpan,
|
|
12
|
+
otelStartSpan,
|
|
13
|
+
preflightCors,
|
|
14
|
+
rejectIfShuttingDown,
|
|
15
|
+
} from './hooks.js'
|
|
16
|
+
import type { AutoRouterType, IRequest } from 'itty-router'
|
|
17
|
+
import type { TenantContext } from './context.js'
|
|
18
|
+
|
|
19
|
+
export type GlobalRoutes = AutoRouterType<IRequest, [TenantContext], Response>
|
|
20
|
+
|
|
21
|
+
export const globalRouter: GlobalRoutes = AutoRouter<
|
|
22
|
+
IRequest,
|
|
23
|
+
[TenantContext],
|
|
24
|
+
Response
|
|
25
|
+
>({
|
|
26
|
+
before: [preflightCors, withParams, otelStartSpan, rejectIfShuttingDown],
|
|
27
|
+
catch: errorMapper,
|
|
28
|
+
finally: [otelEndSpan, applyCors],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
globalRouter.all(`/_electric/shared-state/*`, durableStreamsRouter.fetch)
|
|
32
|
+
globalRouter.all(`/_electric/*`, internalRouter.fetch)
|
|
33
|
+
globalRouter.all(`*`, durableStreamsRouter.fetch)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { SpanKind, SpanStatusCode } from '@opentelemetry/api'
|
|
2
|
+
import { apiError } from '../electric-agents-http.js'
|
|
3
|
+
import { ElectricAgentsError } from '../entity-manager.js'
|
|
4
|
+
import { ATTR, extractTraceContext, tracer } from '../tracing.js'
|
|
5
|
+
import { serverLog } from '../utils/log.js'
|
|
6
|
+
import type { Span } from '@opentelemetry/api'
|
|
7
|
+
import type { IRequest } from 'itty-router'
|
|
8
|
+
import type { TenantContext } from './context.js'
|
|
9
|
+
|
|
10
|
+
const SPAN_KEY = Symbol(`agents-server.otel-span`)
|
|
11
|
+
|
|
12
|
+
interface SpanCarrier {
|
|
13
|
+
[SPAN_KEY]?: Span
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function headersRecord(
|
|
17
|
+
headers: Headers
|
|
18
|
+
): Record<string, string | Array<string> | undefined> {
|
|
19
|
+
const out: Record<string, string> = {}
|
|
20
|
+
headers.forEach((value, key) => {
|
|
21
|
+
out[key] = value
|
|
22
|
+
})
|
|
23
|
+
return out
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function carrier(req: IRequest): IRequest & SpanCarrier {
|
|
27
|
+
return req as IRequest & SpanCarrier
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function startRequestSpan(req: IRequest, ctx: TenantContext): Span {
|
|
31
|
+
const existing = carrier(req)[SPAN_KEY]
|
|
32
|
+
if (existing) return existing
|
|
33
|
+
|
|
34
|
+
const url = new URL(req.url)
|
|
35
|
+
const parentCtx = extractTraceContext(headersRecord(req.headers))
|
|
36
|
+
const span = tracer.startSpan(
|
|
37
|
+
`HTTP ${req.method}`,
|
|
38
|
+
{
|
|
39
|
+
kind: SpanKind.SERVER,
|
|
40
|
+
attributes: {
|
|
41
|
+
[ATTR.HTTP_METHOD]: req.method,
|
|
42
|
+
[ATTR.HTTP_ROUTE]: url.pathname,
|
|
43
|
+
'electric_agents.tenant_id': ctx.service,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
parentCtx
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
carrier(req)[SPAN_KEY] = span
|
|
50
|
+
return span
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function otelStartSpan(req: IRequest, ctx: TenantContext): undefined {
|
|
54
|
+
startRequestSpan(req, ctx)
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function otelEndSpan(
|
|
59
|
+
response: Response | undefined,
|
|
60
|
+
req: IRequest
|
|
61
|
+
): void {
|
|
62
|
+
const span = carrier(req)[SPAN_KEY]
|
|
63
|
+
if (!span) return
|
|
64
|
+
if (response) {
|
|
65
|
+
span.setAttribute(ATTR.HTTP_STATUS, response.status)
|
|
66
|
+
}
|
|
67
|
+
span.end()
|
|
68
|
+
carrier(req)[SPAN_KEY] = undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function applyCors(
|
|
72
|
+
response: Response | undefined
|
|
73
|
+
): Response | undefined {
|
|
74
|
+
if (!response) return response
|
|
75
|
+
const headers = new Headers(response.headers)
|
|
76
|
+
headers.set(`access-control-allow-origin`, `*`)
|
|
77
|
+
headers.set(
|
|
78
|
+
`access-control-allow-methods`,
|
|
79
|
+
`GET, POST, PUT, PATCH, DELETE, OPTIONS`
|
|
80
|
+
)
|
|
81
|
+
headers.set(
|
|
82
|
+
`access-control-allow-headers`,
|
|
83
|
+
`content-type, authorization, electric-claim-token, x-electric-asserted-email, x-electric-asserted-name, ngrok-skip-browser-warning`
|
|
84
|
+
)
|
|
85
|
+
headers.set(`access-control-expose-headers`, `*`)
|
|
86
|
+
return new Response(response.body, {
|
|
87
|
+
status: response.status,
|
|
88
|
+
statusText: response.statusText,
|
|
89
|
+
headers,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function preflightCors(req: IRequest): Response | undefined {
|
|
94
|
+
if (req.method !== `OPTIONS`) return undefined
|
|
95
|
+
return new Response(null, { status: 204 })
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function errorMapper(err: unknown, req: IRequest): Response {
|
|
99
|
+
const span = carrier(req)[SPAN_KEY]
|
|
100
|
+
if (err instanceof Error) {
|
|
101
|
+
span?.recordException(err)
|
|
102
|
+
span?.setStatus({ code: SpanStatusCode.ERROR, message: err.message })
|
|
103
|
+
}
|
|
104
|
+
if (err instanceof ElectricAgentsError) {
|
|
105
|
+
return apiError(err.status, err.code, err.message, err.details)
|
|
106
|
+
}
|
|
107
|
+
serverLog.error(`[agent-server] Unhandled error:`, err)
|
|
108
|
+
return apiError(500, `INTERNAL_SERVER_ERROR`, `Internal server error`)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function rejectIfShuttingDown(
|
|
112
|
+
req: IRequest,
|
|
113
|
+
ctx: TenantContext
|
|
114
|
+
): Response | undefined {
|
|
115
|
+
if (!ctx.isShuttingDown()) return undefined
|
|
116
|
+
const path = new URL(req.url).pathname
|
|
117
|
+
if (!path.startsWith(`/_electric/webhook-forward/`)) return undefined
|
|
118
|
+
return apiError(503, `SERVER_STOPPING`, `Server is shutting down`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getRequestSpan(req: IRequest): Span | undefined {
|
|
122
|
+
return carrier(req)[SPAN_KEY]
|
|
123
|
+
}
|