@spine-event-engine/server 2.0.0-snapshot.24 → 2.0.0-snapshot.25

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.
Files changed (286) hide show
  1. package/README.md +38 -11
  2. package/REFERENCE.md +201 -130
  3. package/dist/agent/agent-acceptance-receipts.d.ts +69 -0
  4. package/dist/agent/agent-acceptance-receipts.d.ts.map +1 -0
  5. package/dist/agent/agent-acceptance-receipts.js +198 -0
  6. package/dist/agent/agent-acceptance-receipts.js.map +1 -0
  7. package/dist/agent/agent-execution-capacity.d.ts +37 -9
  8. package/dist/agent/agent-execution-capacity.d.ts.map +1 -1
  9. package/dist/agent/agent-execution-capacity.js +73 -15
  10. package/dist/agent/agent-execution-capacity.js.map +1 -1
  11. package/dist/agent/agent-history.d.ts +15 -10
  12. package/dist/agent/agent-history.d.ts.map +1 -1
  13. package/dist/agent/agent-history.js +26 -14
  14. package/dist/agent/agent-history.js.map +1 -1
  15. package/dist/agent/agent-live-ai-audit.d.ts +95 -0
  16. package/dist/agent/agent-live-ai-audit.d.ts.map +1 -0
  17. package/dist/agent/agent-live-ai-audit.js +290 -0
  18. package/dist/agent/agent-live-ai-audit.js.map +1 -0
  19. package/dist/agent/agent-live-ai-runtime.d.ts +300 -0
  20. package/dist/agent/agent-live-ai-runtime.d.ts.map +1 -0
  21. package/dist/agent/agent-live-ai-runtime.js +611 -0
  22. package/dist/agent/agent-live-ai-runtime.js.map +1 -0
  23. package/dist/agent/agent-live-audit.d.ts +67 -0
  24. package/dist/agent/agent-live-audit.d.ts.map +1 -0
  25. package/dist/agent/agent-live-audit.js +121 -0
  26. package/dist/agent/agent-live-audit.js.map +1 -0
  27. package/dist/agent/agent-live-budget.d.ts +84 -0
  28. package/dist/agent/agent-live-budget.d.ts.map +1 -0
  29. package/dist/agent/agent-live-budget.js +161 -0
  30. package/dist/agent/agent-live-budget.js.map +1 -0
  31. package/dist/agent/agent-live-fault.d.ts +18 -0
  32. package/dist/agent/agent-live-fault.d.ts.map +1 -0
  33. package/dist/agent/{agent-execution-fault.js → agent-live-fault.js} +7 -7
  34. package/dist/agent/agent-live-fault.js.map +1 -0
  35. package/dist/agent/agent-live-inputs.d.ts +74 -0
  36. package/dist/agent/agent-live-inputs.d.ts.map +1 -0
  37. package/dist/{bus/saved-dispatcher-binding.js → agent/agent-live-inputs.js} +2 -14
  38. package/dist/agent/agent-live-inputs.js.map +1 -0
  39. package/dist/agent/agent-live-interaction-events.d.ts +46 -0
  40. package/dist/agent/agent-live-interaction-events.d.ts.map +1 -0
  41. package/dist/agent/agent-live-interaction-events.js +104 -0
  42. package/dist/agent/agent-live-interaction-events.js.map +1 -0
  43. package/dist/agent/agent-live-mcp-controls.d.ts +129 -0
  44. package/dist/agent/agent-live-mcp-controls.d.ts.map +1 -0
  45. package/dist/agent/agent-live-mcp-controls.js +228 -0
  46. package/dist/agent/agent-live-mcp-controls.js.map +1 -0
  47. package/dist/agent/agent-live-mcp-host.d.ts +88 -0
  48. package/dist/agent/agent-live-mcp-host.d.ts.map +1 -0
  49. package/dist/agent/{agent-mcp-host.js → agent-live-mcp-host.js} +1 -1
  50. package/dist/agent/agent-live-mcp-host.js.map +1 -0
  51. package/dist/agent/{agent-mcp-runtime.d.ts → agent-live-mcp-runtime.d.ts} +6 -6
  52. package/dist/agent/agent-live-mcp-runtime.d.ts.map +1 -0
  53. package/dist/agent/{agent-mcp-runtime.js → agent-live-mcp-runtime.js} +13 -42
  54. package/dist/agent/agent-live-mcp-runtime.js.map +1 -0
  55. package/dist/agent/agent-live-read-runtime.d.ts +61 -0
  56. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-live-read-runtime.js +121 -0
  58. package/dist/agent/agent-live-read-runtime.js.map +1 -0
  59. package/dist/agent/agent-model-selection.d.ts +21 -34
  60. package/dist/agent/agent-model-selection.d.ts.map +1 -1
  61. package/dist/agent/agent-model-selection.js +61 -78
  62. package/dist/agent/agent-model-selection.js.map +1 -1
  63. package/dist/bus/command-bus.d.ts +14 -18
  64. package/dist/bus/command-bus.d.ts.map +1 -1
  65. package/dist/bus/command-bus.js +31 -48
  66. package/dist/bus/command-bus.js.map +1 -1
  67. package/dist/bus/event-bus.d.ts +17 -4
  68. package/dist/bus/event-bus.d.ts.map +1 -1
  69. package/dist/bus/event-bus.js +42 -12
  70. package/dist/bus/event-bus.js.map +1 -1
  71. package/dist/context/bounded-context.d.ts +140 -87
  72. package/dist/context/bounded-context.d.ts.map +1 -1
  73. package/dist/context/bounded-context.js +702 -329
  74. package/dist/context/bounded-context.js.map +1 -1
  75. package/dist/context/entity-inbox.d.ts +7 -2
  76. package/dist/context/entity-inbox.d.ts.map +1 -1
  77. package/dist/context/entity-inbox.js +22 -13
  78. package/dist/context/entity-inbox.js.map +1 -1
  79. package/dist/context/local-inbox-handoff.d.ts +34 -34
  80. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  81. package/dist/context/local-inbox-handoff.js +108 -150
  82. package/dist/context/local-inbox-handoff.js.map +1 -1
  83. package/dist/context/projection-handoff.d.ts +2 -2
  84. package/dist/context/projection-handoff.d.ts.map +1 -1
  85. package/dist/context/projection-handoff.js +10 -8
  86. package/dist/context/projection-handoff.js.map +1 -1
  87. package/dist/context/tenant-index.d.ts +36 -31
  88. package/dist/context/tenant-index.d.ts.map +1 -1
  89. package/dist/context/tenant-index.js +123 -125
  90. package/dist/context/tenant-index.js.map +1 -1
  91. package/dist/context/tenant-unknown-error.d.ts +33 -0
  92. package/dist/context/tenant-unknown-error.d.ts.map +1 -0
  93. package/dist/context/tenant-unknown-error.js +75 -0
  94. package/dist/context/tenant-unknown-error.js.map +1 -0
  95. package/dist/delivery/agent-delivery-scope.d.ts +66 -0
  96. package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
  97. package/dist/delivery/agent-delivery-scope.js +44 -0
  98. package/dist/delivery/agent-delivery-scope.js.map +1 -0
  99. package/dist/delivery/delivery-builder.d.ts +25 -1
  100. package/dist/delivery/delivery-builder.d.ts.map +1 -1
  101. package/dist/delivery/delivery-builder.js +89 -0
  102. package/dist/delivery/delivery-builder.js.map +1 -1
  103. package/dist/delivery/delivery-monitor.d.ts +1 -1
  104. package/dist/delivery/delivery-monitor.js +1 -1
  105. package/dist/delivery/delivery-run-control.d.ts +6 -0
  106. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  107. package/dist/delivery/delivery-run-control.js +10 -0
  108. package/dist/delivery/delivery-run-control.js.map +1 -1
  109. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  110. package/dist/delivery/delivery-supervisor.js +28 -4
  111. package/dist/delivery/delivery-supervisor.js.map +1 -1
  112. package/dist/delivery/delivery-tenant.d.ts +17 -0
  113. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  114. package/dist/delivery/delivery-tenant.js +50 -0
  115. package/dist/delivery/delivery-tenant.js.map +1 -0
  116. package/dist/delivery/delivery.d.ts +96 -2
  117. package/dist/delivery/delivery.d.ts.map +1 -1
  118. package/dist/delivery/delivery.js +693 -53
  119. package/dist/delivery/delivery.js.map +1 -1
  120. package/dist/delivery/inbox-records.d.ts.map +1 -1
  121. package/dist/delivery/inbox-records.js +9 -16
  122. package/dist/delivery/inbox-records.js.map +1 -1
  123. package/dist/delivery/inbox-storage.d.ts +6 -0
  124. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  125. package/dist/delivery/inbox-storage.js +43 -6
  126. package/dist/delivery/inbox-storage.js.map +1 -1
  127. package/dist/delivery/inbox.d.ts.map +1 -1
  128. package/dist/delivery/inbox.js +3 -2
  129. package/dist/delivery/inbox.js.map +1 -1
  130. package/dist/delivery/reception-observation.d.ts +12 -4
  131. package/dist/delivery/reception-observation.d.ts.map +1 -1
  132. package/dist/delivery/reception-observation.js +14 -3
  133. package/dist/delivery/reception-observation.js.map +1 -1
  134. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  135. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  136. package/dist/delivery/sharded-work-registry.js +41 -4
  137. package/dist/delivery/sharded-work-registry.js.map +1 -1
  138. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  139. package/dist/entity/entity-storage-descriptor.js +7 -4
  140. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  141. package/dist/index.d.ts +4 -2
  142. package/dist/index.d.ts.map +1 -1
  143. package/dist/index.js +2 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/integration/external-messages.d.ts.map +1 -1
  146. package/dist/integration/external-messages.js +9 -15
  147. package/dist/integration/external-messages.js.map +1 -1
  148. package/dist/integration/integration-broker.d.ts.map +1 -1
  149. package/dist/integration/integration-broker.js +112 -6
  150. package/dist/integration/integration-broker.js.map +1 -1
  151. package/dist/integration/third-party-context.d.ts.map +1 -1
  152. package/dist/integration/third-party-context.js +7 -10
  153. package/dist/integration/third-party-context.js.map +1 -1
  154. package/dist/repository/primitive-id.js +26 -11
  155. package/dist/repository/primitive-id.js.map +1 -1
  156. package/dist/repository/repository.d.ts +32 -60
  157. package/dist/repository/repository.d.ts.map +1 -1
  158. package/dist/repository/repository.js +1446 -1787
  159. package/dist/repository/repository.js.map +1 -1
  160. package/dist/runtime/runtime.d.ts +13 -0
  161. package/dist/runtime/runtime.d.ts.map +1 -1
  162. package/dist/runtime/runtime.js +18 -0
  163. package/dist/runtime/runtime.js.map +1 -1
  164. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  165. package/dist/runtime/signal-metadata.js +6 -6
  166. package/dist/runtime/signal-metadata.js.map +1 -1
  167. package/dist/runtime/signal-publisher.d.ts +0 -6
  168. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  169. package/dist/runtime/signal-publisher.js +1 -9
  170. package/dist/runtime/signal-publisher.js.map +1 -1
  171. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  172. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  173. package/dist/runtime/standalone-handler-runtime.js +13 -99
  174. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  175. package/dist/server/managed-delivery-gate.d.ts +34 -0
  176. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  177. package/dist/server/managed-delivery-gate.js +70 -0
  178. package/dist/server/managed-delivery-gate.js.map +1 -0
  179. package/dist/server/managed-server-application.d.ts.map +1 -1
  180. package/dist/server/managed-server-application.js +7 -4
  181. package/dist/server/managed-server-application.js.map +1 -1
  182. package/dist/server/server-environment.d.ts +203 -25
  183. package/dist/server/server-environment.d.ts.map +1 -1
  184. package/dist/server/server-environment.js +661 -133
  185. package/dist/server/server-environment.js.map +1 -1
  186. package/dist/server/server-log.d.ts +7 -0
  187. package/dist/server/server-log.d.ts.map +1 -1
  188. package/dist/server/server-log.js +14 -1
  189. package/dist/server/server-log.js.map +1 -1
  190. package/dist/server/server.d.ts +18 -32
  191. package/dist/server/server.d.ts.map +1 -1
  192. package/dist/server/server.js +50 -204
  193. package/dist/server/server.js.map +1 -1
  194. package/dist/services/spine-services.d.ts +1 -1
  195. package/dist/services/spine-services.d.ts.map +1 -1
  196. package/dist/services/spine-services.js +236 -335
  197. package/dist/services/spine-services.js.map +1 -1
  198. package/dist/stand/event-origin-filter.js +3 -3
  199. package/dist/stand/event-origin-filter.js.map +1 -1
  200. package/dist/stand/stand.d.ts +14 -0
  201. package/dist/stand/stand.d.ts.map +1 -1
  202. package/dist/stand/stand.js +53 -9
  203. package/dist/stand/stand.js.map +1 -1
  204. package/dist/stand/subscription-observer.d.ts.map +1 -1
  205. package/dist/stand/subscription-observer.js +54 -40
  206. package/dist/stand/subscription-observer.js.map +1 -1
  207. package/dist/stand/subscription-registry.d.ts +6 -2
  208. package/dist/stand/subscription-registry.d.ts.map +1 -1
  209. package/dist/stand/subscription-registry.js +15 -11
  210. package/dist/stand/subscription-registry.js.map +1 -1
  211. package/dist/stand/subscription-runtime.d.ts +5 -3
  212. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  213. package/dist/stand/subscription-runtime.js +52 -9
  214. package/dist/stand/subscription-runtime.js.map +1 -1
  215. package/dist/testing/index.d.ts.map +1 -1
  216. package/dist/testing/index.js +4 -3
  217. package/dist/testing/index.js.map +1 -1
  218. package/dist/tsconfig.tsbuildinfo +1 -1
  219. package/package.json +14 -14
  220. package/dist/agent/agent-admission.d.ts +0 -80
  221. package/dist/agent/agent-admission.d.ts.map +0 -1
  222. package/dist/agent/agent-admission.js +0 -127
  223. package/dist/agent/agent-admission.js.map +0 -1
  224. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  225. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  226. package/dist/agent/agent-ai-runtime.js +0 -1269
  227. package/dist/agent/agent-ai-runtime.js.map +0 -1
  228. package/dist/agent/agent-execution-fault.d.ts +0 -18
  229. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  230. package/dist/agent/agent-execution-fault.js.map +0 -1
  231. package/dist/agent/agent-execution-session.d.ts +0 -103
  232. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  233. package/dist/agent/agent-execution-session.js +0 -238
  234. package/dist/agent/agent-execution-session.js.map +0 -1
  235. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  236. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  237. package/dist/agent/agent-interaction-audit.js +0 -114
  238. package/dist/agent/agent-interaction-audit.js.map +0 -1
  239. package/dist/agent/agent-interaction-events.d.ts +0 -40
  240. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  241. package/dist/agent/agent-interaction-events.js +0 -129
  242. package/dist/agent/agent-interaction-events.js.map +0 -1
  243. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  244. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  245. package/dist/agent/agent-mcp-budget.js +0 -566
  246. package/dist/agent/agent-mcp-budget.js.map +0 -1
  247. package/dist/agent/agent-mcp-host.d.ts +0 -125
  248. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  249. package/dist/agent/agent-mcp-host.js.map +0 -1
  250. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  251. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  252. package/dist/agent/agent-read-runtime.d.ts +0 -70
  253. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  254. package/dist/agent/agent-read-runtime.js +0 -320
  255. package/dist/agent/agent-read-runtime.js.map +0 -1
  256. package/dist/agent/agent-revisions.d.ts +0 -35
  257. package/dist/agent/agent-revisions.d.ts.map +0 -1
  258. package/dist/agent/agent-revisions.js +0 -133
  259. package/dist/agent/agent-revisions.js.map +0 -1
  260. package/dist/agent/agent-scheduler.d.ts +0 -99
  261. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  262. package/dist/agent/agent-scheduler.js +0 -398
  263. package/dist/agent/agent-scheduler.js.map +0 -1
  264. package/dist/bus/saved-dispatcher-binding.d.ts +0 -53
  265. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  266. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  267. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  268. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  269. package/dist/delivery/delivery-run-coordinator.js +0 -609
  270. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  271. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  272. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  273. package/dist/delivery/parked-delivery-obligations.js +0 -390
  274. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  275. package/dist/server/environment-attachment.d.ts +0 -356
  276. package/dist/server/environment-attachment.d.ts.map +0 -1
  277. package/dist/server/environment-attachment.js +0 -2062
  278. package/dist/server/environment-attachment.js.map +0 -1
  279. package/dist/server/environment-delivery-records.d.ts +0 -77
  280. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  281. package/dist/server/environment-delivery-records.js +0 -282
  282. package/dist/server/environment-delivery-records.js.map +0 -1
  283. package/dist/server/environment-delivery-worker.d.ts +0 -161
  284. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  285. package/dist/server/environment-delivery-worker.js +0 -973
  286. package/dist/server/environment-delivery-worker.js.map +0 -1
@@ -13,23 +13,24 @@
13
13
  */
14
14
  import { randomUUID } from "node:crypto";
15
15
  import { Code, ConnectError } from "@connectrpc/connect";
16
- import { clone, create, ScalarType, toBinary, } from "@bufbuild/protobuf";
16
+ import { clone, create, ScalarType } from "@bufbuild/protobuf";
17
17
  import { AnySchema, BoolValueSchema, BytesValueSchema, DoubleValueSchema, EmptySchema, FloatValueSchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, UInt32ValueSchema, UInt64ValueSchema, } from "@bufbuild/protobuf/wkt";
18
- import { TypeUrls, AnyMessages } from "@spine-event-engine/core";
19
- import { CommandIdSchema, EventSchema, TenantIdSchema, ValidationErrorSchema, VersionSchema, } from "@spine-event-engine/proto";
18
+ import { TypeUrls, AnyPacker } from "@spine-event-engine/core";
19
+ import { CommandIdSchema, TenantIdSchema, ValidationErrorSchema, VersionSchema, } from "@spine-event-engine/proto";
20
20
  import { ErrorSchema } from "@spine-event-engine/proto";
21
21
  import { CommandService } from "@spine-event-engine/proto/client";
22
22
  import { CompositeFilter_CompositeOperator, Filter_Operator, } from "@spine-event-engine/proto/client";
23
23
  import { EntityStateWithVersionSchema as EntityStateVersionSchema, OrderBy_Direction, QueryResponseSchema, } from "@spine-event-engine/proto/client";
24
24
  import { QueryService } from "@spine-event-engine/proto/client";
25
- import { EntityStateUpdateSchema, EntityUpdatesSchema, EventUpdatesSchema, SubscriptionIdSchema, SubscriptionSchema, SubscriptionUpdateSchema, TopicSchema, } from "@spine-event-engine/proto/client";
25
+ import { SubscriptionIdSchema, SubscriptionSchema, SubscriptionUpdateSchema, TopicSchema, } from "@spine-event-engine/proto/client";
26
26
  import { SubscriptionService } from "@spine-event-engine/proto/client";
27
27
  import { EventOriginFilters } from "../stand/event-origin-filter.js";
28
+ import { TenantAdmissions, TenantUnknownError } from "../context/tenant-unknown-error.js";
28
29
  import { AckSchema } from "@spine-event-engine/proto";
29
30
  import { ResponseSchema, StatusSchema, } from "@spine-event-engine/proto";
30
31
  import {} from "@spine-event-engine/storage";
31
32
  import { TenantBoundary } from "@spine-event-engine/storage/provider";
32
- import { boundedContextAccess } from "../context/bounded-context.js";
33
+ import { BoundedContext, boundedContextAccess } from "../context/bounded-context.js";
33
34
  import { EffectiveTenants } from "../context/effective-tenant.js";
34
35
  import { CommandValidationError } from "../bus/command-errors.js";
35
36
  import { TransitionValidationError } from "../repository/command-errors.js";
@@ -38,7 +39,7 @@ import { QueryReader } from "./query-reader.js";
38
39
  import { RegisteredTargets } from "./registered-targets.js";
39
40
  import { emitServerError, emitServerWarning } from "../server/server-log.js";
40
41
  import { managedChildSubscriptionAccess } from "../server/managed-child-subscription.js";
41
- import { InMemorySubscriptionRegistry, } from "../stand/subscription-registry.js";
42
+ import {} from "../stand/subscription-registry.js";
42
43
  const serviceLoggers = new WeakMap();
43
44
  const serviceInstances = new WeakSet();
44
45
  /**
@@ -86,7 +87,6 @@ export class SpineServices {
86
87
  #activationTails = new Map();
87
88
  #removals = new Map();
88
89
  #unknownRemovals = new Set();
89
- #testRegistries = new WeakMap();
90
90
  #queueLimit;
91
91
  #subscriptionLimit;
92
92
  /**
@@ -171,7 +171,7 @@ export class SpineServices {
171
171
  * @returns Acknowledgement for route validation or dispatch.
172
172
  */
173
173
  async #post(command) {
174
- const messageId = command.id && AnyMessages.pack(CommandIdSchema, command.id, { validate: false });
174
+ const messageId = command.id && AnyPacker.pack(CommandIdSchema, command.id);
175
175
  const typeUrl = command.message?.typeUrl;
176
176
  if (typeUrl === undefined || typeUrl.length === 0)
177
177
  return ServiceValues.commandErrorAck(messageId, "INVALID_COMMAND", "Command message type is required.");
@@ -187,6 +187,8 @@ export class SpineServices {
187
187
  });
188
188
  }
189
189
  catch (error) {
190
+ if (error instanceof TenantUnknownError)
191
+ throw TenantStatuses.unknown(error);
190
192
  const postError = ServiceValues.commandPostError(error);
191
193
  this.#logCommandPostFailure(route, command, error);
192
194
  return create(AckSchema, {
@@ -277,7 +279,9 @@ export class SpineServices {
277
279
  const destination = EffectiveTenants.destination(effective, route.context.isMultitenant);
278
280
  return await this.#query(route, ServiceValues.createReadPlan(target, query, route), destination);
279
281
  }
280
- catch {
282
+ catch (error) {
283
+ if (error instanceof TenantUnknownError)
284
+ throw TenantStatuses.unknown(error);
281
285
  return ServiceValues.queryErrorResponse("QUERY_READ_ERROR", "Query read failed.");
282
286
  }
283
287
  }
@@ -291,10 +295,16 @@ export class SpineServices {
291
295
  message: results.map((result) => ServiceValues.packVersionedState(route.schema, result)),
292
296
  });
293
297
  }
298
+ /**
299
+ * Validates a topic synchronously and checks live membership before registry creation.
300
+ *
301
+ * @param topic Requested subscription topic.
302
+ * @returns Created subscription after admission.
303
+ */
294
304
  #subscribe(topic) {
295
305
  const route = this.#subscriptionRoute(topic);
296
306
  ServiceValues.validateTopic(topic);
297
- const shape = ServiceValues.createSubscriptionShape(topic, route);
307
+ ServiceValues.createSubscriptionShape(topic, route);
298
308
  const tenantId = ServiceValues.topicTenant(topic);
299
309
  const tenantError = ServiceValues.tenantMismatch(route.context.isMultitenant, tenantId, "subscription");
300
310
  if (tenantError !== undefined) {
@@ -308,12 +318,10 @@ export class SpineServices {
308
318
  if (id === undefined) {
309
319
  throw new ConnectError("Subscription ID is required.", Code.InvalidArgument);
310
320
  }
311
- void shape;
312
- const registry = this.#subscriptionRegistry(route.context);
313
- if (registry === undefined) {
314
- throw new ConnectError("Subscription registry is unavailable.", Code.FailedPrecondition);
315
- }
316
- return registry.create(subscription).then(() => clone(SubscriptionSchema, subscription));
321
+ const registry = boundedContextAccess.subscriptionRegistry(route.context);
322
+ return TenantStatuses.require(route.context, tenantId, async () => {
323
+ await registry.create(subscription);
324
+ }).then(() => clone(SubscriptionSchema, subscription));
317
325
  }
318
326
  async *#activate(subscription) {
319
327
  const id = subscription.id?.value;
@@ -363,13 +371,28 @@ export class SpineServices {
363
371
  */
364
372
  async #startActivationRecord(id) {
365
373
  const record = this.#subscriptions.get(id) ?? (await this.#findSubscription(id));
366
- if (record === undefined || record.delivery.active)
374
+ if (record === undefined)
375
+ return undefined;
376
+ let attached;
377
+ await this.#requireSubscriptionTenant(record, async () => {
378
+ attached = await this.#activateKnownRecord(record, id);
379
+ });
380
+ return attached;
381
+ }
382
+ /**
383
+ * Activates and attaches one admitted record before its registry closes.
384
+ *
385
+ * @param record Discovered subscription record.
386
+ * @param id Canonical subscription ID.
387
+ * @returns Attached record, if activation remains eligible.
388
+ */
389
+ async #activateKnownRecord(record, id) {
390
+ if (record.delivery.active)
367
391
  return undefined;
368
- const activation = await this.#subscriptionRegistry(record.route.context)?.activate(create(SubscriptionIdSchema, { value: id }));
369
- if (activation === undefined ||
370
- activation.kind === "missing" ||
371
- activation.kind === "expired" ||
372
- record.delivery.closed)
392
+ const activation = await boundedContextAccess
393
+ .subscriptionRegistry(record.route.context)
394
+ .activate(create(SubscriptionIdSchema, { value: id }));
395
+ if (activation.kind === "missing" || activation.kind === "expired" || record.delivery.closed)
373
396
  return undefined;
374
397
  try {
375
398
  this.#subscriptions.set(id, record);
@@ -404,12 +427,41 @@ export class SpineServices {
404
427
  this.#activationTails.delete(id);
405
428
  }
406
429
  }
430
+ /**
431
+ * Leaves an absent subscription ID inert and checks known records before removal.
432
+ *
433
+ * @param subscription Subscription identity supplied by the caller.
434
+ * @returns Cancellation response after any required membership check.
435
+ */
407
436
  #cancel(subscription) {
408
437
  const id = subscription.id?.value;
409
438
  if (id === undefined) {
410
439
  return ServiceValues.okResponse();
411
440
  }
412
- return this.#removeSubscription(id).then(() => ServiceValues.okResponse());
441
+ return this.#cancelKnown(id);
442
+ }
443
+ /**
444
+ * Checks the canonical record's tenant before cancellation side effects.
445
+ *
446
+ * @param id Subscription ID with a possible shared-registry record.
447
+ * @returns Successful cancellation response after removal.
448
+ */
449
+ async #cancelKnown(id) {
450
+ const record = this.#subscriptions.get(id) ?? (await this.#findSubscription(id));
451
+ if (record === undefined)
452
+ await this.#removeSubscription(id);
453
+ else
454
+ await this.#requireSubscriptionTenant(record, () => this.#removeSubscription(id));
455
+ return ServiceValues.okResponse();
456
+ }
457
+ /**
458
+ * Checks the discovered subscription tenant before tenant-specific control work.
459
+ *
460
+ * @param record Canonical shared-registry subscription record.
461
+ * @param onReady Complete admitted one-shot mutation, if present.
462
+ */
463
+ async #requireSubscriptionTenant(record, onReady) {
464
+ await TenantStatuses.require(record.route.context, record.tenantId, onReady);
413
465
  }
414
466
  #subscriptionRoute(topic) {
415
467
  const target = topic.target;
@@ -422,66 +474,13 @@ export class SpineServices {
422
474
  return route;
423
475
  }
424
476
  async #activateRecord(record) {
425
- const registry = this.#subscriptionRegistry(record.route.context);
426
- if (registry === undefined) {
427
- this.#createSubscriptionAttachment(record);
428
- managedChildSubscriptionAccess.installed(record.id);
429
- return;
430
- }
431
- try {
432
- const consumer = await boundedContextAccess.consumeSubscription(record.route.context, record.id, (update) => {
433
- record.delivery.push(update);
434
- if (record.delivery.closed) {
435
- this.#removeDetachedSubscription(record.id);
436
- }
437
- });
438
- record.delivery.attach(consumer);
439
- }
440
- catch (error) {
441
- if (!(error instanceof TypeError) ||
442
- (!error.message.includes("requires a Stand instance") &&
443
- !error.message.includes("requires a built BoundedContext instance"))) {
444
- throw error;
445
- }
446
- this.#createSubscriptionAttachment(record);
447
- }
448
- managedChildSubscriptionAccess.installed(record.id);
449
- }
450
- #createSubscriptionAttachment(record) {
451
- if (record.kind === "event")
452
- return this.#attachEventSubscription(record);
453
- const stateRecord = record;
454
- const standSubscription = stateRecord.route.context.stand().subscribe(stateRecord.route.schema, (update) => {
455
- const subscriptionUpdate = ServiceValues.createEntityUpdate(stateRecord, update);
456
- if (subscriptionUpdate !== undefined) {
457
- stateRecord.delivery.push(subscriptionUpdate);
458
- }
459
- if (stateRecord.delivery.closed) {
460
- this.#removeDetachedSubscription(stateRecord.id);
461
- }
462
- }, ServiceValues.tenantOptions(stateRecord.tenantId));
463
- stateRecord.delivery.attach(standSubscription);
464
- return standSubscription;
465
- }
466
- /**
467
- * Attaches one filtered Event observer to its bounded context.
468
- * @param record The tracked Event subscription.
469
- * @returns The attached Event observer.
470
- */
471
- #attachEventSubscription(record) {
472
- const eventSubscription = boundedContextAccess.subscribeToEvent(record.route.context, record.route.typeUrl, {
473
- onEvent: (event) => {
474
- if (!ServiceValues.eventTenantMatches(record, event))
475
- return;
476
- if (record.eventMatcher !== undefined && !record.eventMatcher(event))
477
- return;
478
- record.delivery.push(ServiceValues.createEventUpdate(record, event));
479
- if (record.delivery.closed)
480
- this.#removeDetachedSubscription(record.id);
481
- },
477
+ const consumer = await boundedContextAccess.consumeSubscription(record.route.context, record.id, (update) => {
478
+ record.delivery.push(update);
479
+ if (record.delivery.closed)
480
+ this.#removeDetachedSubscription(record.id);
482
481
  });
483
- record.delivery.attach(eventSubscription);
484
- return eventSubscription;
482
+ record.delivery.attach(consumer);
483
+ managedChildSubscriptionAccess.installed(record.id);
485
484
  }
486
485
  #removeSubscription(id) {
487
486
  const existing = this.#removals.get(id);
@@ -521,15 +520,12 @@ export class SpineServices {
521
520
  async #runRemoval(id, removal, record, unknown) {
522
521
  try {
523
522
  if (record !== undefined) {
524
- await this.#subscriptionRegistry(record.route.context)?.delete(create(SubscriptionIdSchema, { value: id }));
523
+ await boundedContextAccess
524
+ .subscriptionRegistry(record.route.context)
525
+ .delete(create(SubscriptionIdSchema, { value: id }));
525
526
  }
526
527
  else {
527
- await Promise.all(ServiceValues.uniqueContexts(this.#contexts).flatMap((context) => {
528
- const registry = this.#subscriptionRegistry(context);
529
- return registry === undefined
530
- ? []
531
- : [registry.delete(create(SubscriptionIdSchema, { value: id }))];
532
- }));
528
+ await this.#deleteUnknownSubscription(id);
533
529
  }
534
530
  removal.resolve();
535
531
  }
@@ -545,10 +541,25 @@ export class SpineServices {
545
541
  }
546
542
  }
547
543
  }
544
+ /**
545
+ * Deletes an unknown subscription from every configured context registry.
546
+ *
547
+ * @param id Subscription ID to remove.
548
+ */
549
+ async #deleteUnknownSubscription(id) {
550
+ await Promise.all(ServiceValues.uniqueContexts(this.#contexts).map((context) => this.#registryOperation(context, async () => {
551
+ await boundedContextAccess
552
+ .subscriptionRegistry(context)
553
+ .delete(create(SubscriptionIdSchema, { value: id }));
554
+ })));
555
+ }
548
556
  async #findSubscription(id) {
549
557
  const subscriptionId = create(SubscriptionIdSchema, { value: id });
550
558
  for (const context of ServiceValues.uniqueContexts(this.#contexts)) {
551
- const entry = await this.#subscriptionRegistry(context)?.get(subscriptionId);
559
+ let entry;
560
+ await this.#registryOperation(context, async () => {
561
+ entry = await boundedContextAccess.subscriptionRegistry(context).get(subscriptionId);
562
+ });
552
563
  const subscription = entry?.subscription;
553
564
  const topic = subscription?.topic;
554
565
  if (entry === undefined || subscription === undefined || topic === undefined)
@@ -571,24 +582,15 @@ export class SpineServices {
571
582
  }
572
583
  return undefined;
573
584
  }
574
- #subscriptionRegistry(context) {
575
- if (context === undefined) {
576
- return undefined;
577
- }
578
- try {
579
- return boundedContextAccess.subscriptionRegistry(context);
580
- }
581
- catch {
582
- // Direct route tests may use structural context doubles. Production
583
- // contexts always provide their configured registry through the access seam.
584
- const key = context;
585
- let registry = this.#testRegistries.get(key);
586
- if (registry === undefined) {
587
- registry = new InMemorySubscriptionRegistry();
588
- this.#testRegistries.set(key, registry);
589
- }
590
- return registry;
591
- }
585
+ /**
586
+ * Retains one short registry read or delete through the context close drain.
587
+ *
588
+ * @param context Context selected by the shared subscription route.
589
+ * @param onOperation Complete registry callback.
590
+ * @returns Completion before the selected registry may close.
591
+ */
592
+ #registryOperation(context, onOperation) {
593
+ return boundedContextAccess.admitServiceRegistryOperation(context, onOperation);
592
594
  }
593
595
  }
594
596
  /**
@@ -822,7 +824,8 @@ function validateQueryIds(ids, route) {
822
824
  if (schema === undefined || ids === undefined) {
823
825
  return undefined;
824
826
  }
825
- if (ids.some((id) => id === undefined || AnyMessages.unpack(id, schema) === undefined)) {
827
+ if (ids.some((id) => id === undefined ||
828
+ (schema.typeName !== AnySchema.typeName && AnyPacker.tryUnpack(id, schema) === undefined))) {
826
829
  return invalidQueryError(`QueryService.Read id_filter values must pack ${schema.typeName}.`);
827
830
  }
828
831
  return undefined;
@@ -933,7 +936,7 @@ function normalizedFilters(filters, route) {
933
936
  }
934
937
  function decodeQueryIdValue(value, route) {
935
938
  const schema = route.idField.message;
936
- return schema === undefined ? decodeAnyValue(value) : AnyMessages.unpack(value, schema);
939
+ return schema === undefined ? decodeAnyValue(value) : decodeAnyValue(value, schema);
937
940
  }
938
941
  function normalizedComposite(filter, route) {
939
942
  const predicates = [
@@ -965,13 +968,13 @@ function requiredComparison(filter) {
965
968
  function decodeColumnValue(filter, route) {
966
969
  const column = filter.fieldPath?.fieldName[0] ?? "";
967
970
  if (column === "version" && filter.value !== undefined) {
968
- const value = AnyMessages.unpack(filter.value, VersionSchema);
971
+ const value = AnyPacker.tryUnpack(filter.value, VersionSchema);
969
972
  if (value !== undefined)
970
973
  return value;
971
974
  throw new TypeError('Validated query column "version" has an invalid value.');
972
975
  }
973
976
  if ((column === "archived" || column === "deleted") && filter.value !== undefined) {
974
- const value = AnyMessages.unpack(filter.value, BoolValueSchema);
977
+ const value = AnyPacker.tryUnpack(filter.value, BoolValueSchema);
975
978
  if (value !== undefined)
976
979
  return value.value;
977
980
  throw new TypeError(`Validated query column "${column}" has an invalid value.`);
@@ -981,7 +984,8 @@ function decodeColumnValue(filter, route) {
981
984
  }
982
985
  function filterValueMatches(field, value) {
983
986
  const schema = filterValueSchema(field);
984
- if (schema === undefined || value.typeUrl !== TypeUrls.derive(schema))
987
+ if (schema === undefined ||
988
+ (schema.typeName !== AnySchema.typeName && value.typeUrl !== TypeUrls.derive(schema)))
985
989
  return false;
986
990
  const decoded = field.fieldKind === "message" ? decodeAnyValue(value, schema) : decodeAnyValue(value);
987
991
  if (field.fieldKind === "message") {
@@ -1039,11 +1043,11 @@ function filterValueSchema(field) {
1039
1043
  function systemValueMatches(column, value) {
1040
1044
  if (column === "version") {
1041
1045
  return (value.typeUrl === TypeUrls.derive(VersionSchema) &&
1042
- AnyMessages.unpack(value, VersionSchema) !== undefined);
1046
+ AnyPacker.tryUnpack(value, VersionSchema) !== undefined);
1043
1047
  }
1044
1048
  if (column === "archived" || column === "deleted") {
1045
1049
  return (value.typeUrl === TypeUrls.derive(BoolValueSchema) &&
1046
- AnyMessages.unpack(value, BoolValueSchema) !== undefined);
1050
+ AnyPacker.tryUnpack(value, BoolValueSchema) !== undefined);
1047
1051
  }
1048
1052
  return false;
1049
1053
  }
@@ -1154,7 +1158,7 @@ function validateTopic(topic) {
1154
1158
  }
1155
1159
  }
1156
1160
  /**
1157
- * Validates a topic and prepares complete state or event delivery.
1161
+ * Validates a topic and returns its state or Event subscription shape.
1158
1162
  */
1159
1163
  function createSubscriptionShape(topic, route) {
1160
1164
  const target = topic.target;
@@ -1162,21 +1166,18 @@ function createSubscriptionShape(topic, route) {
1162
1166
  throw new ConnectError("Subscription topic target is required.", Code.InvalidArgument);
1163
1167
  }
1164
1168
  if (route.kind === "event") {
1165
- const eventMatcher = validateEventSubscriptionTopic(target);
1166
- return { kind: "event", route, ...(eventMatcher === undefined ? {} : { eventMatcher }) };
1169
+ validateEventSubscriptionTopic(target);
1170
+ return { kind: "event", route };
1167
1171
  }
1168
1172
  switch (target.criterion.case) {
1169
1173
  case "includeAll":
1170
1174
  if (!target.criterion.value) {
1171
1175
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1172
1176
  }
1173
- return { kind: "state", route, matcher: { match: () => "state" } };
1177
+ return { kind: "state", route };
1174
1178
  case "filters":
1175
- return {
1176
- kind: "state",
1177
- route,
1178
- matcher: createFilteredSubscriptionMatcher(target.criterion.value, route),
1179
- };
1179
+ ServiceValues.validateFilteredSubscriptionTopic(target.criterion.value, route);
1180
+ return { kind: "state", route };
1180
1181
  default:
1181
1182
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1182
1183
  }
@@ -1190,44 +1191,14 @@ function validateEventSubscriptionTopic(target) {
1190
1191
  if (!target.criterion.value) {
1191
1192
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1192
1193
  }
1193
- return undefined;
1194
+ return;
1194
1195
  case "filters":
1195
- return EventOriginFilters.compile(target.criterion.value);
1196
+ EventOriginFilters.compile(target.criterion.value);
1197
+ return;
1196
1198
  default:
1197
1199
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1198
1200
  }
1199
1201
  }
1200
- /**
1201
- * Matches complete state updates against a topic's ID and field filters.
1202
- */
1203
- function createFilteredSubscriptionMatcher(filters, route) {
1204
- ServiceValues.validateSubscriptionFilters(filters);
1205
- const idValues = filters.idFilter?.id.map((id) => decodeSubscriptionIdValue(id, route));
1206
- const statePredicate = createSubscriptionPredicate(filters.filter, route);
1207
- return {
1208
- /**
1209
- * Matches one state update against the topic's ID and field filters.
1210
- *
1211
- * @param update State update to inspect.
1212
- * @returns Delivery kind, if the update changes a matching state.
1213
- */
1214
- match(update) {
1215
- if (idValues !== undefined &&
1216
- !idValues.some((id) => valuesEqual(update.id, id, route.idField.message))) {
1217
- return undefined;
1218
- }
1219
- if (statePredicate(update.state))
1220
- return "state";
1221
- if (update.previousState !== undefined && statePredicate(update.previousState))
1222
- return "noLongerMatching";
1223
- return undefined;
1224
- },
1225
- };
1226
- }
1227
- function createSubscriptionPredicate(filters, route) {
1228
- const predicates = filters.map((filter) => createCompositePredicate(filter, route));
1229
- return (state) => predicates.every((predicate) => predicate(state));
1230
- }
1231
1202
  function validateSubscriptionFilterTree(filters) {
1232
1203
  if (filters.length > SubscriptionLimits.compositeCount) {
1233
1204
  throw new ConnectError("SubscriptionService.Subscribe may contain at most 8 composite filters.", Code.InvalidArgument);
@@ -1284,30 +1255,6 @@ function enqueueSubscriptionFilterChildren(filter, depth, stack) {
1284
1255
  stack.push({ filter: nested, depth: depth + 1 });
1285
1256
  }
1286
1257
  }
1287
- function createCompositePredicate(filter, route) {
1288
- const children = [
1289
- ...filter.filter.map((simple) => createSimplePredicate(simple, route)),
1290
- ...filter.compositeFilter.map((nested) => createCompositePredicate(nested, route)),
1291
- ];
1292
- if (children.length === 0) {
1293
- return () => true;
1294
- }
1295
- return filter.operator === CompositeFilter_CompositeOperator.ALL
1296
- ? (state) => children.every((predicate) => predicate(state))
1297
- : (state) => children.some((predicate) => predicate(state));
1298
- }
1299
- function createSimplePredicate(filter, route) {
1300
- if (filter.operator !== Filter_Operator.EQUAL) {
1301
- throw new ConnectError("SubscriptionService.Subscribe supports only EQUAL field filters.", Code.InvalidArgument);
1302
- }
1303
- if (filter.value === undefined) {
1304
- throw new ConnectError("SubscriptionService.Subscribe field filter value is required.", Code.InvalidArgument);
1305
- }
1306
- validateSubscriptionPath(filter.fieldPath?.fieldName ?? [], "field filter");
1307
- const resolved = resolveMessagePath(route.schema, filter.fieldPath?.fieldName ?? [], "field filter");
1308
- const expected = decodeFieldFilterValue(filter.value, resolved.leafSchema);
1309
- return (state) => valuesEqual(readPathValue(state, resolved.localPath), expected, resolved.leafSchema);
1310
- }
1311
1258
  /**
1312
1259
  * Checks a state filter path before resolving its fields.
1313
1260
  */
@@ -1329,7 +1276,9 @@ function decodeSubscriptionIdValue(value, route) {
1329
1276
  if (route.idField.message === undefined) {
1330
1277
  return decodeAnyValue(value);
1331
1278
  }
1332
- const decoded = AnyMessages.unpack(value, route.idField.message);
1279
+ const decoded = route.idField.message.typeName === AnySchema.typeName
1280
+ ? value
1281
+ : AnyPacker.tryUnpack(value, route.idField.message);
1333
1282
  if (decoded === undefined) {
1334
1283
  throw new ConnectError(`SubscriptionService.Subscribe id_filter values must pack ${route.idField.message.typeName}.`, Code.InvalidArgument);
1335
1284
  }
@@ -1339,7 +1288,7 @@ function decodeFieldFilterValue(value, schema) {
1339
1288
  if (value === undefined || schema === undefined) {
1340
1289
  return decodeAnyValue(value);
1341
1290
  }
1342
- const decoded = AnyMessages.unpack(value, schema);
1291
+ const decoded = schema.typeName === AnySchema.typeName ? value : AnyPacker.tryUnpack(value, schema);
1343
1292
  if (decoded === undefined) {
1344
1293
  throw new ConnectError(`SubscriptionService.Subscribe field filter value must pack ${schema.typeName}.`, Code.InvalidArgument);
1345
1294
  }
@@ -1350,7 +1299,9 @@ function decodeAnyValue(value, schema) {
1350
1299
  return undefined;
1351
1300
  }
1352
1301
  if (schema !== undefined) {
1353
- const decoded = AnyMessages.unpack(value, schema);
1302
+ if (schema.typeName === AnySchema.typeName)
1303
+ return value;
1304
+ const decoded = AnyPacker.tryUnpack(value, schema);
1354
1305
  if (decoded !== undefined) {
1355
1306
  return decoded;
1356
1307
  }
@@ -1367,7 +1318,6 @@ function decodeAnyValue(value, schema) {
1367
1318
  * Resolves a state filter path to generated message fields.
1368
1319
  */
1369
1320
  function resolveMessagePath(schema, fieldPath, label) {
1370
- const localPath = [];
1371
1321
  let currentSchema = schema;
1372
1322
  let leafSchema;
1373
1323
  for (const [index, name] of fieldPath.entries()) {
@@ -1375,7 +1325,6 @@ function resolveMessagePath(schema, fieldPath, label) {
1375
1325
  if (field === undefined) {
1376
1326
  throw new ConnectError(`SubscriptionService.Subscribe ${label} "${fieldPath.join(".")}" is not a state field.`, Code.InvalidArgument);
1377
1327
  }
1378
- localPath.push(field.localName);
1379
1328
  leafSchema = field.message;
1380
1329
  currentSchema = index === fieldPath.length - 1 ? undefined : field.message;
1381
1330
  if (currentSchema === undefined && index < fieldPath.length - 1) {
@@ -1383,7 +1332,6 @@ function resolveMessagePath(schema, fieldPath, label) {
1383
1332
  }
1384
1333
  }
1385
1334
  return Object.freeze({
1386
- localPath,
1387
1335
  ...(leafSchema === undefined ? {} : { leafSchema }),
1388
1336
  });
1389
1337
  }
@@ -1391,37 +1339,12 @@ function findMessageField(schema, name) {
1391
1339
  const fields = (schema?.fields ?? []);
1392
1340
  return fields.find((field) => field.name === name || field.localName === name);
1393
1341
  }
1394
- function readPathValue(value, path) {
1395
- let current = value;
1396
- for (const segment of path) {
1397
- if (typeof current !== "object" || current === null) {
1398
- return undefined;
1399
- }
1400
- current = Reflect.get(current, segment);
1401
- }
1402
- return current;
1403
- }
1404
- function valuesEqual(actual, expected, schema) {
1405
- if (schema !== undefined && isMessage(actual) && isMessage(expected)) {
1406
- return bytesEqual(toBinary(schema, actual), toBinary(schema, expected));
1407
- }
1408
- if (isAny(actual) && isAny(expected)) {
1409
- return bytesEqual(toBinary(AnySchema, actual), toBinary(AnySchema, expected));
1410
- }
1411
- if (actual instanceof Uint8Array && expected instanceof Uint8Array) {
1412
- return bytesEqual(actual, expected);
1413
- }
1414
- return Object.is(actual, expected);
1415
- }
1416
1342
  function isMessage(value) {
1417
1343
  return typeof value === "object" && value !== null && "$typeName" in value;
1418
1344
  }
1419
1345
  function isAny(value) {
1420
1346
  return isMessage(value) && value.$typeName === "google.protobuf.Any";
1421
1347
  }
1422
- function bytesEqual(left, right) {
1423
- return left.length === right.length && left.every((byte, index) => byte === right[index]);
1424
- }
1425
1348
  /**
1426
1349
  * Packs a state snapshot and version for a public query result.
1427
1350
  *
@@ -1432,7 +1355,7 @@ function bytesEqual(left, right) {
1432
1355
  */
1433
1356
  function packVersionedState(schema, result) {
1434
1357
  return create(EntityStateVersionSchema, {
1435
- state: AnyMessages.pack(schema, result.state, { validate: false }),
1358
+ state: AnyPacker.pack(schema, result.state),
1436
1359
  version: result.version ?? create(VersionSchema),
1437
1360
  });
1438
1361
  }
@@ -1442,22 +1365,6 @@ function commandTenant(command) {
1442
1365
  function topicTenant(topic) {
1443
1366
  return tenantValue(topic?.context?.tenantId);
1444
1367
  }
1445
- function eventTenant(event) {
1446
- switch (event.context?.origin.case) {
1447
- case "importContext":
1448
- return tenantValue(event.context.origin.value.tenantId);
1449
- case "pastMessage":
1450
- return tenantValue(event.context.origin.value.actorContext?.tenantId);
1451
- default:
1452
- return undefined;
1453
- }
1454
- }
1455
- function eventTenantMatches(record, event) {
1456
- const tenantId = eventTenant(event);
1457
- return (record.tenantId === undefined ||
1458
- (tenantId !== undefined &&
1459
- TenantBoundary.from(tenantId).key === TenantBoundary.from(record.tenantId).key));
1460
- }
1461
1368
  function tenantValue(tenantId) {
1462
1369
  if (tenantId === undefined)
1463
1370
  return undefined;
@@ -1478,6 +1385,53 @@ function stateRouteIdField(schema, idField) {
1478
1385
  }
1479
1386
  return field;
1480
1387
  }
1388
+ /**
1389
+ * Maps confirmed tenant absence to native service errors.
1390
+ */
1391
+ const TenantStatuses = Object.freeze({
1392
+ /**
1393
+ * Checks live membership and maps only confirmed missing membership to NotFound.
1394
+ *
1395
+ * @param context Receiving context and its selected index.
1396
+ * @param tenantId Complete selected tenant identity.
1397
+ * @param onReady Complete one-shot mutation after membership and preparation.
1398
+ * @returns Completion after membership and any mutation settle.
1399
+ */
1400
+ async require(context, tenantId, onReady) {
1401
+ try {
1402
+ const runtimeContext = context;
1403
+ if (runtimeContext instanceof BoundedContext)
1404
+ await boundedContextAccess.admitServiceInput(runtimeContext, tenantId, () => TenantAdmissions.require(context.tenantIndex, context.name.value, context.isMultitenant, tenantId), onReady);
1405
+ else {
1406
+ await TenantAdmissions.require(context.tenantIndex, context.name.value, context.isMultitenant, tenantId);
1407
+ await onReady?.();
1408
+ }
1409
+ }
1410
+ catch (error) {
1411
+ if (error instanceof TenantUnknownError)
1412
+ throw TenantStatuses.unknown(error);
1413
+ throw error;
1414
+ }
1415
+ },
1416
+ /**
1417
+ * Exposes a confirmed unknown tenant as a native NotFound RPC with Spine details.
1418
+ *
1419
+ * @param error Receiving-context membership failure.
1420
+ * @returns Connect failure preserving its stable Spine error type.
1421
+ */
1422
+ unknown(error) {
1423
+ return new ConnectError(error.message, Code.NotFound, undefined, [
1424
+ {
1425
+ desc: ErrorSchema,
1426
+ value: create(ErrorSchema, {
1427
+ type: error.type,
1428
+ code: Code.NotFound,
1429
+ message: error.message,
1430
+ }),
1431
+ },
1432
+ ], error);
1433
+ },
1434
+ });
1481
1435
  function tenantMismatch(multitenant, tenantId, subject) {
1482
1436
  if (multitenant) {
1483
1437
  return tenantId === undefined
@@ -1499,18 +1453,14 @@ function commandPostError(error) {
1499
1453
  return {
1500
1454
  type: error.type,
1501
1455
  message: error.clientMessage,
1502
- details: AnyMessages.pack(ValidationErrorSchema, error.validationError, {
1503
- validate: false,
1504
- }),
1456
+ details: AnyPacker.pack(ValidationErrorSchema, error.validationError),
1505
1457
  };
1506
1458
  }
1507
1459
  if (error instanceof CommandValidationError) {
1508
1460
  return {
1509
1461
  type: "COMMAND_VALIDATION_ERROR",
1510
1462
  message: "Command payload validation failed.",
1511
- details: AnyMessages.pack(ValidationErrorSchema, error.validationError, {
1512
- validate: false,
1513
- }),
1463
+ details: AnyPacker.pack(ValidationErrorSchema, error.validationError),
1514
1464
  };
1515
1465
  }
1516
1466
  return {
@@ -1578,104 +1528,17 @@ const InternalErrorFacts = Object.freeze({
1578
1528
  }
1579
1529
  },
1580
1530
  });
1581
- function tenantOptions(tenantId) {
1582
- return tenantId === undefined ? {} : { tenantId: clone(TenantIdSchema, tenantId) };
1583
- }
1584
1531
  const VALUE_DECODERS = Object.freeze([
1585
- (value) => AnyMessages.unpack(value, StringValueSchema)?.value,
1586
- (value) => AnyMessages.unpack(value, BoolValueSchema)?.value,
1587
- (value) => AnyMessages.unpack(value, Int32ValueSchema)?.value,
1588
- (value) => AnyMessages.unpack(value, UInt32ValueSchema)?.value,
1589
- (value) => AnyMessages.unpack(value, Int64ValueSchema)?.value,
1590
- (value) => AnyMessages.unpack(value, UInt64ValueSchema)?.value,
1591
- (value) => AnyMessages.unpack(value, FloatValueSchema)?.value,
1592
- (value) => AnyMessages.unpack(value, DoubleValueSchema)?.value,
1593
- (value) => AnyMessages.unpack(value, BytesValueSchema)?.value,
1532
+ (value) => AnyPacker.tryUnpack(value, StringValueSchema)?.value,
1533
+ (value) => AnyPacker.tryUnpack(value, BoolValueSchema)?.value,
1534
+ (value) => AnyPacker.tryUnpack(value, Int32ValueSchema)?.value,
1535
+ (value) => AnyPacker.tryUnpack(value, UInt32ValueSchema)?.value,
1536
+ (value) => AnyPacker.tryUnpack(value, Int64ValueSchema)?.value,
1537
+ (value) => AnyPacker.tryUnpack(value, UInt64ValueSchema)?.value,
1538
+ (value) => AnyPacker.tryUnpack(value, FloatValueSchema)?.value,
1539
+ (value) => AnyPacker.tryUnpack(value, DoubleValueSchema)?.value,
1540
+ (value) => AnyPacker.tryUnpack(value, BytesValueSchema)?.value,
1594
1541
  ]);
1595
- /**
1596
- * Builds a subscription update with a complete state or a removal signal.
1597
- */
1598
- function createEntityUpdate(record, update) {
1599
- const matcher = record.matcher;
1600
- if (matcher === undefined)
1601
- return undefined;
1602
- const match = matcher.match(update);
1603
- if (match === undefined)
1604
- return undefined;
1605
- const kind = match === "state"
1606
- ? {
1607
- case: "state",
1608
- value: AnyMessages.pack(record.route.schema, clone(record.route.schema, update.state), {
1609
- validate: false,
1610
- }),
1611
- }
1612
- : { case: "noLongerMatching", value: true };
1613
- return create(SubscriptionUpdateSchema, {
1614
- subscription: clone(SubscriptionSchema, record.subscription),
1615
- response: okResponse(),
1616
- update: {
1617
- case: "entityUpdates",
1618
- value: create(EntityUpdatesSchema, {
1619
- update: [
1620
- create(EntityStateUpdateSchema, {
1621
- id: packEntityId(record.route, update.id),
1622
- kind,
1623
- }),
1624
- ],
1625
- }),
1626
- },
1627
- });
1628
- }
1629
- function createEventUpdate(record, event) {
1630
- return create(SubscriptionUpdateSchema, {
1631
- subscription: clone(SubscriptionSchema, record.subscription),
1632
- response: okResponse(),
1633
- update: {
1634
- case: "eventUpdates",
1635
- value: create(EventUpdatesSchema, {
1636
- event: [cloneClientEvent(event)],
1637
- }),
1638
- },
1639
- });
1640
- }
1641
- function cloneClientEvent(event) {
1642
- const clientEvent = clone(EventSchema, event);
1643
- const rejection = clientEvent.context?.rejection;
1644
- if (rejection !== undefined) {
1645
- rejection.command = undefined;
1646
- // Clear the legacy wire payload as part of client-side security redaction.
1647
- // eslint-disable-next-line @typescript-eslint/no-deprecated
1648
- rejection.commandMessage = undefined;
1649
- rejection.stacktrace = "";
1650
- }
1651
- return clientEvent;
1652
- }
1653
- function packString(value) {
1654
- return AnyMessages.pack(StringValueSchema, create(StringValueSchema, { value }));
1655
- }
1656
- function packEntityId(route, id) {
1657
- if (isAny(id)) {
1658
- return clone(AnySchema, id);
1659
- }
1660
- if (route.idField.message !== undefined && isMessage(id)) {
1661
- return AnyMessages.pack(route.idField.message, id, { validate: false });
1662
- }
1663
- if (id instanceof Uint8Array) {
1664
- return AnyMessages.pack(BytesValueSchema, create(BytesValueSchema, { value: id }));
1665
- }
1666
- switch (typeof id) {
1667
- case "string":
1668
- return packString(id);
1669
- case "boolean":
1670
- return AnyMessages.pack(BoolValueSchema, create(BoolValueSchema, { value: id }));
1671
- case "number":
1672
- return AnyMessages.pack(DoubleValueSchema, create(DoubleValueSchema, { value: id }));
1673
- case "bigint":
1674
- return AnyMessages.pack(Int64ValueSchema, create(Int64ValueSchema, { value: id }));
1675
- default:
1676
- return undefined;
1677
- }
1678
- }
1679
1542
  /**
1680
1543
  * Groups non-public service contract, query, and subscription operations.
1681
1544
  */
@@ -1733,6 +1596,48 @@ const ServiceValues = Object.freeze({
1733
1596
  }
1734
1597
  return undefined;
1735
1598
  },
1599
+ /**
1600
+ * Validates state ID and field filters before storing the topic.
1601
+ *
1602
+ * @param filters The topic's declared filters.
1603
+ * @param route The registered state route.
1604
+ */
1605
+ validateFilteredSubscriptionTopic(filters, route) {
1606
+ ServiceValues.validateSubscriptionFilters(filters);
1607
+ for (const id of filters.idFilter?.id ?? [])
1608
+ decodeSubscriptionIdValue(id, route);
1609
+ for (const filter of filters.filter)
1610
+ ServiceValues.validateCompositeFilter(filter, route);
1611
+ },
1612
+ /**
1613
+ * Validates nested state filter values without retaining an observer predicate.
1614
+ *
1615
+ * @param filter A validated composite filter.
1616
+ * @param route The registered state route.
1617
+ */
1618
+ validateCompositeFilter(filter, route) {
1619
+ for (const simple of filter.filter)
1620
+ ServiceValues.validateSimpleFilter(simple, route);
1621
+ for (const nested of filter.compositeFilter)
1622
+ ServiceValues.validateCompositeFilter(nested, route);
1623
+ },
1624
+ /**
1625
+ * Validates one field path and packed comparison value.
1626
+ *
1627
+ * @param filter A simple field filter.
1628
+ * @param route The registered state route.
1629
+ */
1630
+ validateSimpleFilter(filter, route) {
1631
+ if (filter.operator !== Filter_Operator.EQUAL) {
1632
+ throw new ConnectError("SubscriptionService.Subscribe supports only EQUAL field filters.", Code.InvalidArgument);
1633
+ }
1634
+ if (filter.value === undefined) {
1635
+ throw new ConnectError("SubscriptionService.Subscribe field filter value is required.", Code.InvalidArgument);
1636
+ }
1637
+ validateSubscriptionPath(filter.fieldPath?.fieldName ?? [], "field filter");
1638
+ const resolved = resolveMessagePath(route.schema, filter.fieldPath?.fieldName ?? [], "field filter");
1639
+ decodeFieldFilterValue(filter.value, resolved.leafSchema);
1640
+ },
1736
1641
  /**
1737
1642
  * Checks that a state topic has a usable ID or field filter.
1738
1643
  *
@@ -1763,8 +1668,6 @@ const ServiceValues = Object.freeze({
1763
1668
  return create(AckSchema, { messageId, status: errorStatus(type, message) });
1764
1669
  },
1765
1670
  commandTenant,
1766
- createEntityUpdate,
1767
- createEventUpdate,
1768
1671
  createReadPlan,
1769
1672
  createSubscriptionRecord,
1770
1673
  createSubscriptionRemoval,
@@ -1772,7 +1675,6 @@ const ServiceValues = Object.freeze({
1772
1675
  defaultQueueLimit: DEFAULT_QUEUE_LIMIT,
1773
1676
  defaultSubscriptionLimit: DEFAULT_SUBSCRIPTION_LIMIT,
1774
1677
  errorStatus,
1775
- eventTenantMatches,
1776
1678
  okResponse,
1777
1679
  okStatus,
1778
1680
  positiveInteger,
@@ -1782,7 +1684,6 @@ const ServiceValues = Object.freeze({
1782
1684
  stateRouteIdField,
1783
1685
  subscriptionLimit,
1784
1686
  tenantMismatch,
1785
- tenantOptions,
1786
1687
  tenantValue,
1787
1688
  topicTenant,
1788
1689
  uniqueContexts,