@electric-ax/agents-server 0.4.1 → 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/dist/entrypoint.js +178 -157
- package/dist/index.cjs +161 -136
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +159 -136
- package/package.json +5 -5
- package/src/db/index.ts +1 -1
- package/src/index.ts +5 -1
- package/src/routing/context.ts +1 -0
- package/src/routing/durable-streams-router.ts +286 -116
- package/src/routing/durable-streams-routing-adapter.ts +26 -58
- package/src/routing/internal-router.ts +6 -2
- package/src/routing/runners-router.ts +1 -0
- package/src/runtime.ts +3 -1
- package/src/server.ts +8 -7
- package/src/standalone-runtime.ts +3 -1
- package/src/stream-client.ts +24 -32
- package/src/utils/server-utils.ts +5 -4
package/dist/index.cjs
CHANGED
|
@@ -316,7 +316,7 @@ function createDb(postgresUrl) {
|
|
|
316
316
|
const poolMax = Number(process.env.ELECTRIC_AGENTS_PG_POOL_MAX ?? `100`);
|
|
317
317
|
const client = (0, postgres.default)(postgresUrl, {
|
|
318
318
|
max: poolMax,
|
|
319
|
-
fetch_types:
|
|
319
|
+
fetch_types: true
|
|
320
320
|
});
|
|
321
321
|
const db = (0, drizzle_orm_postgres_js.drizzle)(client, { schema: schema_exports });
|
|
322
322
|
return {
|
|
@@ -4206,11 +4206,18 @@ function durableStreamsBearerHeaders(bearer) {
|
|
|
4206
4206
|
if (!bearer) return void 0;
|
|
4207
4207
|
return { authorization: async () => await resolveDurableStreamsBearer(bearer) ?? `` };
|
|
4208
4208
|
}
|
|
4209
|
-
function durableStreamsServiceUrl(baseUrl, serviceId) {
|
|
4209
|
+
function durableStreamsServiceUrl(baseUrl, serviceId, options = {}) {
|
|
4210
4210
|
const url = new URL(baseUrl);
|
|
4211
|
-
if (
|
|
4212
|
-
|
|
4213
|
-
|
|
4211
|
+
if (/\/v1\/streams\/[^/]+\/?$/.test(url.pathname)) return baseUrl.replace(/\/+$/, ``);
|
|
4212
|
+
if (/\/v1\/stream\/[^/]+\/?$/.test(url.pathname)) return baseUrl.replace(/\/+$/, ``);
|
|
4213
|
+
const scope = options.scope ?? `service`;
|
|
4214
|
+
const encodedServiceId = encodeURIComponent(serviceId);
|
|
4215
|
+
const path$2 = url.pathname.replace(/\/+$/, ``) || `/`;
|
|
4216
|
+
if (path$2.endsWith(`/v1/streams`)) url.pathname = `${path$2}/${encodedServiceId}`;
|
|
4217
|
+
else if (path$2.endsWith(`/v1/stream`)) url.pathname = scope === `service` ? `${path$2}/${encodedServiceId}` : path$2;
|
|
4218
|
+
else if (scope === `stream-root`) url.pathname = `${path$2 === `/` ? `` : path$2}/v1/stream`;
|
|
4219
|
+
else url.pathname = `${path$2 === `/` ? `` : path$2}/v1/stream/${encodedServiceId}`;
|
|
4220
|
+
return url.toString().replace(/\/+$/, ``);
|
|
4214
4221
|
}
|
|
4215
4222
|
function isNotFoundError(err) {
|
|
4216
4223
|
return err instanceof __durable_streams_client.DurableStreamError && err.code === ErrCodeNotFound || err instanceof __durable_streams_client.FetchError && err.status === 404;
|
|
@@ -4243,34 +4250,15 @@ var StreamClient = class {
|
|
|
4243
4250
|
await applyDurableStreamsBearer(headers, this.options.bearer, { overwrite: opts.overwriteBearer });
|
|
4244
4251
|
return headers;
|
|
4245
4252
|
}
|
|
4246
|
-
subscriptionServiceId() {
|
|
4247
|
-
const url = new URL(this.baseUrl);
|
|
4248
|
-
const match = /^(.*)\/v1\/stream\/([^/]+)\/?$/.exec(url.pathname);
|
|
4249
|
-
return match ? decodeURIComponent(match[2]) : null;
|
|
4250
|
-
}
|
|
4251
4253
|
backendSubscriptionPath(path$2) {
|
|
4252
|
-
|
|
4253
|
-
const serviceId = this.subscriptionServiceId();
|
|
4254
|
-
if (!serviceId) return normalized;
|
|
4255
|
-
if (normalized === serviceId || normalized.startsWith(`${serviceId}/`)) return normalized;
|
|
4256
|
-
return `${serviceId}/${normalized}`;
|
|
4254
|
+
return normalizeSubscriptionPath(path$2);
|
|
4257
4255
|
}
|
|
4258
4256
|
runtimeSubscriptionPath(path$2) {
|
|
4259
|
-
|
|
4260
|
-
const serviceId = this.subscriptionServiceId();
|
|
4261
|
-
if (!serviceId) return normalized;
|
|
4262
|
-
return normalized.startsWith(`${serviceId}/`) ? normalized.slice(serviceId.length + 1) : normalized;
|
|
4257
|
+
return normalizeSubscriptionPath(path$2);
|
|
4263
4258
|
}
|
|
4264
4259
|
subscriptionUrl(subscriptionId) {
|
|
4265
4260
|
const url = new URL(this.baseUrl);
|
|
4266
|
-
|
|
4267
|
-
if (match) {
|
|
4268
|
-
const [, prefix = ``, serviceId] = match;
|
|
4269
|
-
url.pathname = `${prefix}/v1/stream-meta/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
4270
|
-
url.searchParams.set(`service`, decodeURIComponent(serviceId));
|
|
4271
|
-
return url.toString();
|
|
4272
|
-
}
|
|
4273
|
-
url.pathname = `${url.pathname.replace(/\/+$/, ``)}/v1/stream-meta/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
4261
|
+
url.pathname = `${url.pathname.replace(/\/+$/, ``)}/__ds/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
4274
4262
|
return url.toString();
|
|
4275
4263
|
}
|
|
4276
4264
|
subscriptionChildUrl(subscriptionId, ...segments) {
|
|
@@ -4685,7 +4673,7 @@ var ElectricAgentsTenantRuntime = class {
|
|
|
4685
4673
|
this.service = this.serviceId;
|
|
4686
4674
|
this.db = options.db;
|
|
4687
4675
|
if (options.streamClient) this.streamClient = options.streamClient;
|
|
4688
|
-
else if (options.durableStreamsUrl) this.streamClient = new StreamClient(durableStreamsServiceUrl(options.durableStreamsUrl, this.serviceId), { bearer: options.durableStreamsBearer });
|
|
4676
|
+
else if (options.durableStreamsUrl) this.streamClient = new StreamClient(durableStreamsServiceUrl(options.durableStreamsUrl, this.serviceId, { scope: `stream-root` }), { bearer: options.durableStreamsBearer });
|
|
4689
4677
|
else throw new Error(`Either durableStreamsUrl or streamClient is required`);
|
|
4690
4678
|
this.registry = options.registry ?? new PostgresRegistry(this.db, this.serviceId);
|
|
4691
4679
|
this.wakeRegistry = options.wakeRegistry;
|
|
@@ -5954,27 +5942,6 @@ function validateParsedBody(schema, parsed) {
|
|
|
5954
5942
|
};
|
|
5955
5943
|
}
|
|
5956
5944
|
|
|
5957
|
-
//#endregion
|
|
5958
|
-
//#region src/routing/tenant-stream-paths.ts
|
|
5959
|
-
function withoutLeadingSlash(path$2) {
|
|
5960
|
-
return path$2.replace(/^\/+/, ``);
|
|
5961
|
-
}
|
|
5962
|
-
function withLeadingSlash(path$2) {
|
|
5963
|
-
return path$2.startsWith(`/`) ? path$2 : `/${path$2}`;
|
|
5964
|
-
}
|
|
5965
|
-
function prefixTenantStreamPath(path$2, tenantId) {
|
|
5966
|
-
const normalized = withoutLeadingSlash(path$2);
|
|
5967
|
-
if (!normalized || normalized === tenantId) return tenantId;
|
|
5968
|
-
if (normalized.startsWith(`${tenantId}/`)) return normalized;
|
|
5969
|
-
return `${tenantId}/${normalized}`;
|
|
5970
|
-
}
|
|
5971
|
-
function stripTenantStreamPrefix(path$2, tenantId) {
|
|
5972
|
-
const normalized = withoutLeadingSlash(path$2);
|
|
5973
|
-
if (normalized === tenantId) return ``;
|
|
5974
|
-
if (normalized.startsWith(`${tenantId}/`)) return normalized.slice(tenantId.length + 1);
|
|
5975
|
-
return normalized;
|
|
5976
|
-
}
|
|
5977
|
-
|
|
5978
5945
|
//#endregion
|
|
5979
5946
|
//#region src/routing/durable-streams-routing-adapter.ts
|
|
5980
5947
|
function appendSearch(target, source) {
|
|
@@ -5985,43 +5952,30 @@ function removeServiceQuery(target) {
|
|
|
5985
5952
|
target.searchParams.delete(`service`);
|
|
5986
5953
|
return target;
|
|
5987
5954
|
}
|
|
5988
|
-
function
|
|
5989
|
-
|
|
5990
|
-
const segments = incomingUrl.pathname.split(`/`).filter(Boolean);
|
|
5991
|
-
if (segments[0] === `v1` && segments[1] === `stream`) return {
|
|
5992
|
-
incomingUrl,
|
|
5993
|
-
streamPath: segments.length > 2 ? `/${segments.slice(3).join(`/`)}` : `/`
|
|
5994
|
-
};
|
|
5995
|
-
return {
|
|
5996
|
-
incomingUrl,
|
|
5997
|
-
streamPath: incomingUrl.pathname || `/${serviceId}`
|
|
5998
|
-
};
|
|
5955
|
+
function withoutTrailingSlash(pathname) {
|
|
5956
|
+
return pathname.replace(/\/+$/, ``) || `/`;
|
|
5999
5957
|
}
|
|
6000
|
-
function
|
|
6001
|
-
const path$2 = backendStreamPath.replace(/^\/+/, ``);
|
|
6002
|
-
const target = new URL(`/v1/stream/${path$2}`, input.durableStreamsUrl);
|
|
6003
|
-
return target;
|
|
6004
|
-
}
|
|
6005
|
-
function streamMetaUrlWithoutService(input) {
|
|
5958
|
+
function appendRequestPathToStreamRoot(input) {
|
|
6006
5959
|
const incomingUrl = new URL(input.requestUrl, `http://localhost`);
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
return prefixTenantStreamPath(streamPath, serviceId);
|
|
5960
|
+
const path$2 = incomingUrl.pathname.replace(/^\/+/, ``);
|
|
5961
|
+
const target = new URL(input.durableStreamsUrl);
|
|
5962
|
+
target.pathname = path$2 ? `${withoutTrailingSlash(target.pathname)}/${path$2}` : withoutTrailingSlash(target.pathname);
|
|
5963
|
+
return removeServiceQuery(appendSearch(target, incomingUrl));
|
|
5964
|
+
}
|
|
5965
|
+
const streamRootDurableStreamsRoutingAdapter = {
|
|
5966
|
+
streamUrl: appendRequestPathToStreamRoot,
|
|
5967
|
+
controlUrl: appendRequestPathToStreamRoot,
|
|
5968
|
+
toBackendStreamPath(_serviceId, streamPath) {
|
|
5969
|
+
return streamPath.replace(/^\/+/, ``);
|
|
6018
5970
|
},
|
|
6019
|
-
toRuntimeStreamPath(
|
|
6020
|
-
return
|
|
5971
|
+
toRuntimeStreamPath(_serviceId, streamPath) {
|
|
5972
|
+
return streamPath.replace(/^\/+/, ``);
|
|
6021
5973
|
}
|
|
6022
5974
|
};
|
|
6023
|
-
|
|
6024
|
-
|
|
5975
|
+
const pathPrefixedSingleTenantDurableStreamsRoutingAdapter = streamRootDurableStreamsRoutingAdapter;
|
|
5976
|
+
const tenantRootDurableStreamsRoutingAdapter = streamRootDurableStreamsRoutingAdapter;
|
|
5977
|
+
function resolveDurableStreamsRoutingAdapter(adapter, _durableStreamsUrl) {
|
|
5978
|
+
return adapter ?? streamRootDurableStreamsRoutingAdapter;
|
|
6025
5979
|
}
|
|
6026
5980
|
|
|
6027
5981
|
//#endregion
|
|
@@ -6058,13 +6012,13 @@ function buildElectricProxyTarget(options) {
|
|
|
6058
6012
|
return target;
|
|
6059
6013
|
}
|
|
6060
6014
|
async function forwardFetchRequest(options) {
|
|
6061
|
-
const routingAdapter = resolveDurableStreamsRoutingAdapter(options.durableStreamsRouting);
|
|
6015
|
+
const routingAdapter = resolveDurableStreamsRoutingAdapter(options.durableStreamsRouting, options.durableStreamsUrl);
|
|
6062
6016
|
const routingInput = {
|
|
6063
6017
|
durableStreamsUrl: options.durableStreamsUrl,
|
|
6064
6018
|
serviceId: options.serviceId,
|
|
6065
6019
|
requestUrl: options.request.url
|
|
6066
6020
|
};
|
|
6067
|
-
const upstreamUrl = options.route === `
|
|
6021
|
+
const upstreamUrl = options.route === `control` ? routingAdapter.controlUrl(routingInput) : routingAdapter.streamUrl(routingInput);
|
|
6068
6022
|
const headers = new Headers(options.request.headers);
|
|
6069
6023
|
if (options.durableStreamsBearerMode !== `none`) await applyDurableStreamsBearer(headers, options.durableStreamsBearer, { overwrite: options.durableStreamsBearerMode !== `if-missing` });
|
|
6070
6024
|
const init = {
|
|
@@ -6102,8 +6056,21 @@ function sqlStringLiteral(value) {
|
|
|
6102
6056
|
//#endregion
|
|
6103
6057
|
//#region src/routing/durable-streams-router.ts
|
|
6104
6058
|
const subscriptionProxyBodySchema = __sinclair_typebox.Type.Object({ webhook: __sinclair_typebox.Type.Optional(__sinclair_typebox.Type.Object({ url: __sinclair_typebox.Type.String() }, { additionalProperties: true })) }, { additionalProperties: true });
|
|
6059
|
+
const subscriptionControlActions = [
|
|
6060
|
+
`callback`,
|
|
6061
|
+
`claim`,
|
|
6062
|
+
`ack`,
|
|
6063
|
+
`release`
|
|
6064
|
+
];
|
|
6105
6065
|
const durableStreamsRouter = (0, itty_router.Router)();
|
|
6106
|
-
durableStreamsRouter.
|
|
6066
|
+
durableStreamsRouter.put(`/__ds/subscriptions/:subscriptionId`, putSubscriptionBase);
|
|
6067
|
+
durableStreamsRouter.get(`/__ds/subscriptions/:subscriptionId`, getSubscriptionBase);
|
|
6068
|
+
durableStreamsRouter.delete(`/__ds/subscriptions/:subscriptionId`, deleteSubscriptionBase);
|
|
6069
|
+
durableStreamsRouter.post(`/__ds/subscriptions/:subscriptionId/streams`, postSubscriptionStreams);
|
|
6070
|
+
durableStreamsRouter.delete(`/__ds/subscriptions/:subscriptionId/streams/:streamPath+`, deleteSubscriptionStream);
|
|
6071
|
+
for (const action of subscriptionControlActions) durableStreamsRouter.post(`/__ds/subscriptions/:subscriptionId/${action}`, subscriptionAction(action));
|
|
6072
|
+
durableStreamsRouter.all(`/__ds`, controlPassThrough);
|
|
6073
|
+
durableStreamsRouter.all(`/__ds/*`, controlPassThrough);
|
|
6107
6074
|
durableStreamsRouter.post(`*`, streamAppend);
|
|
6108
6075
|
durableStreamsRouter.all(`*`, proxyPassThrough);
|
|
6109
6076
|
function bodyFromBytes$1(body) {
|
|
@@ -6116,7 +6083,7 @@ function responseFromUpstream$1(response, body) {
|
|
|
6116
6083
|
headers: responseHeaders(response)
|
|
6117
6084
|
});
|
|
6118
6085
|
}
|
|
6119
|
-
async function forwardToDurableStreams(ctx, request, body, route = `stream`, urlOverride) {
|
|
6086
|
+
async function forwardToDurableStreams(ctx, request, body, route = `stream`, urlOverride, durableStreamsBearerMode = `overwrite`) {
|
|
6120
6087
|
const headers = new Headers(request.headers);
|
|
6121
6088
|
headers.delete(`host`);
|
|
6122
6089
|
let requestBody = body;
|
|
@@ -6130,24 +6097,13 @@ async function forwardToDurableStreams(ctx, request, body, route = `stream`, url
|
|
|
6130
6097
|
body: requestBody,
|
|
6131
6098
|
durableStreamsUrl: ctx.durableStreamsUrl,
|
|
6132
6099
|
durableStreamsBearer: ctx.durableStreamsBearer,
|
|
6133
|
-
durableStreamsBearerMode
|
|
6100
|
+
durableStreamsBearerMode,
|
|
6134
6101
|
durableStreamsRouting: ctx.durableStreamsRouting,
|
|
6135
6102
|
serviceId: ctx.service,
|
|
6136
6103
|
dispatcher: ctx.durableStreamsDispatcher,
|
|
6137
6104
|
route
|
|
6138
6105
|
});
|
|
6139
6106
|
}
|
|
6140
|
-
function subscriptionIdFromPath(pathname) {
|
|
6141
|
-
const match = /^\/v1\/stream-meta\/subscriptions\/([^/]+)(?:\/.*)?$/.exec(pathname);
|
|
6142
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
6143
|
-
}
|
|
6144
|
-
function isSubscriptionBasePath(pathname) {
|
|
6145
|
-
return /^\/v1\/stream-meta\/subscriptions\/[^/]+\/?$/.test(pathname);
|
|
6146
|
-
}
|
|
6147
|
-
function usesSubscriptionScopedBearer(requestUrl) {
|
|
6148
|
-
const pathname = new URL(requestUrl, `http://localhost`).pathname;
|
|
6149
|
-
return /^\/v1\/stream-meta\/subscriptions\/[^/]+\/(?:ack|release|callback)\/?$/.test(pathname);
|
|
6150
|
-
}
|
|
6151
6107
|
function rewriteSubscriptionBodyForBackend(payload, service, routingAdapter) {
|
|
6152
6108
|
if (typeof payload.pattern === `string`) payload.pattern = routingAdapter.toBackendStreamPath(service, payload.pattern);
|
|
6153
6109
|
if (Array.isArray(payload.streams)) payload.streams = payload.streams.map((stream) => typeof stream === `string` ? routingAdapter.toBackendStreamPath(service, stream) : stream);
|
|
@@ -6192,44 +6148,50 @@ function decodeJson(bytes) {
|
|
|
6192
6148
|
return null;
|
|
6193
6149
|
}
|
|
6194
6150
|
}
|
|
6195
|
-
function
|
|
6196
|
-
const
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
return
|
|
6151
|
+
function routeParam$2(request, name) {
|
|
6152
|
+
const value = request.params[name];
|
|
6153
|
+
const raw = Array.isArray(value) ? value[0] : value;
|
|
6154
|
+
return decodeURIComponent(raw ?? ``);
|
|
6155
|
+
}
|
|
6156
|
+
function subscriptionRoutingAdapter(ctx) {
|
|
6157
|
+
return resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting, ctx.durableStreamsUrl);
|
|
6202
6158
|
}
|
|
6203
|
-
async function
|
|
6204
|
-
const
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6159
|
+
async function rewriteSubscriptionRequestBody(request, ctx, subscriptionId, routingAdapter) {
|
|
6160
|
+
const body = await readRequestBody(request);
|
|
6161
|
+
if (body.length === 0) return {
|
|
6162
|
+
ok: true,
|
|
6163
|
+
body,
|
|
6164
|
+
targetWebhookUrl: null
|
|
6165
|
+
};
|
|
6166
|
+
const validation = validateBody(subscriptionProxyBodySchema, body);
|
|
6167
|
+
if (!validation.ok) return {
|
|
6168
|
+
ok: false,
|
|
6169
|
+
response: validation.response
|
|
6170
|
+
};
|
|
6171
|
+
const payload = validation.value;
|
|
6209
6172
|
let targetWebhookUrl = null;
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
if (requestBody.length > 0) {
|
|
6214
|
-
const validation = validateBody(subscriptionProxyBodySchema, requestBody);
|
|
6215
|
-
if (!validation.ok) return validation.response;
|
|
6216
|
-
const payload = validation.value;
|
|
6217
|
-
if (payload.webhook?.url !== void 0) {
|
|
6218
|
-
targetWebhookUrl = rewriteLoopbackWebhookUrl(payload.webhook.url) ?? null;
|
|
6219
|
-
payload.webhook.url = (0, __electric_ax_agents_runtime.appendPathToUrl)(ctx.publicUrl, `/_electric/webhook-forward/${encodeURIComponent(subscriptionId)}`);
|
|
6220
|
-
}
|
|
6221
|
-
rewriteSubscriptionBodyForBackend(payload, ctx.service, routingAdapter);
|
|
6222
|
-
requestBody = new TextEncoder().encode(JSON.stringify(payload));
|
|
6223
|
-
}
|
|
6173
|
+
if (payload.webhook?.url !== void 0) {
|
|
6174
|
+
targetWebhookUrl = rewriteLoopbackWebhookUrl(payload.webhook.url) ?? null;
|
|
6175
|
+
payload.webhook.url = (0, __electric_ax_agents_runtime.appendPathToUrl)(ctx.publicUrl, `/_electric/webhook-forward/${encodeURIComponent(subscriptionId)}`);
|
|
6224
6176
|
}
|
|
6225
|
-
|
|
6226
|
-
|
|
6177
|
+
rewriteSubscriptionBodyForBackend(payload, ctx.service, routingAdapter);
|
|
6178
|
+
return {
|
|
6179
|
+
ok: true,
|
|
6180
|
+
body: new TextEncoder().encode(JSON.stringify(payload)),
|
|
6181
|
+
targetWebhookUrl
|
|
6182
|
+
};
|
|
6183
|
+
}
|
|
6184
|
+
async function forwardSubscriptionRequest(request, ctx, routingAdapter, opts = {}) {
|
|
6185
|
+
const upstream = await forwardToDurableStreams(ctx, request, opts.body, `control`, opts.requestUrl, opts.bearerMode ?? `overwrite`);
|
|
6227
6186
|
let responseBytes = upstream.body ? new Uint8Array(await upstream.arrayBuffer()) : new Uint8Array();
|
|
6228
6187
|
responseBytes = rewriteSubscriptionResponseForClient(responseBytes, upstream, ctx.service, routingAdapter);
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6188
|
+
return {
|
|
6189
|
+
upstream,
|
|
6190
|
+
response: responseFromUpstream$1(upstream, responseBytes)
|
|
6191
|
+
};
|
|
6192
|
+
}
|
|
6193
|
+
async function upsertSubscriptionWebhook(ctx, subscriptionId, targetWebhookUrl) {
|
|
6194
|
+
await ctx.pgDb.insert(subscriptionWebhooks).values({
|
|
6233
6195
|
tenantId: ctx.service,
|
|
6234
6196
|
subscriptionId,
|
|
6235
6197
|
webhookUrl: targetWebhookUrl
|
|
@@ -6237,8 +6199,64 @@ async function subscriptionProxy(request, ctx) {
|
|
|
6237
6199
|
target: [subscriptionWebhooks.tenantId, subscriptionWebhooks.subscriptionId],
|
|
6238
6200
|
set: { webhookUrl: targetWebhookUrl }
|
|
6239
6201
|
});
|
|
6202
|
+
}
|
|
6203
|
+
async function deleteSubscriptionWebhook(ctx, subscriptionId) {
|
|
6204
|
+
await ctx.pgDb.delete(subscriptionWebhooks).where((0, drizzle_orm.and)((0, drizzle_orm.eq)(subscriptionWebhooks.tenantId, ctx.service), (0, drizzle_orm.eq)(subscriptionWebhooks.subscriptionId, subscriptionId)));
|
|
6205
|
+
}
|
|
6206
|
+
function rewriteSubscriptionStreamPathInUrl(requestUrl, service, routingAdapter, streamPath) {
|
|
6207
|
+
const prefix = requestUrl.pathname.slice(0, requestUrl.pathname.indexOf(`/streams/`) + `/streams/`.length);
|
|
6208
|
+
requestUrl.pathname = `${prefix}${encodeURIComponent(routingAdapter.toBackendStreamPath(service, streamPath))}`;
|
|
6209
|
+
return requestUrl.toString();
|
|
6210
|
+
}
|
|
6211
|
+
async function putSubscriptionBase(request, ctx) {
|
|
6212
|
+
const subscriptionId = routeParam$2(request, `subscriptionId`);
|
|
6213
|
+
const routingAdapter = subscriptionRoutingAdapter(ctx);
|
|
6214
|
+
const rewrite = await rewriteSubscriptionRequestBody(request, ctx, subscriptionId, routingAdapter);
|
|
6215
|
+
if (!rewrite.ok) return rewrite.response;
|
|
6216
|
+
const { upstream, response } = await forwardSubscriptionRequest(request, ctx, routingAdapter, { body: rewrite.body });
|
|
6217
|
+
if (upstream.ok && rewrite.targetWebhookUrl) await upsertSubscriptionWebhook(ctx, subscriptionId, rewrite.targetWebhookUrl);
|
|
6218
|
+
return response;
|
|
6219
|
+
}
|
|
6220
|
+
async function getSubscriptionBase(request, ctx) {
|
|
6221
|
+
const routingAdapter = subscriptionRoutingAdapter(ctx);
|
|
6222
|
+
return (await forwardSubscriptionRequest(request, ctx, routingAdapter)).response;
|
|
6223
|
+
}
|
|
6224
|
+
async function deleteSubscriptionBase(request, ctx) {
|
|
6225
|
+
const subscriptionId = routeParam$2(request, `subscriptionId`);
|
|
6226
|
+
const routingAdapter = subscriptionRoutingAdapter(ctx);
|
|
6227
|
+
const { upstream, response } = await forwardSubscriptionRequest(request, ctx, routingAdapter);
|
|
6228
|
+
if (upstream.ok) await deleteSubscriptionWebhook(ctx, subscriptionId);
|
|
6240
6229
|
return response;
|
|
6241
6230
|
}
|
|
6231
|
+
async function postSubscriptionStreams(request, ctx) {
|
|
6232
|
+
const subscriptionId = routeParam$2(request, `subscriptionId`);
|
|
6233
|
+
const routingAdapter = subscriptionRoutingAdapter(ctx);
|
|
6234
|
+
const rewrite = await rewriteSubscriptionRequestBody(request, ctx, subscriptionId, routingAdapter);
|
|
6235
|
+
if (!rewrite.ok) return rewrite.response;
|
|
6236
|
+
return (await forwardSubscriptionRequest(request, ctx, routingAdapter, { body: rewrite.body })).response;
|
|
6237
|
+
}
|
|
6238
|
+
async function deleteSubscriptionStream(request, ctx) {
|
|
6239
|
+
const routingAdapter = subscriptionRoutingAdapter(ctx);
|
|
6240
|
+
const requestUrl = rewriteSubscriptionStreamPathInUrl(new URL(request.url), ctx.service, routingAdapter, routeParam$2(request, `streamPath`));
|
|
6241
|
+
return (await forwardSubscriptionRequest(request, ctx, routingAdapter, { requestUrl })).response;
|
|
6242
|
+
}
|
|
6243
|
+
function subscriptionAction(action) {
|
|
6244
|
+
return async (request, ctx) => {
|
|
6245
|
+
const subscriptionId = routeParam$2(request, `subscriptionId`);
|
|
6246
|
+
const routingAdapter = subscriptionRoutingAdapter(ctx);
|
|
6247
|
+
const rewrite = await rewriteSubscriptionRequestBody(request, ctx, subscriptionId, routingAdapter);
|
|
6248
|
+
if (!rewrite.ok) return rewrite.response;
|
|
6249
|
+
const bearerMode = action === `ack` || action === `release` || action === `callback` ? `if-missing` : `overwrite`;
|
|
6250
|
+
return (await forwardSubscriptionRequest(request, ctx, routingAdapter, {
|
|
6251
|
+
body: rewrite.body,
|
|
6252
|
+
bearerMode
|
|
6253
|
+
})).response;
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
6256
|
+
async function controlPassThrough(request, ctx) {
|
|
6257
|
+
const upstream = await forwardToDurableStreams(ctx, request, void 0, `control`);
|
|
6258
|
+
return responseFromUpstream$1(upstream);
|
|
6259
|
+
}
|
|
6242
6260
|
async function streamAppend(request, ctx) {
|
|
6243
6261
|
return await electricAgentsStreamAppendRouter.fetch(createStreamAppendRouteRequest(request), ctx.runtime, (req, body) => forwardFetchRequest({
|
|
6244
6262
|
request: {
|
|
@@ -6259,10 +6277,9 @@ async function proxyPassThrough(request, ctx) {
|
|
|
6259
6277
|
const upstream = await forwardToDurableStreams(ctx, request);
|
|
6260
6278
|
const streamPath = new URL(request.url).pathname;
|
|
6261
6279
|
const method = request.method.toUpperCase();
|
|
6262
|
-
const
|
|
6263
|
-
const endTrackedRead = method === `GET` && !isControlPath ? await ctx.entityBridgeManager.beginClientRead(streamPath) : null;
|
|
6280
|
+
const endTrackedRead = method === `GET` ? await ctx.entityBridgeManager.beginClientRead(streamPath) : null;
|
|
6264
6281
|
try {
|
|
6265
|
-
if (method === `HEAD`
|
|
6282
|
+
if (method === `HEAD`) await ctx.entityBridgeManager.touchByStreamPath(streamPath);
|
|
6266
6283
|
return responseFromUpstream$1(upstream);
|
|
6267
6284
|
} finally {
|
|
6268
6285
|
await endTrackedRead?.();
|
|
@@ -6809,6 +6826,12 @@ function getRequestSpan(req) {
|
|
|
6809
6826
|
return carrier(req)[SPAN_KEY];
|
|
6810
6827
|
}
|
|
6811
6828
|
|
|
6829
|
+
//#endregion
|
|
6830
|
+
//#region src/routing/tenant-stream-paths.ts
|
|
6831
|
+
function withLeadingSlash(path$2) {
|
|
6832
|
+
return path$2.startsWith(`/`) ? path$2 : `/${path$2}`;
|
|
6833
|
+
}
|
|
6834
|
+
|
|
6812
6835
|
//#endregion
|
|
6813
6836
|
//#region src/routing/runners-router.ts
|
|
6814
6837
|
const registerRunnerBodySchema = __sinclair_typebox.Type.Object({
|
|
@@ -7132,7 +7155,7 @@ async function webhookForward(request, ctx) {
|
|
|
7132
7155
|
let runningEntityUrl = null;
|
|
7133
7156
|
const parsedBody = parsedBodyResult.value;
|
|
7134
7157
|
const newWebhook = newWebhookPayload(parsedBody);
|
|
7135
|
-
const routingAdapter = resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting);
|
|
7158
|
+
const routingAdapter = resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting, ctx.durableStreamsUrl);
|
|
7136
7159
|
if (parsedBody) {
|
|
7137
7160
|
const rawPrimaryStream = newWebhook?.primaryStream ?? parsedBody.primary_stream ?? parsedBody.primaryStream ?? parsedBody.streamPath ?? null;
|
|
7138
7161
|
const primaryStream = typeof rawPrimaryStream === `string` ? toRuntimeStreamPath(rawPrimaryStream, ctx.service, routingAdapter) : null;
|
|
@@ -7261,7 +7284,7 @@ async function callbackForward(request, ctx) {
|
|
|
7261
7284
|
}
|
|
7262
7285
|
return (0, itty_router.json)(responseBody);
|
|
7263
7286
|
}
|
|
7264
|
-
const upstreamBody = encodeCallbackForwardBody(ctx.service, consumerId, requestBody, resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting));
|
|
7287
|
+
const upstreamBody = encodeCallbackForwardBody(ctx.service, consumerId, requestBody, resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting, ctx.durableStreamsUrl));
|
|
7265
7288
|
let upstream;
|
|
7266
7289
|
try {
|
|
7267
7290
|
const subscriptionId = durableStreamsSubscriptionCallback(target.callbackUrl);
|
|
@@ -7380,4 +7403,6 @@ exports.createDb = createDb
|
|
|
7380
7403
|
exports.globalRouter = globalRouter
|
|
7381
7404
|
exports.isUnregisteredTenantError = isUnregisteredTenantError
|
|
7382
7405
|
exports.pathPrefixedSingleTenantDurableStreamsRoutingAdapter = pathPrefixedSingleTenantDurableStreamsRoutingAdapter
|
|
7383
|
-
exports.runMigrations = runMigrations
|
|
7406
|
+
exports.runMigrations = runMigrations
|
|
7407
|
+
exports.streamRootDurableStreamsRoutingAdapter = streamRootDurableStreamsRoutingAdapter
|
|
7408
|
+
exports.tenantRootDurableStreamsRoutingAdapter = tenantRootDurableStreamsRoutingAdapter
|
package/dist/index.d.cts
CHANGED
|
@@ -3551,7 +3551,6 @@ declare class StreamClient {
|
|
|
3551
3551
|
private streamUrl;
|
|
3552
3552
|
private streamHeaders;
|
|
3553
3553
|
private requestHeaders;
|
|
3554
|
-
private subscriptionServiceId;
|
|
3555
3554
|
private backendSubscriptionPath;
|
|
3556
3555
|
private runtimeSubscriptionPath;
|
|
3557
3556
|
private subscriptionUrl;
|
|
@@ -4251,11 +4250,13 @@ interface DurableStreamsRoutingInput {
|
|
|
4251
4250
|
}
|
|
4252
4251
|
interface DurableStreamsRoutingAdapter {
|
|
4253
4252
|
streamUrl(input: DurableStreamsRoutingInput): URL;
|
|
4254
|
-
|
|
4253
|
+
controlUrl(input: DurableStreamsRoutingInput): URL;
|
|
4255
4254
|
toBackendStreamPath(serviceId: string, streamPath: string): string;
|
|
4256
4255
|
toRuntimeStreamPath(serviceId: string, streamPath: string): string;
|
|
4257
4256
|
}
|
|
4257
|
+
declare const streamRootDurableStreamsRoutingAdapter: DurableStreamsRoutingAdapter;
|
|
4258
4258
|
declare const pathPrefixedSingleTenantDurableStreamsRoutingAdapter: DurableStreamsRoutingAdapter;
|
|
4259
|
+
declare const tenantRootDurableStreamsRoutingAdapter: DurableStreamsRoutingAdapter;
|
|
4259
4260
|
|
|
4260
4261
|
//#endregion
|
|
4261
4262
|
//#region src/routing/context.d.ts
|
|
@@ -4270,6 +4271,7 @@ interface TenantContext {
|
|
|
4270
4271
|
principal: Principal;
|
|
4271
4272
|
publicUrl: string;
|
|
4272
4273
|
localUrl?: string;
|
|
4274
|
+
/** Resolved Durable Streams root URL for this tenant. */
|
|
4273
4275
|
durableStreamsUrl: string;
|
|
4274
4276
|
durableStreamsBearer?: DurableStreamsBearerProvider;
|
|
4275
4277
|
durableStreamsRouting?: DurableStreamsRoutingAdapter;
|
|
@@ -4301,4 +4303,4 @@ declare class UnregisteredTenantError extends Error {
|
|
|
4301
4303
|
declare function isUnregisteredTenantError(error: unknown): error is UnregisteredTenantError;
|
|
4302
4304
|
|
|
4303
4305
|
//#endregion
|
|
4304
|
-
export { AgentsHost, AgentsHostOptions, AgentsHostTenantConfig, AgentsHostTenantRuntime, AuthenticateRequest, ConsumerClaim, DEFAULT_TENANT_ID, DispatchPolicy, DispatchTarget, DrizzleDB, DurableStreamsBearerProvider, DurableStreamsRoutingAdapter, DurableStreamsRoutingInput, ElectricAgentsRunner, ElectricAgentsUser, EntityBridgeCoordinator, EntityDispatchState, GlobalRoutes, PgClient, Principal, PrincipalKind, PublicWakeNotification, RegisterRunnerRequest, RequestPrincipal, RunnerAdminStatus, RunnerHeartbeatRequest, RunnerKind, RunnerLiveness, SourceStreamOffset, StreamClient, StreamClientOptions, SubscriptionClaimResponse, SubscriptionCreateInput, SubscriptionResponse, SubscriptionStreamInfo, TenantContext, UnregisteredTenantError, WakeNotificationRow, createDb, globalRouter, isUnregisteredTenantError, pathPrefixedSingleTenantDurableStreamsRoutingAdapter, runMigrations };
|
|
4306
|
+
export { AgentsHost, AgentsHostOptions, AgentsHostTenantConfig, AgentsHostTenantRuntime, AuthenticateRequest, ConsumerClaim, DEFAULT_TENANT_ID, DispatchPolicy, DispatchTarget, DrizzleDB, DurableStreamsBearerProvider, DurableStreamsRoutingAdapter, DurableStreamsRoutingInput, ElectricAgentsRunner, ElectricAgentsUser, EntityBridgeCoordinator, EntityDispatchState, GlobalRoutes, PgClient, Principal, PrincipalKind, PublicWakeNotification, RegisterRunnerRequest, RequestPrincipal, RunnerAdminStatus, RunnerHeartbeatRequest, RunnerKind, RunnerLiveness, SourceStreamOffset, StreamClient, StreamClientOptions, SubscriptionClaimResponse, SubscriptionCreateInput, SubscriptionResponse, SubscriptionStreamInfo, TenantContext, UnregisteredTenantError, WakeNotificationRow, createDb, globalRouter, isUnregisteredTenantError, pathPrefixedSingleTenantDurableStreamsRoutingAdapter, runMigrations, streamRootDurableStreamsRoutingAdapter, tenantRootDurableStreamsRoutingAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -3552,7 +3552,6 @@ declare class StreamClient {
|
|
|
3552
3552
|
private streamUrl;
|
|
3553
3553
|
private streamHeaders;
|
|
3554
3554
|
private requestHeaders;
|
|
3555
|
-
private subscriptionServiceId;
|
|
3556
3555
|
private backendSubscriptionPath;
|
|
3557
3556
|
private runtimeSubscriptionPath;
|
|
3558
3557
|
private subscriptionUrl;
|
|
@@ -4252,11 +4251,13 @@ interface DurableStreamsRoutingInput {
|
|
|
4252
4251
|
}
|
|
4253
4252
|
interface DurableStreamsRoutingAdapter {
|
|
4254
4253
|
streamUrl(input: DurableStreamsRoutingInput): URL;
|
|
4255
|
-
|
|
4254
|
+
controlUrl(input: DurableStreamsRoutingInput): URL;
|
|
4256
4255
|
toBackendStreamPath(serviceId: string, streamPath: string): string;
|
|
4257
4256
|
toRuntimeStreamPath(serviceId: string, streamPath: string): string;
|
|
4258
4257
|
}
|
|
4258
|
+
declare const streamRootDurableStreamsRoutingAdapter: DurableStreamsRoutingAdapter;
|
|
4259
4259
|
declare const pathPrefixedSingleTenantDurableStreamsRoutingAdapter: DurableStreamsRoutingAdapter;
|
|
4260
|
+
declare const tenantRootDurableStreamsRoutingAdapter: DurableStreamsRoutingAdapter;
|
|
4260
4261
|
|
|
4261
4262
|
//#endregion
|
|
4262
4263
|
//#region src/routing/context.d.ts
|
|
@@ -4271,6 +4272,7 @@ interface TenantContext {
|
|
|
4271
4272
|
principal: Principal;
|
|
4272
4273
|
publicUrl: string;
|
|
4273
4274
|
localUrl?: string;
|
|
4275
|
+
/** Resolved Durable Streams root URL for this tenant. */
|
|
4274
4276
|
durableStreamsUrl: string;
|
|
4275
4277
|
durableStreamsBearer?: DurableStreamsBearerProvider;
|
|
4276
4278
|
durableStreamsRouting?: DurableStreamsRoutingAdapter;
|
|
@@ -4302,4 +4304,4 @@ declare class UnregisteredTenantError extends Error {
|
|
|
4302
4304
|
declare function isUnregisteredTenantError(error: unknown): error is UnregisteredTenantError;
|
|
4303
4305
|
|
|
4304
4306
|
//#endregion
|
|
4305
|
-
export { AgentsHost, AgentsHostOptions, AgentsHostTenantConfig, AgentsHostTenantRuntime, AuthenticateRequest, ConsumerClaim, DEFAULT_TENANT_ID, DispatchPolicy, DispatchTarget, DrizzleDB, DurableStreamsBearerProvider, DurableStreamsRoutingAdapter, DurableStreamsRoutingInput, ElectricAgentsRunner, ElectricAgentsUser, EntityBridgeCoordinator, EntityDispatchState, GlobalRoutes, PgClient, Principal, PrincipalKind, PublicWakeNotification, RegisterRunnerRequest, RequestPrincipal, RunnerAdminStatus, RunnerHeartbeatRequest, RunnerKind, RunnerLiveness, SourceStreamOffset, StreamClient, StreamClientOptions, SubscriptionClaimResponse, SubscriptionCreateInput, SubscriptionResponse, SubscriptionStreamInfo, TenantContext, UnregisteredTenantError, WakeNotificationRow, createDb, globalRouter, isUnregisteredTenantError, pathPrefixedSingleTenantDurableStreamsRoutingAdapter, runMigrations };
|
|
4307
|
+
export { AgentsHost, AgentsHostOptions, AgentsHostTenantConfig, AgentsHostTenantRuntime, AuthenticateRequest, ConsumerClaim, DEFAULT_TENANT_ID, DispatchPolicy, DispatchTarget, DrizzleDB, DurableStreamsBearerProvider, DurableStreamsRoutingAdapter, DurableStreamsRoutingInput, ElectricAgentsRunner, ElectricAgentsUser, EntityBridgeCoordinator, EntityDispatchState, GlobalRoutes, PgClient, Principal, PrincipalKind, PublicWakeNotification, RegisterRunnerRequest, RequestPrincipal, RunnerAdminStatus, RunnerHeartbeatRequest, RunnerKind, RunnerLiveness, SourceStreamOffset, StreamClient, StreamClientOptions, SubscriptionClaimResponse, SubscriptionCreateInput, SubscriptionResponse, SubscriptionStreamInfo, TenantContext, UnregisteredTenantError, WakeNotificationRow, createDb, globalRouter, isUnregisteredTenantError, pathPrefixedSingleTenantDurableStreamsRoutingAdapter, runMigrations, streamRootDurableStreamsRoutingAdapter, tenantRootDurableStreamsRoutingAdapter };
|