@electric-ax/agents-server 0.4.14 → 0.4.16
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/dist/entrypoint.js +1530 -256
- package/dist/index.cjs +1517 -232
- package/dist/index.d.cts +1359 -212
- package/dist/index.d.ts +1359 -212
- package/dist/index.js +1519 -234
- package/drizzle/0010_sandbox_profiles.sql +5 -0
- package/drizzle/0011_entity_permissions.sql +100 -0
- package/drizzle/0012_horton_user_manage_permission.sql +25 -0
- package/drizzle/0013_worker_user_manage_permission.sql +25 -0
- package/drizzle/meta/_journal.json +28 -0
- package/package.json +7 -7
- package/src/db/schema.ts +200 -0
- package/src/electric-agents-types.ts +147 -2
- package/src/entity-bridge-manager.ts +57 -6
- package/src/entity-manager.ts +411 -62
- package/src/entity-projector.ts +79 -17
- package/src/entity-registry.ts +681 -5
- package/src/index.ts +11 -0
- package/src/permissions.ts +239 -0
- package/src/routing/context.ts +2 -0
- package/src/routing/durable-streams-router.ts +125 -4
- package/src/routing/electric-proxy-router.ts +4 -0
- package/src/routing/entities-router.ts +362 -20
- package/src/routing/entity-types-router.ts +244 -15
- package/src/routing/hooks.ts +1 -0
- package/src/routing/observations-router.ts +2 -1
- package/src/routing/runners-router.ts +10 -0
- package/src/routing/sandbox.ts +173 -0
- package/src/runtime.ts +34 -0
- package/src/sandbox-choice-schema.ts +28 -0
- package/src/scheduler.ts +2 -0
- package/src/server.ts +5 -0
- package/src/stream-client.ts +17 -1
- package/src/utils/server-utils.ts +192 -12
- package/src/wake-registry.ts +30 -11
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
assertTags,
|
|
4
4
|
buildTagsIndex,
|
|
5
5
|
getEntitiesStreamPath,
|
|
6
|
+
hashString,
|
|
6
7
|
normalizeTags,
|
|
7
8
|
sourceRefForTags,
|
|
8
9
|
} from '@electric-ax/agents-runtime'
|
|
@@ -13,7 +14,9 @@ import {
|
|
|
13
14
|
} from '@electric-sql/client'
|
|
14
15
|
import { serverLog } from './utils/log.js'
|
|
15
16
|
import { electricUrlWithPath } from './utils/electric-url.js'
|
|
17
|
+
import { buildReadableEntitiesWhere } from './utils/server-utils.js'
|
|
16
18
|
import { DEFAULT_TENANT_ID } from './tenant.js'
|
|
19
|
+
import { isBuiltInSystemPrincipalUrl } from './principal.js'
|
|
17
20
|
import type { EntityBridgeRow, PostgresRegistry } from './entity-registry.js'
|
|
18
21
|
import type { StreamClient } from './stream-client.js'
|
|
19
22
|
import type {
|
|
@@ -30,7 +33,11 @@ import type {
|
|
|
30
33
|
export interface EntityBridgeCoordinator {
|
|
31
34
|
start(): Promise<void>
|
|
32
35
|
stop(): Promise<void>
|
|
33
|
-
register(
|
|
36
|
+
register(
|
|
37
|
+
tagsInput: unknown,
|
|
38
|
+
principalUrl: string,
|
|
39
|
+
principalKind: string
|
|
40
|
+
): Promise<{
|
|
34
41
|
sourceRef: string
|
|
35
42
|
streamUrl: string
|
|
36
43
|
}>
|
|
@@ -115,19 +122,44 @@ function sqlStringLiteral(value: string): string {
|
|
|
115
122
|
|
|
116
123
|
function buildTenantTagsWhereClause(
|
|
117
124
|
tenantId: string,
|
|
118
|
-
tags: EntityTags
|
|
125
|
+
tags: EntityTags,
|
|
126
|
+
principalUrl?: string,
|
|
127
|
+
principalKind?: string,
|
|
128
|
+
permissionBypass?: boolean
|
|
119
129
|
): string {
|
|
120
|
-
|
|
130
|
+
const readableWhere =
|
|
131
|
+
principalUrl && principalKind
|
|
132
|
+
? buildReadableEntitiesWhere({
|
|
133
|
+
tenantId,
|
|
134
|
+
principalUrl,
|
|
135
|
+
principalKind,
|
|
136
|
+
permissionBypass,
|
|
137
|
+
})
|
|
138
|
+
: `tenant_id = ${sqlStringLiteral(tenantId)} AND FALSE`
|
|
139
|
+
return `${readableWhere} AND (${buildTagsWhereClause(tags)})`
|
|
121
140
|
}
|
|
122
141
|
|
|
123
142
|
function shapeEntityKey(message: ChangeMessage<EntityShapeRow>): string {
|
|
124
143
|
return message.value.url
|
|
125
144
|
}
|
|
126
145
|
|
|
146
|
+
function principalScopedSourceRef(
|
|
147
|
+
tagSourceRef: string,
|
|
148
|
+
principalUrl: string,
|
|
149
|
+
principalKind: string
|
|
150
|
+
): string {
|
|
151
|
+
return `${tagSourceRef}-${hashString(
|
|
152
|
+
JSON.stringify({ principalKind, principalUrl })
|
|
153
|
+
)}`
|
|
154
|
+
}
|
|
155
|
+
|
|
127
156
|
class EntityBridge {
|
|
128
157
|
readonly sourceRef: string
|
|
129
158
|
readonly tags: EntityTags
|
|
130
159
|
readonly streamUrl: string
|
|
160
|
+
private readonly principalUrl?: string
|
|
161
|
+
private readonly principalKind?: string
|
|
162
|
+
private readonly permissionBypass: boolean
|
|
131
163
|
|
|
132
164
|
private currentMembers = new Map<string, EntityMembershipRow>()
|
|
133
165
|
private producer: IdempotentProducer | null = null
|
|
@@ -152,6 +184,9 @@ class EntityBridge {
|
|
|
152
184
|
this.sourceRef = row.sourceRef
|
|
153
185
|
this.tags = normalizeTags(row.tags)
|
|
154
186
|
this.streamUrl = row.streamUrl
|
|
187
|
+
this.principalUrl = row.principalUrl
|
|
188
|
+
this.principalKind = row.principalKind
|
|
189
|
+
this.permissionBypass = isBuiltInSystemPrincipalUrl(row.principalUrl)
|
|
155
190
|
this.initialShapeHandle = row.shapeHandle
|
|
156
191
|
this.initialShapeOffset = row.shapeOffset
|
|
157
192
|
}
|
|
@@ -316,7 +351,13 @@ class EntityBridge {
|
|
|
316
351
|
url: electricUrlWithPath(this.electricUrl, `/v1/shape`).toString(),
|
|
317
352
|
params: {
|
|
318
353
|
table: `entities`,
|
|
319
|
-
where: buildTenantTagsWhereClause(
|
|
354
|
+
where: buildTenantTagsWhereClause(
|
|
355
|
+
this.tenantId,
|
|
356
|
+
this.tags,
|
|
357
|
+
this.principalUrl,
|
|
358
|
+
this.principalKind,
|
|
359
|
+
this.permissionBypass
|
|
360
|
+
),
|
|
320
361
|
...(this.electricSecret ? { secret: this.electricSecret } : {}),
|
|
321
362
|
columns: [...ENTITY_SHAPE_COLUMNS],
|
|
322
363
|
replica: `full`,
|
|
@@ -564,7 +605,11 @@ export class EntityBridgeManager implements EntityBridgeCoordinator {
|
|
|
564
605
|
)
|
|
565
606
|
}
|
|
566
607
|
|
|
567
|
-
async register(
|
|
608
|
+
async register(
|
|
609
|
+
tagsInput: unknown,
|
|
610
|
+
principalUrl: string,
|
|
611
|
+
principalKind: string
|
|
612
|
+
): Promise<{
|
|
568
613
|
sourceRef: string
|
|
569
614
|
streamUrl: string
|
|
570
615
|
}> {
|
|
@@ -573,13 +618,19 @@ export class EntityBridgeManager implements EntityBridgeCoordinator {
|
|
|
573
618
|
}
|
|
574
619
|
|
|
575
620
|
const tags = normalizeTags(assertTags(tagsInput))
|
|
576
|
-
const sourceRef =
|
|
621
|
+
const sourceRef = principalScopedSourceRef(
|
|
622
|
+
sourceRefForTags(tags),
|
|
623
|
+
principalUrl,
|
|
624
|
+
principalKind
|
|
625
|
+
)
|
|
577
626
|
const streamUrl = getEntitiesStreamPath(sourceRef)
|
|
578
627
|
|
|
579
628
|
const row = await this.registry.upsertEntityBridge({
|
|
580
629
|
sourceRef,
|
|
581
630
|
tags,
|
|
582
631
|
streamUrl,
|
|
632
|
+
principalUrl,
|
|
633
|
+
principalKind,
|
|
583
634
|
})
|
|
584
635
|
await this.registry.touchEntityBridge(sourceRef)
|
|
585
636
|
await this.ensureBridge(row)
|