@electric-ax/agents-server 0.4.2 → 0.4.3

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/src/server.ts CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  createRuntimeHandler,
8
8
  } from '@electric-ax/agents-runtime'
9
9
  import { createDb, runMigrations } from './db/index.js'
10
- import { pathPrefixedSingleTenantDurableStreamsRoutingAdapter } from './routing/durable-streams-routing-adapter.js'
11
10
  import { ossServerRouter } from './routing/oss-server-router.js'
12
11
  import { startStandaloneAgentsRuntime } from './standalone-runtime.js'
13
12
  import { StreamClient, durableStreamsServiceUrl } from './stream-client.js'
@@ -145,7 +144,9 @@ export class ElectricAgentsServer {
145
144
  this.options = options
146
145
  this.streamClient = options.durableStreamsUrl
147
146
  ? new StreamClient(
148
- durableStreamsServiceUrl(options.durableStreamsUrl, this.tenantId),
147
+ durableStreamsServiceUrl(options.durableStreamsUrl, this.tenantId, {
148
+ scope: `stream-root`,
149
+ }),
149
150
  { bearer: options.durableStreamsBearer }
150
151
  )
151
152
  : null!
@@ -186,7 +187,9 @@ export class ElectricAgentsServer {
186
187
  )
187
188
  this.options.durableStreamsUrl = streamsUrl
188
189
  this.streamClient = new StreamClient(
189
- durableStreamsServiceUrl(streamsUrl, this.tenantId),
190
+ durableStreamsServiceUrl(streamsUrl, this.tenantId, {
191
+ scope: `stream-root`,
192
+ }),
190
193
  { bearer: this.options.durableStreamsBearer }
191
194
  )
192
195
  }
@@ -401,11 +404,9 @@ export class ElectricAgentsServer {
401
404
  principal,
402
405
  publicUrl: this.publicUrl,
403
406
  localUrl: this._url,
404
- durableStreamsUrl: this.options.durableStreamsUrl,
407
+ durableStreamsUrl: this.streamClient.baseUrl,
405
408
  durableStreamsBearer: this.options.durableStreamsBearer,
406
- durableStreamsRouting:
407
- this.options.durableStreamsRouting ??
408
- pathPrefixedSingleTenantDurableStreamsRoutingAdapter,
409
+ durableStreamsRouting: this.options.durableStreamsRouting,
409
410
  durableStreamsDispatcher: this.streamsAgent,
410
411
  electricUrl: this.options.electricUrl,
411
412
  electricSecret: this.options.electricSecret,
@@ -58,7 +58,9 @@ export async function startStandaloneAgentsRuntime(
58
58
  options.streamClient ??
59
59
  (options.durableStreamsUrl
60
60
  ? new StreamClient(
61
- durableStreamsServiceUrl(options.durableStreamsUrl, serviceId),
61
+ durableStreamsServiceUrl(options.durableStreamsUrl, serviceId, {
62
+ scope: `stream-root`,
63
+ }),
62
64
  { bearer: options.durableStreamsBearer }
63
65
  )
64
66
  : undefined)
@@ -14,6 +14,8 @@ export interface StreamClientOptions {
14
14
  bearer?: DurableStreamsBearerProvider
15
15
  }
16
16
 
17
+ type DurableStreamsUrlScope = `service` | `stream-root`
18
+
17
19
  export interface StreamAppendResult {
18
20
  offset: string
19
21
  }
@@ -141,14 +143,29 @@ function durableStreamsBearerHeaders(
141
143
 
142
144
  export function durableStreamsServiceUrl(
143
145
  baseUrl: string,
144
- serviceId: string
146
+ serviceId: string,
147
+ options: { scope?: DurableStreamsUrlScope } = {}
145
148
  ): string {
146
149
  const url = new URL(baseUrl)
147
- if (/^\/v1\/stream\/[^/]+\/?$/.test(url.pathname)) {
150
+ if (/\/v1\/streams\/[^/]+\/?$/.test(url.pathname)) {
151
+ return baseUrl.replace(/\/+$/, ``)
152
+ }
153
+ if (/\/v1\/stream\/[^/]+\/?$/.test(url.pathname)) {
148
154
  return baseUrl.replace(/\/+$/, ``)
149
155
  }
150
- const base = baseUrl.replace(/\/+$/, ``)
151
- return `${base}/v1/stream/${encodeURIComponent(serviceId)}`
156
+ const scope = options.scope ?? `service`
157
+ const encodedServiceId = encodeURIComponent(serviceId)
158
+ const path = url.pathname.replace(/\/+$/, ``) || `/`
159
+ if (path.endsWith(`/v1/streams`)) {
160
+ url.pathname = `${path}/${encodedServiceId}`
161
+ } else if (path.endsWith(`/v1/stream`)) {
162
+ url.pathname = scope === `service` ? `${path}/${encodedServiceId}` : path
163
+ } else if (scope === `stream-root`) {
164
+ url.pathname = `${path === `/` ? `` : path}/v1/stream`
165
+ } else {
166
+ url.pathname = `${path === `/` ? `` : path}/v1/stream/${encodedServiceId}`
167
+ }
168
+ return url.toString().replace(/\/+$/, ``)
152
169
  }
153
170
 
154
171
  function isNotFoundError(err: unknown): boolean {
@@ -202,42 +219,17 @@ export class StreamClient {
202
219
  return headers
203
220
  }
204
221
 
205
- private subscriptionServiceId(): string | null {
206
- const url = new URL(this.baseUrl)
207
- const match = /^(.*)\/v1\/stream\/([^/]+)\/?$/.exec(url.pathname)
208
- return match ? decodeURIComponent(match[2]!) : null
209
- }
210
-
211
222
  private backendSubscriptionPath(path: string): string {
212
- const normalized = normalizeSubscriptionPath(path)
213
- const serviceId = this.subscriptionServiceId()
214
- if (!serviceId) return normalized
215
- if (normalized === serviceId || normalized.startsWith(`${serviceId}/`)) {
216
- return normalized
217
- }
218
- return `${serviceId}/${normalized}`
223
+ return normalizeSubscriptionPath(path)
219
224
  }
220
225
 
221
226
  private runtimeSubscriptionPath(path: string): string {
222
- const normalized = normalizeSubscriptionPath(path)
223
- const serviceId = this.subscriptionServiceId()
224
- if (!serviceId) return normalized
225
- return normalized.startsWith(`${serviceId}/`)
226
- ? normalized.slice(serviceId.length + 1)
227
- : normalized
227
+ return normalizeSubscriptionPath(path)
228
228
  }
229
229
 
230
230
  private subscriptionUrl(subscriptionId: string): string {
231
231
  const url = new URL(this.baseUrl)
232
- const match = /^(.*)\/v1\/stream\/([^/]+)\/?$/.exec(url.pathname)
233
- if (match) {
234
- const [, prefix = ``, serviceId] = match
235
- url.pathname = `${prefix}/v1/stream-meta/subscriptions/${encodeURIComponent(subscriptionId)}`
236
- url.searchParams.set(`service`, decodeURIComponent(serviceId!))
237
- return url.toString()
238
- }
239
-
240
- url.pathname = `${url.pathname.replace(/\/+$/, ``)}/v1/stream-meta/subscriptions/${encodeURIComponent(subscriptionId)}`
232
+ url.pathname = `${url.pathname.replace(/\/+$/, ``)}/__ds/subscriptions/${encodeURIComponent(subscriptionId)}`
241
233
  return url.toString()
242
234
  }
243
235
 
@@ -167,12 +167,13 @@ export async function forwardFetchRequest(options: {
167
167
  serviceId: string
168
168
  body?: Uint8Array
169
169
  dispatcher?: Agent
170
- route?: `stream` | `stream-meta`
170
+ route?: `stream` | `control`
171
171
  durableStreamsBearer?: DurableStreamsBearerProvider
172
172
  durableStreamsBearerMode?: `overwrite` | `if-missing` | `none`
173
173
  }): Promise<Response> {
174
174
  const routingAdapter = resolveDurableStreamsRoutingAdapter(
175
- options.durableStreamsRouting
175
+ options.durableStreamsRouting,
176
+ options.durableStreamsUrl
176
177
  )
177
178
  const routingInput = {
178
179
  durableStreamsUrl: options.durableStreamsUrl,
@@ -180,8 +181,8 @@ export async function forwardFetchRequest(options: {
180
181
  requestUrl: options.request.url,
181
182
  }
182
183
  const upstreamUrl =
183
- options.route === `stream-meta`
184
- ? routingAdapter.streamMetaUrl(routingInput)
184
+ options.route === `control`
185
+ ? routingAdapter.controlUrl(routingInput)
185
186
  : routingAdapter.streamUrl(routingInput)
186
187
 
187
188
  const headers = new Headers(options.request.headers)