@electric-ax/agents-server 0.4.9 → 0.4.12

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.
@@ -446,7 +446,7 @@ const ErrCodeForkWaitTimeout = `FORK_WAIT_TIMEOUT`;
446
446
  const ErrCodeEntityPersistFailed = `ENTITY_PERSIST_FAILED`;
447
447
  const ErrCodeAgentUiNotFound = `AGENT_UI_NOT_FOUND`;
448
448
  const ErrCodeSubscriptionNotFound = `SUBSCRIPTION_NOT_FOUND`;
449
- const ErrCodeCallbackNotFound = `CALLBACK_NOT_FOUND`;
449
+ const ErrCodeWakeCallbackNotFound = `WAKE_CALLBACK_NOT_FOUND`;
450
450
 
451
451
  //#endregion
452
452
  //#region src/utils/electric-url.ts
@@ -466,7 +466,7 @@ function electricUrlWithPath(electricUrl, path$1) {
466
466
  //#region src/routing/durable-streams-routing-adapter.ts
467
467
  function appendSearch(target, source) {
468
468
  source.searchParams.forEach((value, key) => {
469
- if (key !== `service`) target.searchParams.append(key, value);
469
+ target.searchParams.append(key, value);
470
470
  });
471
471
  return target;
472
472
  }
@@ -1610,7 +1610,7 @@ async function rewriteSubscriptionRequestBody(request, ctx, subscriptionId, rout
1610
1610
  let targetWebhookUrl = null;
1611
1611
  if (payload.webhook?.url !== void 0) {
1612
1612
  targetWebhookUrl = rewriteLoopbackWebhookUrl(payload.webhook.url) ?? null;
1613
- payload.webhook.url = appendPathToUrl(ctx.publicUrl, `/_electric/webhook-forward/${encodeURIComponent(subscriptionId)}`);
1613
+ payload.webhook.url = appendPathToUrl(ctx.publicUrl, `/_electric/subscription-webhooks/${encodeURIComponent(subscriptionId)}`);
1614
1614
  }
1615
1615
  rewriteSubscriptionBodyForBackend(payload, ctx.service, routingAdapter);
1616
1616
  return {
@@ -1733,20 +1733,6 @@ async function proxyPassThrough(request, ctx) {
1733
1733
  }
1734
1734
  }
1735
1735
 
1736
- //#endregion
1737
- //#region src/routing/cron-router.ts
1738
- const cronRegisterBodySchema = Type.Object({
1739
- expression: Type.String(),
1740
- timezone: Type.Optional(Type.String())
1741
- });
1742
- const cronRouter = Router({ base: `/_electric/cron` });
1743
- cronRouter.post(`/register`, withSchema(cronRegisterBodySchema), registerCron);
1744
- async function registerCron(request, ctx) {
1745
- const parsed = routeBody(request);
1746
- const streamPath = await ctx.entityManager.getOrCreateCronStream(parsed.expression, parsed.timezone);
1747
- return json({ streamUrl: streamPath });
1748
- }
1749
-
1750
1736
  //#endregion
1751
1737
  //#region src/routing/electric-proxy-router.ts
1752
1738
  const electricProxyRouter = Router({ base: `/_electric/electric` });
@@ -3632,7 +3618,7 @@ var EntityManager = class {
3632
3618
  };
3633
3619
  }
3634
3620
  /**
3635
- * Deliver a message to an entity's main stream, with optional input schema
3621
+ * Deliver a message to an entity's main stream, with optional inbox schema
3636
3622
  * validation.
3637
3623
  */
3638
3624
  async send(entityUrl, req, opts) {
@@ -3720,7 +3706,7 @@ var EntityManager = class {
3720
3706
  if (result.changed && this.entityBridgeManager) await this.entityBridgeManager.onEntityChanged(entityUrl);
3721
3707
  return updated;
3722
3708
  }
3723
- async removeTag(entityUrl, key, token) {
3709
+ async deleteTag(entityUrl, key, token) {
3724
3710
  const entity = await this.registry.getEntity(entityUrl);
3725
3711
  if (!entity) throw new ElectricAgentsError(ErrCodeNotFound, `Entity not found`, 404);
3726
3712
  if (!this.isValidWriteToken(entity, token)) throw new ElectricAgentsError(ErrCodeUnauthorized, `Invalid write token`, 401);
@@ -3731,7 +3717,7 @@ var EntityManager = class {
3731
3717
  if (result.changed && this.entityBridgeManager) await this.entityBridgeManager.onEntityChanged(entityUrl);
3732
3718
  return updated;
3733
3719
  }
3734
- async registerEntitiesSource(tags) {
3720
+ async ensureEntitiesMembershipStream(tags) {
3735
3721
  if (!this.entityBridgeManager) throw new Error(`Entity bridge manager not configured`);
3736
3722
  return this.entityBridgeManager.register(this.validateTags(tags));
3737
3723
  }
@@ -4160,7 +4146,7 @@ var EntityManager = class {
4160
4146
  return null;
4161
4147
  }
4162
4148
  /**
4163
- * Add new input/output schema keys to an entity type directly in Postgres.
4149
+ * Add new inbox/state schema keys to an entity type directly in Postgres.
4164
4150
  */
4165
4151
  async amendSchemas(typeName, schemas) {
4166
4152
  if (typeName === `principal`) throw new ElectricAgentsError(ErrCodeInvalidRequest, `Entity type "principal" is built in and cannot be amended`, 400);
@@ -4200,7 +4186,7 @@ var EntityManager = class {
4200
4186
  }
4201
4187
  /**
4202
4188
  * Enrich webhook payload with entity context.
4203
- * Called by ElectricAgentsServer during webhook forwarding to inject entity context.
4189
+ * Called by ElectricAgentsServer during subscription webhook dispatch to inject entity context.
4204
4190
  */
4205
4191
  async enrichPayload(payload, consumer) {
4206
4192
  const entity = await this.registry.getEntityByStream(consumer.primary_stream);
@@ -4465,7 +4451,7 @@ async function linkStreamToTargetSubscription(ctx, target, entity, subscriptionI
4465
4451
  }
4466
4452
  const webhookUrl = rewriteLoopbackWebhookUrl(target.url);
4467
4453
  if (!webhookUrl) throw new ElectricAgentsError(ErrCodeInvalidRequest, `Webhook dispatch target must include a valid URL`, 400);
4468
- const forwardUrl = appendPathToUrl(ctx.publicUrl, `/_electric/webhook-forward/${encodeURIComponent(subscriptionId)}`);
4454
+ const forwardUrl = appendPathToUrl(ctx.publicUrl, `/_electric/subscription-webhooks/${encodeURIComponent(subscriptionId)}`);
4469
4455
  await ensureSubscriptionIncludesStream(ctx, subscriptionId, streamPath, {
4470
4456
  type: `webhook`,
4471
4457
  streams: [streamPath],
@@ -4484,7 +4470,7 @@ async function linkStreamToTargetSubscription(ctx, target, entity, subscriptionI
4484
4470
 
4485
4471
  //#endregion
4486
4472
  //#region src/routing/entities-router.ts
4487
- const stringRecordSchema = Type.Record(Type.String(), Type.String());
4473
+ const stringRecordSchema$1 = Type.Record(Type.String(), Type.String());
4488
4474
  function writeTokenFromRequest(request) {
4489
4475
  const electricClaimToken = request.headers.get(`electric-claim-token`)?.trim();
4490
4476
  if (electricClaimToken) return electricClaimToken;
@@ -4501,7 +4487,7 @@ const wakeConditionSchema = Type.Union([Type.Literal(`runFinished`), Type.Object
4501
4487
  })]);
4502
4488
  const spawnBodySchema = Type.Object({
4503
4489
  args: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
4504
- tags: Type.Optional(stringRecordSchema),
4490
+ tags: Type.Optional(stringRecordSchema$1),
4505
4491
  parent: Type.Optional(Type.String()),
4506
4492
  dispatch_policy: Type.Optional(dispatchPolicySchema),
4507
4493
  initialMessage: Type.Optional(Type.Unknown()),
@@ -4593,10 +4579,8 @@ const eventSourceSubscriptionBodySchema = Type.Object({
4593
4579
  lifetime: Type.Optional(subscriptionLifetimeSchema),
4594
4580
  reason: Type.Optional(Type.String())
4595
4581
  });
4596
- const entitiesRegisterBodySchema = Type.Object({ tags: Type.Optional(stringRecordSchema) });
4597
4582
  const entitiesRouter = Router({ base: `/_electric/entities` });
4598
4583
  entitiesRouter.get(`/`, listEntities);
4599
- entitiesRouter.post(`/register`, withSchema(entitiesRegisterBodySchema), registerEntitiesSource);
4600
4584
  entitiesRouter.put(`/:type/:instanceId`, withSpawnableEntityType, withSchema(spawnBodySchema), spawnEntity);
4601
4585
  entitiesRouter.get(`/:type/:instanceId`, withExistingEntity, getEntity);
4602
4586
  entitiesRouter.head(`/:type/:instanceId`, withExistingEntity, headEntity);
@@ -4607,7 +4591,7 @@ entitiesRouter.patch(`/:type/:instanceId/inbox/:messageKey`, withExistingEntity,
4607
4591
  entitiesRouter.delete(`/:type/:instanceId/inbox/:messageKey`, withExistingEntity, deleteInboxMessage);
4608
4592
  entitiesRouter.post(`/:type/:instanceId/fork`, withExistingEntity, withSchema(forkBodySchema), forkEntity);
4609
4593
  entitiesRouter.post(`/:type/:instanceId/tags/:tagKey`, withExistingEntity, withSchema(setTagBodySchema), setTag);
4610
- entitiesRouter.delete(`/:type/:instanceId/tags/:tagKey`, withExistingEntity, removeTag);
4594
+ entitiesRouter.delete(`/:type/:instanceId/tags/:tagKey`, withExistingEntity, deleteTag);
4611
4595
  entitiesRouter.put(`/:type/:instanceId/schedules/:scheduleId`, withExistingEntity, withSchema(scheduleBodySchema), upsertSchedule);
4612
4596
  entitiesRouter.delete(`/:type/:instanceId/schedules/:scheduleId`, withExistingEntity, deleteSchedule);
4613
4597
  entitiesRouter.put(`/:type/:instanceId/event-source-subscriptions/:subscriptionId`, withExistingEntity, withSchema(eventSourceSubscriptionBodySchema), upsertEventSourceSubscription);
@@ -4675,11 +4659,6 @@ async function listEntities({ query }, ctx) {
4675
4659
  });
4676
4660
  return json(entities$1.map((entity) => toPublicEntity(entity)));
4677
4661
  }
4678
- async function registerEntitiesSource(request, ctx) {
4679
- const parsed = routeBody(request);
4680
- const result = await ctx.entityManager.registerEntitiesSource(parsed.tags ?? {});
4681
- return json(result);
4682
- }
4683
4662
  async function upsertSchedule(request, ctx) {
4684
4663
  const principalMutationError = rejectPrincipalEntityMutation(request, `scheduled`);
4685
4664
  if (principalMutationError) return principalMutationError;
@@ -4760,7 +4739,7 @@ async function deleteEventSourceSubscription(request, ctx) {
4760
4739
  return json(result);
4761
4740
  }
4762
4741
  async function setTag(request, ctx) {
4763
- const principalMutationError = rejectPrincipalEntityMutation(request, `tagged`);
4742
+ const principalMutationError = rejectPrincipalEntityMutation(request, `tag updated`);
4764
4743
  if (principalMutationError) return principalMutationError;
4765
4744
  const parsed = routeBody(request);
4766
4745
  const { entityUrl } = requireExistingEntityRoute(request);
@@ -4768,12 +4747,12 @@ async function setTag(request, ctx) {
4768
4747
  const updated = await ctx.entityManager.setTag(entityUrl, decodeURIComponent(request.params.tagKey), { value: parsed.value }, token);
4769
4748
  return json(toPublicEntity(updated));
4770
4749
  }
4771
- async function removeTag(request, ctx) {
4772
- const principalMutationError = rejectPrincipalEntityMutation(request, `untagged`);
4750
+ async function deleteTag(request, ctx) {
4751
+ const principalMutationError = rejectPrincipalEntityMutation(request, `tag deleted`);
4773
4752
  if (principalMutationError) return principalMutationError;
4774
4753
  const { entityUrl } = requireExistingEntityRoute(request);
4775
4754
  const token = writeTokenFromRequest(request);
4776
- const updated = await ctx.entityManager.removeTag(entityUrl, decodeURIComponent(request.params.tagKey), token);
4755
+ const updated = await ctx.entityManager.deleteTag(entityUrl, decodeURIComponent(request.params.tagKey), token);
4777
4756
  return json(toPublicEntity(updated));
4778
4757
  }
4779
4758
  async function forkEntity(request, ctx) {
@@ -4905,17 +4884,13 @@ const registerEntityTypeBodySchema = Type.Object({
4905
4884
  creation_schema: Type.Optional(jsonObjectSchema),
4906
4885
  inbox_schemas: Type.Optional(schemaMapSchema),
4907
4886
  state_schemas: Type.Optional(schemaMapSchema),
4908
- input_schemas: Type.Optional(schemaMapSchema),
4909
- output_schemas: Type.Optional(schemaMapSchema),
4910
4887
  serve_endpoint: Type.Optional(Type.String()),
4911
4888
  default_dispatch_policy: Type.Optional(dispatchPolicySchema)
4912
- });
4889
+ }, { additionalProperties: false });
4913
4890
  const amendEntityTypeSchemasBodySchema = Type.Object({
4914
- input_schemas: Type.Optional(schemaMapSchema),
4915
- output_schemas: Type.Optional(schemaMapSchema),
4916
4891
  inbox_schemas: Type.Optional(schemaMapSchema),
4917
4892
  state_schemas: Type.Optional(schemaMapSchema)
4918
- });
4893
+ }, { additionalProperties: false });
4919
4894
  const entityTypesRouter = Router({ base: `/_electric/entity-types` });
4920
4895
  entityTypesRouter.get(`/`, listEntityTypes);
4921
4896
  entityTypesRouter.post(`/`, withSchema(registerEntityTypeBodySchema), registerEntityType);
@@ -4955,8 +4930,8 @@ async function getEntityType(request, ctx) {
4955
4930
  async function amendSchemas(request, ctx) {
4956
4931
  const parsed = routeBody(request);
4957
4932
  const updated = await ctx.entityManager.amendSchemas(request.params.name, {
4958
- inbox_schemas: parsed.inbox_schemas ?? parsed.input_schemas,
4959
- state_schemas: parsed.state_schemas ?? parsed.output_schemas
4933
+ inbox_schemas: parsed.inbox_schemas,
4934
+ state_schemas: parsed.state_schemas
4960
4935
  });
4961
4936
  return json(toPublicEntityType(updated));
4962
4937
  }
@@ -4966,13 +4941,12 @@ async function deleteEntityType(request, ctx) {
4966
4941
  }
4967
4942
  function normalizeEntityTypeRequest(parsed) {
4968
4943
  const serveEndpoint = rewriteLoopbackWebhookUrl(parsed.serve_endpoint);
4969
- const compatibilityFields = parsed;
4970
4944
  return {
4971
4945
  name: parsed.name ?? ``,
4972
4946
  description: parsed.description ?? ``,
4973
4947
  creation_schema: parsed.creation_schema,
4974
- inbox_schemas: parsed.inbox_schemas ?? compatibilityFields.input_schemas,
4975
- state_schemas: parsed.state_schemas ?? compatibilityFields.output_schemas,
4948
+ inbox_schemas: parsed.inbox_schemas,
4949
+ state_schemas: parsed.state_schemas,
4976
4950
  serve_endpoint: serveEndpoint,
4977
4951
  default_dispatch_policy: parsed.default_dispatch_policy ?? (serveEndpoint ? { targets: [{
4978
4952
  type: `webhook`,
@@ -4983,8 +4957,6 @@ function normalizeEntityTypeRequest(parsed) {
4983
4957
  function toPublicEntityType(entityType) {
4984
4958
  return {
4985
4959
  ...entityType,
4986
- input_schemas: entityType.inbox_schemas,
4987
- output_schemas: entityType.state_schemas,
4988
4960
  revision: entityType.revision
4989
4961
  };
4990
4962
  }
@@ -5068,13 +5040,35 @@ function errorMapper(err, req) {
5068
5040
  function rejectIfShuttingDown(req, ctx) {
5069
5041
  if (!ctx.isShuttingDown()) return void 0;
5070
5042
  const path$1 = new URL(req.url).pathname;
5071
- if (!path$1.startsWith(`/_electric/webhook-forward/`)) return void 0;
5043
+ if (!path$1.startsWith(`/_electric/subscription-webhooks/`)) return void 0;
5072
5044
  return apiError(503, `SERVER_STOPPING`, `Server is shutting down`);
5073
5045
  }
5074
5046
  function getRequestSpan(req) {
5075
5047
  return carrier(req)[SPAN_KEY];
5076
5048
  }
5077
5049
 
5050
+ //#endregion
5051
+ //#region src/routing/observations-router.ts
5052
+ const stringRecordSchema = Type.Record(Type.String(), Type.String());
5053
+ const ensureEntitiesMembershipStreamBodySchema = Type.Object({ tags: Type.Optional(stringRecordSchema) });
5054
+ const ensureCronStreamBodySchema = Type.Object({
5055
+ expression: Type.String(),
5056
+ timezone: Type.Optional(Type.String())
5057
+ });
5058
+ const observationsRouter = Router({ base: `/_electric/observations` });
5059
+ observationsRouter.post(`/entities/ensure-stream`, withSchema(ensureEntitiesMembershipStreamBodySchema), ensureEntitiesMembershipStream);
5060
+ observationsRouter.post(`/cron/ensure-stream`, withSchema(ensureCronStreamBodySchema), ensureCronStream);
5061
+ async function ensureEntitiesMembershipStream(request, ctx) {
5062
+ const parsed = routeBody(request);
5063
+ const result = await ctx.entityManager.ensureEntitiesMembershipStream(parsed.tags ?? {});
5064
+ return json(result);
5065
+ }
5066
+ async function ensureCronStream(request, ctx) {
5067
+ const parsed = routeBody(request);
5068
+ const streamPath = await ctx.entityManager.getOrCreateCronStream(parsed.expression, parsed.timezone);
5069
+ return json({ streamUrl: streamPath });
5070
+ }
5071
+
5078
5072
  //#endregion
5079
5073
  //#region src/routing/tenant-stream-paths.ts
5080
5074
  function withLeadingSlash(path$1) {
@@ -5413,7 +5407,7 @@ async function notificationFromClaim(ctx, input) {
5413
5407
  wakeId: input.claim.wake_id,
5414
5408
  streamPath: primaryStream,
5415
5409
  streams: streams$1,
5416
- callback: appendPathToUrl(ctx.publicUrl, `/_electric/callback-forward/${encodeURIComponent(input.claim.wake_id)}`),
5410
+ callback: appendPathToUrl(ctx.publicUrl, `/_electric/wake-callbacks/${encodeURIComponent(input.claim.wake_id)}`),
5417
5411
  claimToken: input.claim.token,
5418
5412
  triggerEvent: `message_received`,
5419
5413
  entity: {
@@ -5448,7 +5442,7 @@ const wakeRegistrationBodySchema = Type.Object({
5448
5442
  includeResponse: Type.Optional(Type.Boolean()),
5449
5443
  manifestKey: Type.Optional(Type.String())
5450
5444
  });
5451
- const webhookForwardBodySchema = Type.Object({
5445
+ const subscriptionWebhookBodySchema = Type.Object({
5452
5446
  subscription_id: Type.Optional(Type.String()),
5453
5447
  wake_id: Type.Optional(Type.String()),
5454
5448
  generation: Type.Optional(Type.Number()),
@@ -5462,7 +5456,7 @@ const webhookForwardBodySchema = Type.Object({
5462
5456
  consumer_id: Type.Optional(Type.String()),
5463
5457
  callback: Type.Optional(Type.String())
5464
5458
  }, { additionalProperties: true });
5465
- const callbackForwardBodySchema = Type.Object({
5459
+ const wakeCallbackBodySchema = Type.Object({
5466
5460
  epoch: Type.Optional(Type.Number()),
5467
5461
  generation: Type.Optional(Type.Number()),
5468
5462
  wakeId: Type.Optional(Type.String()),
@@ -5475,13 +5469,13 @@ const internalRouter = Router({ base: `/_electric` });
5475
5469
  internalRouter.get(`/health`, () => json({ status: `ok` }));
5476
5470
  internalRouter.get(`/event-sources`, listEventSources);
5477
5471
  internalRouter.post(`/wake`, withSchema(wakeRegistrationBodySchema), registerWake);
5478
- internalRouter.post(`/webhook-forward/:subscriptionId`, webhookForward);
5479
- internalRouter.post(`/callback-forward/:consumerId`, callbackForward);
5472
+ internalRouter.post(`/subscription-webhooks/:subscriptionId`, subscriptionWebhook);
5473
+ internalRouter.post(`/wake-callbacks/:consumerId`, wakeCallback);
5480
5474
  internalRouter.all(`/runners`, runnersRouter.fetch);
5481
5475
  internalRouter.all(`/runners/*`, runnersRouter.fetch);
5482
5476
  internalRouter.all(`/entities/*`, entitiesRouter.fetch);
5483
5477
  internalRouter.all(`/entity-types/*`, entityTypesRouter.fetch);
5484
- internalRouter.all(`/cron/*`, cronRouter.fetch);
5478
+ internalRouter.all(`/observations/*`, observationsRouter.fetch);
5485
5479
  internalRouter.get(`/electric/*`, electricProxyRouter.fetch);
5486
5480
  internalRouter.all(`*`, () => status(404));
5487
5481
  function routeParam(request, name) {
@@ -5514,13 +5508,30 @@ function resolveWebhookSigner(ctx) {
5514
5508
  return ctx.webhookSigner ?? getDefaultWebhookSigner();
5515
5509
  }
5516
5510
  function durableStreamsWebhookJwksUrl(ctx) {
5517
- if (!ctx.durableStreamsRouting) return appendPathToUrl(ctx.durableStreamsUrl, `/__ds/jwks.json`);
5511
+ if (!ctx.durableStreamsRouting) return appendPathToBackendUrl(ctx.durableStreamsUrl, `/__ds/jwks.json`);
5518
5512
  return resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting, ctx.durableStreamsUrl).controlUrl({
5519
5513
  durableStreamsUrl: ctx.durableStreamsUrl,
5520
5514
  serviceId: ctx.service,
5521
5515
  requestUrl: appendPathToUrl(ctx.publicUrl, `/__ds/jwks.json`)
5522
5516
  }).toString();
5523
5517
  }
5518
+ function appendPathToBackendUrl(baseUrl, path$1) {
5519
+ const base = new URL(baseUrl);
5520
+ const pathUrl = new URL(path$1, `http://electric-agents.local`);
5521
+ const basePath = base.pathname === `/` ? `` : base.pathname.replace(/\/+$/, ``);
5522
+ const suffix = pathUrl.pathname.startsWith(`/`) ? pathUrl.pathname : `/${pathUrl.pathname}`;
5523
+ const target = new URL(base);
5524
+ target.pathname = `${basePath}${suffix}`;
5525
+ target.search = ``;
5526
+ target.hash = pathUrl.hash;
5527
+ base.searchParams.forEach((value, key) => {
5528
+ target.searchParams.append(key, value);
5529
+ });
5530
+ pathUrl.searchParams.forEach((value, key) => {
5531
+ target.searchParams.append(key, value);
5532
+ });
5533
+ return target.toString();
5534
+ }
5524
5535
  function durableStreamsJwksFetchClient(ctx) {
5525
5536
  return async (input, init) => {
5526
5537
  const headers = new Headers(init?.headers);
@@ -5584,10 +5595,10 @@ async function listEventSources(_request, ctx) {
5584
5595
  function isAgentVisibleEventSource(source) {
5585
5596
  return source.agentVisible === true && source.status === `active`;
5586
5597
  }
5587
- async function webhookForward(request, ctx) {
5598
+ async function subscriptionWebhook(request, ctx) {
5588
5599
  const subscriptionId = routeParam(request, `subscriptionId`);
5589
5600
  const rootSpan = getRequestSpan(request);
5590
- rootSpan?.updateName(`webhook-forward`);
5601
+ rootSpan?.updateName(`subscription-webhook`);
5591
5602
  rootSpan?.setAttribute(`electric_agents.webhook.subscription_id`, subscriptionId);
5592
5603
  const body = await readRequestBody(request);
5593
5604
  const signatureError = await verifyDurableStreamsWebhook(request, ctx, body);
@@ -5601,7 +5612,7 @@ async function webhookForward(request, ctx) {
5601
5612
  }
5602
5613
  });
5603
5614
  if (!targetWebhookUrl) return apiError(404, ErrCodeSubscriptionNotFound, `Unknown webhook subscription`);
5604
- const parsedBodyResult = validateOptionalJsonBody(webhookForwardBodySchema, body, request.headers.get(`content-type`));
5615
+ const parsedBodyResult = validateOptionalJsonBody(subscriptionWebhookBodySchema, body, request.headers.get(`content-type`));
5605
5616
  if (!parsedBodyResult.ok) return parsedBodyResult.response;
5606
5617
  let forwardBody = body;
5607
5618
  let runningEntityUrl = null;
@@ -5647,7 +5658,7 @@ async function webhookForward(request, ctx) {
5647
5658
  span.end();
5648
5659
  }
5649
5660
  }).catch((err) => {
5650
- serverLog.warn(`[webhook-forward] consumerCallbacks upsert failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
5661
+ serverLog.warn(`[subscription-webhook] consumerCallbacks upsert failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
5651
5662
  }) : void 0;
5652
5663
  const [entity, enriched] = await Promise.all([entityPromise, enrichPromise]);
5653
5664
  if (entity?.status === `stopped` || entity?.status === `paused`) {
@@ -5668,7 +5679,7 @@ async function webhookForward(request, ctx) {
5668
5679
  runningEntityUrl = entity.url;
5669
5680
  }
5670
5681
  if (consumerId && callbackUrl) {
5671
- const callback = appendPathToUrl(ctx.publicUrl, `/_electric/callback-forward/${encodeURIComponent(consumerId)}`);
5682
+ const callback = appendPathToUrl(ctx.publicUrl, `/_electric/wake-callbacks/${encodeURIComponent(consumerId)}`);
5672
5683
  enriched.callback = callback;
5673
5684
  if (newWebhook) {
5674
5685
  enriched.consumerId = newWebhook.wakeId;
@@ -5705,21 +5716,21 @@ async function webhookForward(request, ctx) {
5705
5716
  });
5706
5717
  } catch (err) {
5707
5718
  if (runningEntityUrl) await ctx.entityManager.registry.updateStatus(runningEntityUrl, `idle`);
5708
- return apiError(502, `WEBHOOK_FORWARD_FAILED`, err instanceof Error ? err.message : String(err));
5719
+ return apiError(502, `SUBSCRIPTION_WEBHOOK_FAILED`, err instanceof Error ? err.message : String(err));
5709
5720
  }
5710
5721
  const responseBytes = upstream.body ? new Uint8Array(await upstream.arrayBuffer()) : new Uint8Array();
5711
5722
  return responseFromUpstream(upstream, responseBytes);
5712
5723
  }
5713
- async function callbackForward(request, ctx) {
5724
+ async function wakeCallback(request, ctx) {
5714
5725
  const consumerId = routeParam(request, `consumerId`);
5715
5726
  const rows = await ctx.pgDb.select().from(consumerCallbacks).where(and(eq(consumerCallbacks.tenantId, ctx.service), eq(consumerCallbacks.consumerId, consumerId))).limit(1);
5716
5727
  const target = rows[0] ? {
5717
5728
  callbackUrl: rows[0].callbackUrl,
5718
5729
  primaryStream: rows[0].primaryStream
5719
5730
  } : void 0;
5720
- if (!target) return apiError(404, ErrCodeCallbackNotFound, `Unknown callback-forward consumer`);
5731
+ if (!target) return apiError(404, ErrCodeWakeCallbackNotFound, `Unknown wake-callback consumer`);
5721
5732
  const body = await readRequestBody(request);
5722
- const parsedBodyResult = validateOptionalJsonBody(callbackForwardBodySchema, body, request.headers.get(`content-type`));
5733
+ const parsedBodyResult = validateOptionalJsonBody(wakeCallbackBodySchema, body, request.headers.get(`content-type`));
5723
5734
  if (!parsedBodyResult.ok) return parsedBodyResult.response;
5724
5735
  const requestBody = parsedBodyResult.value;
5725
5736
  const isClaimRequest = requestBody?.wakeId !== void 0 || requestBody?.wake_id !== void 0;
@@ -5737,14 +5748,14 @@ async function callbackForward(request, ctx) {
5737
5748
  }
5738
5749
  return json(responseBody);
5739
5750
  }
5740
- const upstreamBody = encodeCallbackForwardBody(ctx.service, consumerId, requestBody, resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting, ctx.durableStreamsUrl));
5751
+ const upstreamBody = encodeWakeCallbackBody(ctx.service, consumerId, requestBody, resolveDurableStreamsRoutingAdapter(ctx.durableStreamsRouting, ctx.durableStreamsUrl));
5741
5752
  let upstream;
5742
5753
  try {
5743
5754
  const subscriptionId = durableStreamsSubscriptionCallback(target.callbackUrl);
5744
5755
  if (subscriptionId) {
5745
5756
  const token = claimTokenFromRequest(request);
5746
5757
  if (!token) return apiError(401, `UNAUTHORIZED`, `Missing claim token`);
5747
- const upstreamPayload = encodeCallbackForwardPayload(consumerId, requestBody, (stream) => stream.replace(/^\/+/, ``));
5758
+ const upstreamPayload = encodeWakeCallbackPayload(consumerId, requestBody, (stream) => stream.replace(/^\/+/, ``));
5748
5759
  const result = await ctx.streamClient.ackSubscription(subscriptionId, token, upstreamPayload);
5749
5760
  upstream = json(result);
5750
5761
  } else upstream = await fetch(target.callbackUrl, {
@@ -5753,7 +5764,7 @@ async function callbackForward(request, ctx) {
5753
5764
  body: bodyFromBytes(upstreamBody)
5754
5765
  });
5755
5766
  } catch (err) {
5756
- return apiError(502, `CALLBACK_FORWARD_FAILED`, err instanceof Error ? err.message : String(err));
5767
+ return apiError(502, `WAKE_CALLBACK_FAILED`, err instanceof Error ? err.message : String(err));
5757
5768
  }
5758
5769
  let responseBytes = upstream.body ? new Uint8Array(await upstream.arrayBuffer()) : new Uint8Array();
5759
5770
  if (isClaimRequest && upstream.ok && target.primaryStream) {
@@ -5773,7 +5784,7 @@ async function callbackForward(request, ctx) {
5773
5784
  epoch
5774
5785
  });
5775
5786
  if (upstream.ok && isDoneRequest && target.primaryStream) {
5776
- serverLog.info(`[callback-forward] done received for stream=${target.primaryStream} consumer=${consumerId}`);
5787
+ serverLog.info(`[wake-callback] done received for stream=${target.primaryStream} consumer=${consumerId}`);
5777
5788
  const stillOwnsClaim = ctx.runtime.claimWriteTokens.owns(ctx.service, target.primaryStream, consumerId);
5778
5789
  const entity = await ctx.entityManager.registry.getEntityByStream(target.primaryStream);
5779
5790
  let entityCleared = false;
@@ -5795,13 +5806,13 @@ async function callbackForward(request, ctx) {
5795
5806
  if (entity && (entityCleared || stillOwnsClaim)) {
5796
5807
  await ctx.entityManager.registry.updateStatus(entity.url, entity.status === `stopping` ? `stopped` : `idle`);
5797
5808
  await ctx.entityBridgeManager.onEntityChanged(entity.url);
5798
- serverLog.info(`[callback-forward] status updated after done for ${entity.url}`);
5799
- } else if (!entity) serverLog.warn(`[callback-forward] done received but no entity found for stream=${target.primaryStream}`);
5809
+ serverLog.info(`[wake-callback] status updated after done for ${entity.url}`);
5810
+ } else if (!entity) serverLog.warn(`[wake-callback] done received but no entity found for stream=${target.primaryStream}`);
5800
5811
  if (stillOwnsClaim) ctx.runtime.claimWriteTokens.clearStream(ctx.service, target.primaryStream);
5801
- else if (entity) serverLog.info(`[callback-forward] done arrived after in-memory token evicted (stream=${target.primaryStream} consumer=${consumerId})`);
5802
- } else if (requestBody?.done === true) serverLog.warn(`[callback-forward] done received but skipped: upstream.ok=${upstream.ok} primaryStream=${target.primaryStream ?? `null`} consumer=${consumerId}`);
5812
+ else if (entity) serverLog.info(`[wake-callback] done arrived after in-memory token evicted (stream=${target.primaryStream} consumer=${consumerId})`);
5813
+ } else if (requestBody?.done === true) serverLog.warn(`[wake-callback] done received but skipped: upstream.ok=${upstream.ok} primaryStream=${target.primaryStream ?? `null`} consumer=${consumerId}`);
5803
5814
  } catch (err) {
5804
- serverLog.error(`[callback-forward] error processing done for consumer=${consumerId}: ${err instanceof Error ? err.message : String(err)}`);
5815
+ serverLog.error(`[wake-callback] error processing done for consumer=${consumerId}: ${err instanceof Error ? err.message : String(err)}`);
5805
5816
  }
5806
5817
  return responseFromUpstream(upstream, responseBytes);
5807
5818
  }
@@ -5810,11 +5821,11 @@ async function mintClaimWriteToken(ctx, streamPath, consumerId) {
5810
5821
  if (!entity) return void 0;
5811
5822
  return ctx.runtime.claimWriteTokens.mint(ctx.service, streamPath, consumerId);
5812
5823
  }
5813
- function encodeCallbackForwardBody(service, consumerId, body, routingAdapter) {
5814
- const payload = encodeCallbackForwardPayload(consumerId, body, (stream) => routingAdapter.toBackendStreamPath(service, stream));
5824
+ function encodeWakeCallbackBody(service, consumerId, body, routingAdapter) {
5825
+ const payload = encodeWakeCallbackPayload(consumerId, body, (stream) => routingAdapter.toBackendStreamPath(service, stream));
5815
5826
  return new TextEncoder().encode(JSON.stringify(payload));
5816
5827
  }
5817
- function encodeCallbackForwardPayload(consumerId, body, mapStream) {
5828
+ function encodeWakeCallbackPayload(consumerId, body, mapStream) {
5818
5829
  if (!body) return {};
5819
5830
  const generation = body.generation ?? body.epoch;
5820
5831
  const wakeId = body.wake_id ?? body.wakeId ?? consumerId;
@@ -7396,7 +7407,7 @@ var WakeRegistry = class {
7396
7407
  try {
7397
7408
  for (const message of messages) {
7398
7409
  await this.applyShapeMessage(message);
7399
- if (!settled && `control` in message.headers && message.headers.control === `up-to-date`) {
7410
+ if (!settled && isControlMessage(message) && message.headers.control === `up-to-date`) {
7400
7411
  settled = true;
7401
7412
  resolve$1();
7402
7413
  }