@spine-event-engine/server 2.0.0-snapshot.23 → 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 (300) hide show
  1. package/README.md +38 -11
  2. package/REFERENCE.md +278 -141
  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/agent/agent-live-inputs.js +15 -0
  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 +82 -104
  68. package/dist/bus/event-bus.d.ts.map +1 -1
  69. package/dist/bus/event-bus.js +87 -127
  70. package/dist/bus/event-bus.js.map +1 -1
  71. package/dist/context/bounded-context.d.ts +143 -91
  72. package/dist/context/bounded-context.d.ts.map +1 -1
  73. package/dist/context/bounded-context.js +753 -359
  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 +43 -20
  78. package/dist/context/entity-inbox.js.map +1 -1
  79. package/dist/context/local-inbox-handoff.d.ts +35 -34
  80. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  81. package/dist/context/local-inbox-handoff.js +123 -135
  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 +12 -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 +12 -9
  104. package/dist/delivery/delivery-monitor.d.ts.map +1 -1
  105. package/dist/delivery/delivery-monitor.js +15 -9
  106. package/dist/delivery/delivery-monitor.js.map +1 -1
  107. package/dist/delivery/delivery-ports.d.ts +6 -13
  108. package/dist/delivery/delivery-ports.d.ts.map +1 -1
  109. package/dist/delivery/delivery-run-control.d.ts +21 -0
  110. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  111. package/dist/delivery/delivery-run-control.js +10 -0
  112. package/dist/delivery/delivery-run-control.js.map +1 -1
  113. package/dist/delivery/delivery-supervisor.d.ts +33 -5
  114. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  115. package/dist/delivery/delivery-supervisor.js +178 -26
  116. package/dist/delivery/delivery-supervisor.js.map +1 -1
  117. package/dist/delivery/delivery-tenant.d.ts +17 -0
  118. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  119. package/dist/delivery/delivery-tenant.js +50 -0
  120. package/dist/delivery/delivery-tenant.js.map +1 -0
  121. package/dist/delivery/delivery.d.ts +96 -2
  122. package/dist/delivery/delivery.d.ts.map +1 -1
  123. package/dist/delivery/delivery.js +803 -89
  124. package/dist/delivery/delivery.js.map +1 -1
  125. package/dist/delivery/inbox-records.d.ts.map +1 -1
  126. package/dist/delivery/inbox-records.js +9 -16
  127. package/dist/delivery/inbox-records.js.map +1 -1
  128. package/dist/delivery/inbox-storage.d.ts +14 -11
  129. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  130. package/dist/delivery/inbox-storage.js +72 -78
  131. package/dist/delivery/inbox-storage.js.map +1 -1
  132. package/dist/delivery/inbox.d.ts +9 -12
  133. package/dist/delivery/inbox.d.ts.map +1 -1
  134. package/dist/delivery/inbox.js +13 -15
  135. package/dist/delivery/inbox.js.map +1 -1
  136. package/dist/delivery/reception-observation.d.ts +64 -0
  137. package/dist/delivery/reception-observation.d.ts.map +1 -0
  138. package/dist/delivery/reception-observation.js +78 -0
  139. package/dist/delivery/reception-observation.js.map +1 -0
  140. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  141. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  142. package/dist/delivery/sharded-work-registry.js +41 -4
  143. package/dist/delivery/sharded-work-registry.js.map +1 -1
  144. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  145. package/dist/entity/entity-storage-descriptor.js +7 -4
  146. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  147. package/dist/index.d.ts +4 -2
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -1
  150. package/dist/index.js.map +1 -1
  151. package/dist/integration/external-messages.d.ts.map +1 -1
  152. package/dist/integration/external-messages.js +9 -15
  153. package/dist/integration/external-messages.js.map +1 -1
  154. package/dist/integration/integration-broker.d.ts.map +1 -1
  155. package/dist/integration/integration-broker.js +112 -6
  156. package/dist/integration/integration-broker.js.map +1 -1
  157. package/dist/integration/third-party-context.d.ts.map +1 -1
  158. package/dist/integration/third-party-context.js +7 -10
  159. package/dist/integration/third-party-context.js.map +1 -1
  160. package/dist/repository/primitive-id.js +26 -11
  161. package/dist/repository/primitive-id.js.map +1 -1
  162. package/dist/repository/repository.d.ts +33 -62
  163. package/dist/repository/repository.d.ts.map +1 -1
  164. package/dist/repository/repository.js +1475 -1827
  165. package/dist/repository/repository.js.map +1 -1
  166. package/dist/runtime/runtime.d.ts +24 -0
  167. package/dist/runtime/runtime.d.ts.map +1 -1
  168. package/dist/runtime/runtime.js +31 -0
  169. package/dist/runtime/runtime.js.map +1 -1
  170. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  171. package/dist/runtime/signal-metadata.js +8 -15
  172. package/dist/runtime/signal-metadata.js.map +1 -1
  173. package/dist/runtime/signal-publisher.d.ts +8 -36
  174. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  175. package/dist/runtime/signal-publisher.js +68 -61
  176. package/dist/runtime/signal-publisher.js.map +1 -1
  177. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  178. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  179. package/dist/runtime/standalone-handler-runtime.js +14 -130
  180. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  181. package/dist/server/managed-delivery-gate.d.ts +34 -0
  182. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  183. package/dist/server/managed-delivery-gate.js +70 -0
  184. package/dist/server/managed-delivery-gate.js.map +1 -0
  185. package/dist/server/managed-server-application.d.ts +2 -2
  186. package/dist/server/managed-server-application.d.ts.map +1 -1
  187. package/dist/server/managed-server-application.js +471 -178
  188. package/dist/server/managed-server-application.js.map +1 -1
  189. package/dist/server/node-coordinator.d.ts +14 -0
  190. package/dist/server/node-coordinator.d.ts.map +1 -1
  191. package/dist/server/node-coordinator.js +266 -45
  192. package/dist/server/node-coordinator.js.map +1 -1
  193. package/dist/server/server-environment.d.ts +203 -25
  194. package/dist/server/server-environment.d.ts.map +1 -1
  195. package/dist/server/server-environment.js +661 -133
  196. package/dist/server/server-environment.js.map +1 -1
  197. package/dist/server/server-log.d.ts +7 -0
  198. package/dist/server/server-log.d.ts.map +1 -1
  199. package/dist/server/server-log.js +18 -1
  200. package/dist/server/server-log.js.map +1 -1
  201. package/dist/server/server.d.ts +18 -32
  202. package/dist/server/server.d.ts.map +1 -1
  203. package/dist/server/server.js +62 -203
  204. package/dist/server/server.js.map +1 -1
  205. package/dist/services/spine-services.d.ts +1 -1
  206. package/dist/services/spine-services.d.ts.map +1 -1
  207. package/dist/services/spine-services.js +399 -370
  208. package/dist/services/spine-services.js.map +1 -1
  209. package/dist/stand/event-origin-filter.d.ts +16 -0
  210. package/dist/stand/event-origin-filter.d.ts.map +1 -0
  211. package/dist/stand/event-origin-filter.js +124 -0
  212. package/dist/stand/event-origin-filter.js.map +1 -0
  213. package/dist/stand/stand.d.ts +14 -0
  214. package/dist/stand/stand.d.ts.map +1 -1
  215. package/dist/stand/stand.js +53 -9
  216. package/dist/stand/stand.js.map +1 -1
  217. package/dist/stand/subscription-observer.d.ts.map +1 -1
  218. package/dist/stand/subscription-observer.js +61 -40
  219. package/dist/stand/subscription-observer.js.map +1 -1
  220. package/dist/stand/subscription-registry.d.ts +6 -2
  221. package/dist/stand/subscription-registry.d.ts.map +1 -1
  222. package/dist/stand/subscription-registry.js +15 -11
  223. package/dist/stand/subscription-registry.js.map +1 -1
  224. package/dist/stand/subscription-runtime.d.ts +23 -4
  225. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  226. package/dist/stand/subscription-runtime.js +84 -13
  227. package/dist/stand/subscription-runtime.js.map +1 -1
  228. package/dist/testing/index.d.ts.map +1 -1
  229. package/dist/testing/index.js +3 -2
  230. package/dist/testing/index.js.map +1 -1
  231. package/dist/tsconfig.tsbuildinfo +1 -1
  232. package/package.json +15 -15
  233. package/dist/agent/agent-admission.d.ts +0 -80
  234. package/dist/agent/agent-admission.d.ts.map +0 -1
  235. package/dist/agent/agent-admission.js +0 -127
  236. package/dist/agent/agent-admission.js.map +0 -1
  237. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  238. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  239. package/dist/agent/agent-ai-runtime.js +0 -1269
  240. package/dist/agent/agent-ai-runtime.js.map +0 -1
  241. package/dist/agent/agent-execution-fault.d.ts +0 -18
  242. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  243. package/dist/agent/agent-execution-fault.js.map +0 -1
  244. package/dist/agent/agent-execution-session.d.ts +0 -103
  245. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  246. package/dist/agent/agent-execution-session.js +0 -238
  247. package/dist/agent/agent-execution-session.js.map +0 -1
  248. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  249. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  250. package/dist/agent/agent-interaction-audit.js +0 -114
  251. package/dist/agent/agent-interaction-audit.js.map +0 -1
  252. package/dist/agent/agent-interaction-events.d.ts +0 -40
  253. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  254. package/dist/agent/agent-interaction-events.js +0 -129
  255. package/dist/agent/agent-interaction-events.js.map +0 -1
  256. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  257. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  258. package/dist/agent/agent-mcp-budget.js +0 -566
  259. package/dist/agent/agent-mcp-budget.js.map +0 -1
  260. package/dist/agent/agent-mcp-host.d.ts +0 -125
  261. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  262. package/dist/agent/agent-mcp-host.js.map +0 -1
  263. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  264. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  265. package/dist/agent/agent-read-runtime.d.ts +0 -70
  266. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  267. package/dist/agent/agent-read-runtime.js +0 -320
  268. package/dist/agent/agent-read-runtime.js.map +0 -1
  269. package/dist/agent/agent-revisions.d.ts +0 -35
  270. package/dist/agent/agent-revisions.d.ts.map +0 -1
  271. package/dist/agent/agent-revisions.js +0 -133
  272. package/dist/agent/agent-revisions.js.map +0 -1
  273. package/dist/agent/agent-scheduler.d.ts +0 -99
  274. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  275. package/dist/agent/agent-scheduler.js +0 -398
  276. package/dist/agent/agent-scheduler.js.map +0 -1
  277. package/dist/bus/saved-dispatcher-binding.d.ts +0 -96
  278. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  279. package/dist/bus/saved-dispatcher-binding.js +0 -35
  280. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  281. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  282. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  283. package/dist/delivery/delivery-run-coordinator.js +0 -609
  284. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  285. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  286. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  287. package/dist/delivery/parked-delivery-obligations.js +0 -390
  288. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  289. package/dist/server/environment-attachment.d.ts +0 -356
  290. package/dist/server/environment-attachment.d.ts.map +0 -1
  291. package/dist/server/environment-attachment.js +0 -2062
  292. package/dist/server/environment-attachment.js.map +0 -1
  293. package/dist/server/environment-delivery-records.d.ts +0 -77
  294. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  295. package/dist/server/environment-delivery-records.js +0 -282
  296. package/dist/server/environment-delivery-records.js.map +0 -1
  297. package/dist/server/environment-delivery-worker.d.ts +0 -161
  298. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  299. package/dist/server/environment-delivery-worker.js +0 -673
  300. package/dist/server/environment-delivery-worker.js.map +0 -1
@@ -13,31 +13,33 @@
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
+ import { EventOriginFilters } from "../stand/event-origin-filter.js";
28
+ import { TenantAdmissions, TenantUnknownError } from "../context/tenant-unknown-error.js";
27
29
  import { AckSchema } from "@spine-event-engine/proto";
28
30
  import { ResponseSchema, StatusSchema, } from "@spine-event-engine/proto";
29
31
  import {} from "@spine-event-engine/storage";
30
32
  import { TenantBoundary } from "@spine-event-engine/storage/provider";
31
- import { boundedContextAccess } from "../context/bounded-context.js";
33
+ import { BoundedContext, boundedContextAccess } from "../context/bounded-context.js";
32
34
  import { EffectiveTenants } from "../context/effective-tenant.js";
33
35
  import { CommandValidationError } from "../bus/command-errors.js";
34
36
  import { TransitionValidationError } from "../repository/command-errors.js";
35
37
  import {} from "../stand/stand.js";
36
38
  import { QueryReader } from "./query-reader.js";
37
39
  import { RegisteredTargets } from "./registered-targets.js";
38
- import { emitServerWarning } from "../server/server-log.js";
40
+ import { emitServerError, emitServerWarning } from "../server/server-log.js";
39
41
  import { managedChildSubscriptionAccess } from "../server/managed-child-subscription.js";
40
- import { InMemorySubscriptionRegistry, } from "../stand/subscription-registry.js";
42
+ import {} from "../stand/subscription-registry.js";
41
43
  const serviceLoggers = new WeakMap();
42
44
  const serviceInstances = new WeakSet();
43
45
  /**
@@ -85,7 +87,6 @@ export class SpineServices {
85
87
  #activationTails = new Map();
86
88
  #removals = new Map();
87
89
  #unknownRemovals = new Set();
88
- #testRegistries = new WeakMap();
89
90
  #queueLimit;
90
91
  #subscriptionLimit;
91
92
  /**
@@ -170,28 +171,14 @@ export class SpineServices {
170
171
  * @returns Acknowledgement for route validation or dispatch.
171
172
  */
172
173
  async #post(command) {
173
- const messageId = command.id && AnyMessages.pack(CommandIdSchema, command.id, { validate: false });
174
+ const messageId = command.id && AnyPacker.pack(CommandIdSchema, command.id);
174
175
  const typeUrl = command.message?.typeUrl;
175
- if (typeUrl === undefined || typeUrl.length === 0) {
176
- return create(AckSchema, {
177
- messageId,
178
- status: ServiceValues.errorStatus("INVALID_COMMAND", "Command message type is required."),
179
- });
180
- }
181
- const route = this.#commandRoutes.get(typeUrl);
182
- if (route === undefined) {
183
- return create(AckSchema, {
184
- messageId,
185
- status: ServiceValues.errorStatus("UNSUPPORTED_COMMAND", "No bounded context accepted the command."),
186
- });
187
- }
188
- const tenantError = ServiceValues.tenantMismatch(route.context.isMultitenant, ServiceValues.commandTenant(command), "command");
189
- if (tenantError !== undefined) {
190
- return create(AckSchema, {
191
- messageId,
192
- status: ServiceValues.errorStatus(tenantError.type, tenantError.message),
193
- });
194
- }
176
+ if (typeUrl === undefined || typeUrl.length === 0)
177
+ return ServiceValues.commandErrorAck(messageId, "INVALID_COMMAND", "Command message type is required.");
178
+ const admission = this.#commandAdmission(command, messageId, typeUrl);
179
+ if ("ack" in admission)
180
+ return admission.ack;
181
+ const { route } = admission;
195
182
  try {
196
183
  await route.context.commandBus().post(command);
197
184
  return create(AckSchema, {
@@ -200,13 +187,61 @@ export class SpineServices {
200
187
  });
201
188
  }
202
189
  catch (error) {
190
+ if (error instanceof TenantUnknownError)
191
+ throw TenantStatuses.unknown(error);
203
192
  const postError = ServiceValues.commandPostError(error);
193
+ this.#logCommandPostFailure(route, command, error);
204
194
  return create(AckSchema, {
205
195
  messageId,
206
196
  status: ServiceValues.errorStatus(postError.type, postError.message, postError.details),
207
197
  });
208
198
  }
209
199
  }
200
+ /**
201
+ * Checks route and tenant admission before command dispatch.
202
+ *
203
+ * @param command The public Command request.
204
+ * @param messageId The packed Command ID returned in the Ack.
205
+ * @param typeUrl The validated Command message type.
206
+ * @returns A selected route or a stable rejection Ack.
207
+ */
208
+ #commandAdmission(command, messageId, typeUrl) {
209
+ const route = this.#commandRoutes.get(typeUrl);
210
+ if (route === undefined) {
211
+ return {
212
+ ack: ServiceValues.commandErrorAck(messageId, "UNSUPPORTED_COMMAND", "No bounded context accepted the command."),
213
+ };
214
+ }
215
+ const tenantError = ServiceValues.tenantMismatch(route.context.isMultitenant, ServiceValues.commandTenant(command), "command");
216
+ if (tenantError !== undefined) {
217
+ return {
218
+ ack: ServiceValues.commandErrorAck(messageId, tenantError.type, tenantError.message),
219
+ };
220
+ }
221
+ return { route };
222
+ }
223
+ /**
224
+ * Records one failed command post with safe correlation fields.
225
+ *
226
+ * @param route The selected bounded context and command type.
227
+ * @param command The failed Command envelope.
228
+ * @param error The internal failure, retained for diagnostic classification only.
229
+ */
230
+ #logCommandPostFailure(route, command, error) {
231
+ if (error instanceof TransitionValidationError || error instanceof CommandValidationError)
232
+ return;
233
+ const logger = serviceLoggers.get(this);
234
+ if (logger === undefined)
235
+ return;
236
+ emitServerError(logger, "Command post failed.", {
237
+ contextName: route.context.name.value,
238
+ commandId: command.id?.uuid,
239
+ commandType: route.typeUrl,
240
+ operation: "command.post",
241
+ errorName: InternalErrorFacts.name(error),
242
+ errorCode: InternalErrorFacts.code(error),
243
+ });
244
+ }
210
245
  /**
211
246
  * Validates a public state query before reading its registered Stand.
212
247
  *
@@ -244,7 +279,9 @@ export class SpineServices {
244
279
  const destination = EffectiveTenants.destination(effective, route.context.isMultitenant);
245
280
  return await this.#query(route, ServiceValues.createReadPlan(target, query, route), destination);
246
281
  }
247
- catch {
282
+ catch (error) {
283
+ if (error instanceof TenantUnknownError)
284
+ throw TenantStatuses.unknown(error);
248
285
  return ServiceValues.queryErrorResponse("QUERY_READ_ERROR", "Query read failed.");
249
286
  }
250
287
  }
@@ -258,10 +295,16 @@ export class SpineServices {
258
295
  message: results.map((result) => ServiceValues.packVersionedState(route.schema, result)),
259
296
  });
260
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
+ */
261
304
  #subscribe(topic) {
262
305
  const route = this.#subscriptionRoute(topic);
263
306
  ServiceValues.validateTopic(topic);
264
- const shape = ServiceValues.createSubscriptionShape(topic, route);
307
+ ServiceValues.createSubscriptionShape(topic, route);
265
308
  const tenantId = ServiceValues.topicTenant(topic);
266
309
  const tenantError = ServiceValues.tenantMismatch(route.context.isMultitenant, tenantId, "subscription");
267
310
  if (tenantError !== undefined) {
@@ -275,53 +318,24 @@ export class SpineServices {
275
318
  if (id === undefined) {
276
319
  throw new ConnectError("Subscription ID is required.", Code.InvalidArgument);
277
320
  }
278
- void shape;
279
- const registry = this.#subscriptionRegistry(route.context);
280
- if (registry === undefined) {
281
- throw new ConnectError("Subscription registry is unavailable.", Code.FailedPrecondition);
282
- }
283
- 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));
284
325
  }
285
326
  async *#activate(subscription) {
286
327
  const id = subscription.id?.value;
287
328
  if (id === undefined) {
288
329
  return;
289
330
  }
290
- let record;
291
- await this.#serializeActivation(id, async () => {
292
- const local = this.#subscriptions.get(id);
293
- record = local ?? (await this.#findSubscription(id));
294
- if (record === undefined)
295
- return;
296
- if (record.delivery.active) {
297
- record = undefined;
298
- return;
299
- }
300
- const activation = await this.#subscriptionRegistry(record.route.context)?.activate(create(SubscriptionIdSchema, { value: id }));
301
- if (activation === undefined ||
302
- activation.kind === "missing" ||
303
- activation.kind === "expired" ||
304
- record.delivery.closed) {
305
- record = undefined;
306
- return;
307
- }
308
- try {
309
- this.#subscriptions.set(id, record);
310
- await this.#activateRecord(record);
311
- }
312
- catch (error) {
313
- try {
314
- await this.#removeSubscription(id);
315
- }
316
- catch (cleanupError) {
317
- throw new AggregateError([error, cleanupError], "Subscription activation and cleanup failed.");
318
- }
319
- throw error;
320
- }
321
- });
331
+ const record = await this.#activationRecord(id);
322
332
  if (record === undefined)
323
333
  return;
324
334
  try {
335
+ yield create(SubscriptionUpdateSchema, {
336
+ subscription: clone(SubscriptionSchema, record.subscription),
337
+ response: ServiceValues.okResponse(),
338
+ });
325
339
  for (;;) {
326
340
  const update = await record.delivery.next();
327
341
  if (update === undefined) {
@@ -336,6 +350,65 @@ export class SpineServices {
336
350
  }
337
351
  }
338
352
  }
353
+ /**
354
+ * Serializes one native activation and returns its attached record.
355
+ *
356
+ * @param id The logical subscription identifier.
357
+ * @returns The attached record, or undefined when admission fails.
358
+ */
359
+ async #activationRecord(id) {
360
+ let attached;
361
+ await this.#serializeActivation(id, async () => {
362
+ attached = await this.#startActivationRecord(id);
363
+ });
364
+ return attached;
365
+ }
366
+ /**
367
+ * Validates and attaches one retained subscription before acknowledging readiness.
368
+ *
369
+ * @param id The logical subscription identifier.
370
+ * @returns The attached record, or undefined when unavailable.
371
+ */
372
+ async #startActivationRecord(id) {
373
+ const record = this.#subscriptions.get(id) ?? (await this.#findSubscription(id));
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)
391
+ return undefined;
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)
396
+ return undefined;
397
+ try {
398
+ this.#subscriptions.set(id, record);
399
+ await this.#activateRecord(record);
400
+ return record;
401
+ }
402
+ catch (error) {
403
+ try {
404
+ await this.#removeSubscription(id);
405
+ }
406
+ catch (cleanupError) {
407
+ throw new AggregateError([error, cleanupError], "Subscription activation and cleanup failed.");
408
+ }
409
+ throw error;
410
+ }
411
+ }
339
412
  async #serializeActivation(id, work) {
340
413
  const previous = this.#activationTails.get(id) ?? Promise.resolve();
341
414
  let finish = () => undefined;
@@ -354,12 +427,41 @@ export class SpineServices {
354
427
  this.#activationTails.delete(id);
355
428
  }
356
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
+ */
357
436
  #cancel(subscription) {
358
437
  const id = subscription.id?.value;
359
438
  if (id === undefined) {
360
439
  return ServiceValues.okResponse();
361
440
  }
362
- 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);
363
465
  }
364
466
  #subscriptionRoute(topic) {
365
467
  const target = topic.target;
@@ -372,60 +474,14 @@ export class SpineServices {
372
474
  return route;
373
475
  }
374
476
  async #activateRecord(record) {
375
- const registry = this.#subscriptionRegistry(record.route.context);
376
- if (registry === undefined) {
377
- this.#createSubscriptionAttachment(record);
378
- managedChildSubscriptionAccess.installed(record.id);
379
- return;
380
- }
381
- try {
382
- const consumer = await boundedContextAccess.consumeSubscription(record.route.context, record.id, (update) => {
383
- record.delivery.push(update);
384
- if (record.delivery.closed) {
385
- this.#removeDetachedSubscription(record.id);
386
- }
387
- });
388
- record.delivery.attach(consumer);
389
- }
390
- catch (error) {
391
- if (!(error instanceof TypeError) ||
392
- (!error.message.includes("requires a Stand instance") &&
393
- !error.message.includes("requires a built BoundedContext instance"))) {
394
- throw error;
395
- }
396
- this.#createSubscriptionAttachment(record);
397
- }
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);
481
+ });
482
+ record.delivery.attach(consumer);
398
483
  managedChildSubscriptionAccess.installed(record.id);
399
484
  }
400
- #createSubscriptionAttachment(record) {
401
- if (record.kind === "event") {
402
- const eventSubscription = boundedContextAccess.subscribeToEvent(record.route.context, record.route.typeUrl, {
403
- onEvent: (event) => {
404
- if (!ServiceValues.eventTenantMatches(record, event)) {
405
- return;
406
- }
407
- record.delivery.push(ServiceValues.createEventUpdate(record, event));
408
- if (record.delivery.closed) {
409
- this.#removeDetachedSubscription(record.id);
410
- }
411
- },
412
- });
413
- record.delivery.attach(eventSubscription);
414
- return eventSubscription;
415
- }
416
- const stateRecord = record;
417
- const standSubscription = stateRecord.route.context.stand().subscribe(stateRecord.route.schema, (update) => {
418
- const subscriptionUpdate = ServiceValues.createEntityUpdate(stateRecord, update);
419
- if (subscriptionUpdate !== undefined) {
420
- stateRecord.delivery.push(subscriptionUpdate);
421
- }
422
- if (stateRecord.delivery.closed) {
423
- this.#removeDetachedSubscription(stateRecord.id);
424
- }
425
- }, ServiceValues.tenantOptions(stateRecord.tenantId));
426
- stateRecord.delivery.attach(standSubscription);
427
- return standSubscription;
428
- }
429
485
  #removeSubscription(id) {
430
486
  const existing = this.#removals.get(id);
431
487
  if (existing !== undefined) {
@@ -464,15 +520,12 @@ export class SpineServices {
464
520
  async #runRemoval(id, removal, record, unknown) {
465
521
  try {
466
522
  if (record !== undefined) {
467
- 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 }));
468
526
  }
469
527
  else {
470
- await Promise.all(ServiceValues.uniqueContexts(this.#contexts).flatMap((context) => {
471
- const registry = this.#subscriptionRegistry(context);
472
- return registry === undefined
473
- ? []
474
- : [registry.delete(create(SubscriptionIdSchema, { value: id }))];
475
- }));
528
+ await this.#deleteUnknownSubscription(id);
476
529
  }
477
530
  removal.resolve();
478
531
  }
@@ -488,10 +541,25 @@ export class SpineServices {
488
541
  }
489
542
  }
490
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
+ }
491
556
  async #findSubscription(id) {
492
557
  const subscriptionId = create(SubscriptionIdSchema, { value: id });
493
558
  for (const context of ServiceValues.uniqueContexts(this.#contexts)) {
494
- 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
+ });
495
563
  const subscription = entry?.subscription;
496
564
  const topic = subscription?.topic;
497
565
  if (entry === undefined || subscription === undefined || topic === undefined)
@@ -514,24 +582,15 @@ export class SpineServices {
514
582
  }
515
583
  return undefined;
516
584
  }
517
- #subscriptionRegistry(context) {
518
- if (context === undefined) {
519
- return undefined;
520
- }
521
- try {
522
- return boundedContextAccess.subscriptionRegistry(context);
523
- }
524
- catch {
525
- // Direct route tests may use structural context doubles. Production
526
- // contexts always provide their configured registry through the access seam.
527
- const key = context;
528
- let registry = this.#testRegistries.get(key);
529
- if (registry === undefined) {
530
- registry = new InMemorySubscriptionRegistry();
531
- this.#testRegistries.set(key, registry);
532
- }
533
- return registry;
534
- }
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);
535
594
  }
536
595
  }
537
596
  /**
@@ -765,7 +824,8 @@ function validateQueryIds(ids, route) {
765
824
  if (schema === undefined || ids === undefined) {
766
825
  return undefined;
767
826
  }
768
- 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))) {
769
829
  return invalidQueryError(`QueryService.Read id_filter values must pack ${schema.typeName}.`);
770
830
  }
771
831
  return undefined;
@@ -876,7 +936,7 @@ function normalizedFilters(filters, route) {
876
936
  }
877
937
  function decodeQueryIdValue(value, route) {
878
938
  const schema = route.idField.message;
879
- return schema === undefined ? decodeAnyValue(value) : AnyMessages.unpack(value, schema);
939
+ return schema === undefined ? decodeAnyValue(value) : decodeAnyValue(value, schema);
880
940
  }
881
941
  function normalizedComposite(filter, route) {
882
942
  const predicates = [
@@ -908,13 +968,13 @@ function requiredComparison(filter) {
908
968
  function decodeColumnValue(filter, route) {
909
969
  const column = filter.fieldPath?.fieldName[0] ?? "";
910
970
  if (column === "version" && filter.value !== undefined) {
911
- const value = AnyMessages.unpack(filter.value, VersionSchema);
971
+ const value = AnyPacker.tryUnpack(filter.value, VersionSchema);
912
972
  if (value !== undefined)
913
973
  return value;
914
974
  throw new TypeError('Validated query column "version" has an invalid value.');
915
975
  }
916
976
  if ((column === "archived" || column === "deleted") && filter.value !== undefined) {
917
- const value = AnyMessages.unpack(filter.value, BoolValueSchema);
977
+ const value = AnyPacker.tryUnpack(filter.value, BoolValueSchema);
918
978
  if (value !== undefined)
919
979
  return value.value;
920
980
  throw new TypeError(`Validated query column "${column}" has an invalid value.`);
@@ -924,7 +984,8 @@ function decodeColumnValue(filter, route) {
924
984
  }
925
985
  function filterValueMatches(field, value) {
926
986
  const schema = filterValueSchema(field);
927
- if (schema === undefined || value.typeUrl !== TypeUrls.derive(schema))
987
+ if (schema === undefined ||
988
+ (schema.typeName !== AnySchema.typeName && value.typeUrl !== TypeUrls.derive(schema)))
928
989
  return false;
929
990
  const decoded = field.fieldKind === "message" ? decodeAnyValue(value, schema) : decodeAnyValue(value);
930
991
  if (field.fieldKind === "message") {
@@ -982,11 +1043,11 @@ function filterValueSchema(field) {
982
1043
  function systemValueMatches(column, value) {
983
1044
  if (column === "version") {
984
1045
  return (value.typeUrl === TypeUrls.derive(VersionSchema) &&
985
- AnyMessages.unpack(value, VersionSchema) !== undefined);
1046
+ AnyPacker.tryUnpack(value, VersionSchema) !== undefined);
986
1047
  }
987
1048
  if (column === "archived" || column === "deleted") {
988
1049
  return (value.typeUrl === TypeUrls.derive(BoolValueSchema) &&
989
- AnyMessages.unpack(value, BoolValueSchema) !== undefined);
1050
+ AnyPacker.tryUnpack(value, BoolValueSchema) !== undefined);
990
1051
  }
991
1052
  return false;
992
1053
  }
@@ -1097,7 +1158,7 @@ function validateTopic(topic) {
1097
1158
  }
1098
1159
  }
1099
1160
  /**
1100
- * Validates a topic and prepares complete state or event delivery.
1161
+ * Validates a topic and returns its state or Event subscription shape.
1101
1162
  */
1102
1163
  function createSubscriptionShape(topic, route) {
1103
1164
  const target = topic.target;
@@ -1113,13 +1174,10 @@ function createSubscriptionShape(topic, route) {
1113
1174
  if (!target.criterion.value) {
1114
1175
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1115
1176
  }
1116
- return { kind: "state", route, matcher: { match: () => "state" } };
1177
+ return { kind: "state", route };
1117
1178
  case "filters":
1118
- return {
1119
- kind: "state",
1120
- route,
1121
- matcher: createFilteredSubscriptionMatcher(target.criterion.value, route),
1122
- };
1179
+ ServiceValues.validateFilteredSubscriptionTopic(target.criterion.value, route);
1180
+ return { kind: "state", route };
1123
1181
  default:
1124
1182
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1125
1183
  }
@@ -1135,42 +1193,12 @@ function validateEventSubscriptionTopic(target) {
1135
1193
  }
1136
1194
  return;
1137
1195
  case "filters":
1138
- throw new ConnectError("SubscriptionService.Subscribe event topics support only include_all in this runtime slice.", Code.InvalidArgument);
1196
+ EventOriginFilters.compile(target.criterion.value);
1197
+ return;
1139
1198
  default:
1140
1199
  throw new ConnectError("SubscriptionService.Subscribe requires filters or include_all = true.", Code.InvalidArgument);
1141
1200
  }
1142
1201
  }
1143
- /**
1144
- * Matches complete state updates against a topic's ID and field filters.
1145
- */
1146
- function createFilteredSubscriptionMatcher(filters, route) {
1147
- ServiceValues.validateSubscriptionFilters(filters);
1148
- const idValues = filters.idFilter?.id.map((id) => decodeSubscriptionIdValue(id, route));
1149
- const statePredicate = createSubscriptionPredicate(filters.filter, route);
1150
- return {
1151
- /**
1152
- * Matches one state update against the topic's ID and field filters.
1153
- *
1154
- * @param update State update to inspect.
1155
- * @returns Delivery kind, if the update changes a matching state.
1156
- */
1157
- match(update) {
1158
- if (idValues !== undefined &&
1159
- !idValues.some((id) => valuesEqual(update.id, id, route.idField.message))) {
1160
- return undefined;
1161
- }
1162
- if (statePredicate(update.state))
1163
- return "state";
1164
- if (update.previousState !== undefined && statePredicate(update.previousState))
1165
- return "noLongerMatching";
1166
- return undefined;
1167
- },
1168
- };
1169
- }
1170
- function createSubscriptionPredicate(filters, route) {
1171
- const predicates = filters.map((filter) => createCompositePredicate(filter, route));
1172
- return (state) => predicates.every((predicate) => predicate(state));
1173
- }
1174
1202
  function validateSubscriptionFilterTree(filters) {
1175
1203
  if (filters.length > SubscriptionLimits.compositeCount) {
1176
1204
  throw new ConnectError("SubscriptionService.Subscribe may contain at most 8 composite filters.", Code.InvalidArgument);
@@ -1227,30 +1255,6 @@ function enqueueSubscriptionFilterChildren(filter, depth, stack) {
1227
1255
  stack.push({ filter: nested, depth: depth + 1 });
1228
1256
  }
1229
1257
  }
1230
- function createCompositePredicate(filter, route) {
1231
- const children = [
1232
- ...filter.filter.map((simple) => createSimplePredicate(simple, route)),
1233
- ...filter.compositeFilter.map((nested) => createCompositePredicate(nested, route)),
1234
- ];
1235
- if (children.length === 0) {
1236
- return () => true;
1237
- }
1238
- return filter.operator === CompositeFilter_CompositeOperator.ALL
1239
- ? (state) => children.every((predicate) => predicate(state))
1240
- : (state) => children.some((predicate) => predicate(state));
1241
- }
1242
- function createSimplePredicate(filter, route) {
1243
- if (filter.operator !== Filter_Operator.EQUAL) {
1244
- throw new ConnectError("SubscriptionService.Subscribe supports only EQUAL field filters.", Code.InvalidArgument);
1245
- }
1246
- if (filter.value === undefined) {
1247
- throw new ConnectError("SubscriptionService.Subscribe field filter value is required.", Code.InvalidArgument);
1248
- }
1249
- validateSubscriptionPath(filter.fieldPath?.fieldName ?? [], "field filter");
1250
- const resolved = resolveMessagePath(route.schema, filter.fieldPath?.fieldName ?? [], "field filter");
1251
- const expected = decodeFieldFilterValue(filter.value, resolved.leafSchema);
1252
- return (state) => valuesEqual(readPathValue(state, resolved.localPath), expected, resolved.leafSchema);
1253
- }
1254
1258
  /**
1255
1259
  * Checks a state filter path before resolving its fields.
1256
1260
  */
@@ -1272,7 +1276,9 @@ function decodeSubscriptionIdValue(value, route) {
1272
1276
  if (route.idField.message === undefined) {
1273
1277
  return decodeAnyValue(value);
1274
1278
  }
1275
- 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);
1276
1282
  if (decoded === undefined) {
1277
1283
  throw new ConnectError(`SubscriptionService.Subscribe id_filter values must pack ${route.idField.message.typeName}.`, Code.InvalidArgument);
1278
1284
  }
@@ -1282,7 +1288,7 @@ function decodeFieldFilterValue(value, schema) {
1282
1288
  if (value === undefined || schema === undefined) {
1283
1289
  return decodeAnyValue(value);
1284
1290
  }
1285
- const decoded = AnyMessages.unpack(value, schema);
1291
+ const decoded = schema.typeName === AnySchema.typeName ? value : AnyPacker.tryUnpack(value, schema);
1286
1292
  if (decoded === undefined) {
1287
1293
  throw new ConnectError(`SubscriptionService.Subscribe field filter value must pack ${schema.typeName}.`, Code.InvalidArgument);
1288
1294
  }
@@ -1293,7 +1299,9 @@ function decodeAnyValue(value, schema) {
1293
1299
  return undefined;
1294
1300
  }
1295
1301
  if (schema !== undefined) {
1296
- const decoded = AnyMessages.unpack(value, schema);
1302
+ if (schema.typeName === AnySchema.typeName)
1303
+ return value;
1304
+ const decoded = AnyPacker.tryUnpack(value, schema);
1297
1305
  if (decoded !== undefined) {
1298
1306
  return decoded;
1299
1307
  }
@@ -1310,7 +1318,6 @@ function decodeAnyValue(value, schema) {
1310
1318
  * Resolves a state filter path to generated message fields.
1311
1319
  */
1312
1320
  function resolveMessagePath(schema, fieldPath, label) {
1313
- const localPath = [];
1314
1321
  let currentSchema = schema;
1315
1322
  let leafSchema;
1316
1323
  for (const [index, name] of fieldPath.entries()) {
@@ -1318,7 +1325,6 @@ function resolveMessagePath(schema, fieldPath, label) {
1318
1325
  if (field === undefined) {
1319
1326
  throw new ConnectError(`SubscriptionService.Subscribe ${label} "${fieldPath.join(".")}" is not a state field.`, Code.InvalidArgument);
1320
1327
  }
1321
- localPath.push(field.localName);
1322
1328
  leafSchema = field.message;
1323
1329
  currentSchema = index === fieldPath.length - 1 ? undefined : field.message;
1324
1330
  if (currentSchema === undefined && index < fieldPath.length - 1) {
@@ -1326,7 +1332,6 @@ function resolveMessagePath(schema, fieldPath, label) {
1326
1332
  }
1327
1333
  }
1328
1334
  return Object.freeze({
1329
- localPath,
1330
1335
  ...(leafSchema === undefined ? {} : { leafSchema }),
1331
1336
  });
1332
1337
  }
@@ -1334,37 +1339,12 @@ function findMessageField(schema, name) {
1334
1339
  const fields = (schema?.fields ?? []);
1335
1340
  return fields.find((field) => field.name === name || field.localName === name);
1336
1341
  }
1337
- function readPathValue(value, path) {
1338
- let current = value;
1339
- for (const segment of path) {
1340
- if (typeof current !== "object" || current === null) {
1341
- return undefined;
1342
- }
1343
- current = Reflect.get(current, segment);
1344
- }
1345
- return current;
1346
- }
1347
- function valuesEqual(actual, expected, schema) {
1348
- if (schema !== undefined && isMessage(actual) && isMessage(expected)) {
1349
- return bytesEqual(toBinary(schema, actual), toBinary(schema, expected));
1350
- }
1351
- if (isAny(actual) && isAny(expected)) {
1352
- return bytesEqual(toBinary(AnySchema, actual), toBinary(AnySchema, expected));
1353
- }
1354
- if (actual instanceof Uint8Array && expected instanceof Uint8Array) {
1355
- return bytesEqual(actual, expected);
1356
- }
1357
- return Object.is(actual, expected);
1358
- }
1359
1342
  function isMessage(value) {
1360
1343
  return typeof value === "object" && value !== null && "$typeName" in value;
1361
1344
  }
1362
1345
  function isAny(value) {
1363
1346
  return isMessage(value) && value.$typeName === "google.protobuf.Any";
1364
1347
  }
1365
- function bytesEqual(left, right) {
1366
- return left.length === right.length && left.every((byte, index) => byte === right[index]);
1367
- }
1368
1348
  /**
1369
1349
  * Packs a state snapshot and version for a public query result.
1370
1350
  *
@@ -1375,7 +1355,7 @@ function bytesEqual(left, right) {
1375
1355
  */
1376
1356
  function packVersionedState(schema, result) {
1377
1357
  return create(EntityStateVersionSchema, {
1378
- state: AnyMessages.pack(schema, result.state, { validate: false }),
1358
+ state: AnyPacker.pack(schema, result.state),
1379
1359
  version: result.version ?? create(VersionSchema),
1380
1360
  });
1381
1361
  }
@@ -1385,22 +1365,6 @@ function commandTenant(command) {
1385
1365
  function topicTenant(topic) {
1386
1366
  return tenantValue(topic?.context?.tenantId);
1387
1367
  }
1388
- function eventTenant(event) {
1389
- switch (event.context?.origin.case) {
1390
- case "importContext":
1391
- return tenantValue(event.context.origin.value.tenantId);
1392
- case "pastMessage":
1393
- return tenantValue(event.context.origin.value.actorContext?.tenantId);
1394
- default:
1395
- return undefined;
1396
- }
1397
- }
1398
- function eventTenantMatches(record, event) {
1399
- const tenantId = eventTenant(event);
1400
- return (record.tenantId === undefined ||
1401
- (tenantId !== undefined &&
1402
- TenantBoundary.from(tenantId).key === TenantBoundary.from(record.tenantId).key));
1403
- }
1404
1368
  function tenantValue(tenantId) {
1405
1369
  if (tenantId === undefined)
1406
1370
  return undefined;
@@ -1421,6 +1385,53 @@ function stateRouteIdField(schema, idField) {
1421
1385
  }
1422
1386
  return field;
1423
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
+ });
1424
1435
  function tenantMismatch(multitenant, tenantId, subject) {
1425
1436
  if (multitenant) {
1426
1437
  return tenantId === undefined
@@ -1442,18 +1453,14 @@ function commandPostError(error) {
1442
1453
  return {
1443
1454
  type: error.type,
1444
1455
  message: error.clientMessage,
1445
- details: AnyMessages.pack(ValidationErrorSchema, error.validationError, {
1446
- validate: false,
1447
- }),
1456
+ details: AnyPacker.pack(ValidationErrorSchema, error.validationError),
1448
1457
  };
1449
1458
  }
1450
1459
  if (error instanceof CommandValidationError) {
1451
1460
  return {
1452
1461
  type: "COMMAND_VALIDATION_ERROR",
1453
1462
  message: "Command payload validation failed.",
1454
- details: AnyMessages.pack(ValidationErrorSchema, error.validationError, {
1455
- validate: false,
1456
- }),
1463
+ details: AnyPacker.pack(ValidationErrorSchema, error.validationError),
1457
1464
  };
1458
1465
  }
1459
1466
  return {
@@ -1461,104 +1468,77 @@ function commandPostError(error) {
1461
1468
  message: "Command post failed.",
1462
1469
  };
1463
1470
  }
1464
- function tenantOptions(tenantId) {
1465
- return tenantId === undefined ? {} : { tenantId: clone(TenantIdSchema, tenantId) };
1466
- }
1467
- const VALUE_DECODERS = Object.freeze([
1468
- (value) => AnyMessages.unpack(value, StringValueSchema)?.value,
1469
- (value) => AnyMessages.unpack(value, BoolValueSchema)?.value,
1470
- (value) => AnyMessages.unpack(value, Int32ValueSchema)?.value,
1471
- (value) => AnyMessages.unpack(value, UInt32ValueSchema)?.value,
1472
- (value) => AnyMessages.unpack(value, Int64ValueSchema)?.value,
1473
- (value) => AnyMessages.unpack(value, UInt64ValueSchema)?.value,
1474
- (value) => AnyMessages.unpack(value, FloatValueSchema)?.value,
1475
- (value) => AnyMessages.unpack(value, DoubleValueSchema)?.value,
1476
- (value) => AnyMessages.unpack(value, BytesValueSchema)?.value,
1471
+ const recognizedSqlErrorCodes = new Set([
1472
+ "40001",
1473
+ "40P01",
1474
+ "42703",
1475
+ "42P01",
1476
+ "23503",
1477
+ "23505",
1478
+ "P0001",
1479
+ "08006",
1480
+ "ER_BAD_FIELD_ERROR",
1481
+ "ER_LOCK_DEADLOCK",
1482
+ "ER_LOCK_WAIT_TIMEOUT",
1483
+ "ER_SIGNAL_EXCEPTION",
1477
1484
  ]);
1478
- /**
1479
- * Builds a subscription update with a complete state or a removal signal.
1480
- */
1481
- function createEntityUpdate(record, update) {
1482
- const matcher = record.matcher;
1483
- if (matcher === undefined)
1484
- return undefined;
1485
- const match = matcher.match(update);
1486
- if (match === undefined)
1485
+ const InternalErrorFacts = Object.freeze({
1486
+ /**
1487
+ * Reads a recognized SQL code from an internal failure chain.
1488
+ *
1489
+ * @param error The internal failure.
1490
+ * @returns A recognized code, if present.
1491
+ */
1492
+ code(error) {
1493
+ const seen = new Set();
1494
+ let current = error;
1495
+ for (let depth = 0; depth < 8 && typeof current === "object" && current !== null; depth++) {
1496
+ if (seen.has(current))
1497
+ break;
1498
+ seen.add(current);
1499
+ try {
1500
+ const code = Reflect.get(current, "code");
1501
+ if (typeof code === "string" && recognizedSqlErrorCodes.has(code))
1502
+ return code;
1503
+ current = Reflect.get(current, "cause");
1504
+ }
1505
+ catch {
1506
+ return undefined;
1507
+ }
1508
+ }
1487
1509
  return undefined;
1488
- const kind = match === "state"
1489
- ? {
1490
- case: "state",
1491
- value: AnyMessages.pack(record.route.schema, clone(record.route.schema, update.state), {
1492
- validate: false,
1493
- }),
1510
+ },
1511
+ /**
1512
+ * Reads a safe error class name without trusting a rejection's accessors.
1513
+ *
1514
+ * @param error The internal failure.
1515
+ * @returns A known error name, if present.
1516
+ */
1517
+ name(error) {
1518
+ try {
1519
+ if (!(error instanceof Error))
1520
+ return undefined;
1521
+ const name = error.name;
1522
+ return ["Error", "TypeError", "RangeError", "AggregateError"].includes(name)
1523
+ ? name
1524
+ : undefined;
1494
1525
  }
1495
- : { case: "noLongerMatching", value: true };
1496
- return create(SubscriptionUpdateSchema, {
1497
- subscription: clone(SubscriptionSchema, record.subscription),
1498
- response: okResponse(),
1499
- update: {
1500
- case: "entityUpdates",
1501
- value: create(EntityUpdatesSchema, {
1502
- update: [
1503
- create(EntityStateUpdateSchema, {
1504
- id: packEntityId(record.route, update.id),
1505
- kind,
1506
- }),
1507
- ],
1508
- }),
1509
- },
1510
- });
1511
- }
1512
- function createEventUpdate(record, event) {
1513
- return create(SubscriptionUpdateSchema, {
1514
- subscription: clone(SubscriptionSchema, record.subscription),
1515
- response: okResponse(),
1516
- update: {
1517
- case: "eventUpdates",
1518
- value: create(EventUpdatesSchema, {
1519
- event: [cloneClientEvent(event)],
1520
- }),
1521
- },
1522
- });
1523
- }
1524
- function cloneClientEvent(event) {
1525
- const clientEvent = clone(EventSchema, event);
1526
- const rejection = clientEvent.context?.rejection;
1527
- if (rejection !== undefined) {
1528
- rejection.command = undefined;
1529
- // Clear the legacy wire payload as part of client-side security redaction.
1530
- // eslint-disable-next-line @typescript-eslint/no-deprecated
1531
- rejection.commandMessage = undefined;
1532
- rejection.stacktrace = "";
1533
- }
1534
- return clientEvent;
1535
- }
1536
- function packString(value) {
1537
- return AnyMessages.pack(StringValueSchema, create(StringValueSchema, { value }));
1538
- }
1539
- function packEntityId(route, id) {
1540
- if (isAny(id)) {
1541
- return clone(AnySchema, id);
1542
- }
1543
- if (route.idField.message !== undefined && isMessage(id)) {
1544
- return AnyMessages.pack(route.idField.message, id, { validate: false });
1545
- }
1546
- if (id instanceof Uint8Array) {
1547
- return AnyMessages.pack(BytesValueSchema, create(BytesValueSchema, { value: id }));
1548
- }
1549
- switch (typeof id) {
1550
- case "string":
1551
- return packString(id);
1552
- case "boolean":
1553
- return AnyMessages.pack(BoolValueSchema, create(BoolValueSchema, { value: id }));
1554
- case "number":
1555
- return AnyMessages.pack(DoubleValueSchema, create(DoubleValueSchema, { value: id }));
1556
- case "bigint":
1557
- return AnyMessages.pack(Int64ValueSchema, create(Int64ValueSchema, { value: id }));
1558
- default:
1526
+ catch {
1559
1527
  return undefined;
1560
- }
1561
- }
1528
+ }
1529
+ },
1530
+ });
1531
+ const VALUE_DECODERS = Object.freeze([
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,
1541
+ ]);
1562
1542
  /**
1563
1543
  * Groups non-public service contract, query, and subscription operations.
1564
1544
  */
@@ -1616,6 +1596,48 @@ const ServiceValues = Object.freeze({
1616
1596
  }
1617
1597
  return undefined;
1618
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
+ },
1619
1641
  /**
1620
1642
  * Checks that a state topic has a usable ID or field filter.
1621
1643
  *
@@ -1634,9 +1656,18 @@ const ServiceValues = Object.freeze({
1634
1656
  validateSubscriptionFilterTree(filters.filter);
1635
1657
  },
1636
1658
  commandPostError,
1659
+ /**
1660
+ * Builds a stable Command rejection acknowledgement.
1661
+ *
1662
+ * @param messageId The packed Command ID, if supplied.
1663
+ * @param type The public error code.
1664
+ * @param message The public error message.
1665
+ * @returns A sanitized Ack.
1666
+ */
1667
+ commandErrorAck(messageId, type, message) {
1668
+ return create(AckSchema, { messageId, status: errorStatus(type, message) });
1669
+ },
1637
1670
  commandTenant,
1638
- createEntityUpdate,
1639
- createEventUpdate,
1640
1671
  createReadPlan,
1641
1672
  createSubscriptionRecord,
1642
1673
  createSubscriptionRemoval,
@@ -1644,7 +1675,6 @@ const ServiceValues = Object.freeze({
1644
1675
  defaultQueueLimit: DEFAULT_QUEUE_LIMIT,
1645
1676
  defaultSubscriptionLimit: DEFAULT_SUBSCRIPTION_LIMIT,
1646
1677
  errorStatus,
1647
- eventTenantMatches,
1648
1678
  okResponse,
1649
1679
  okStatus,
1650
1680
  positiveInteger,
@@ -1654,7 +1684,6 @@ const ServiceValues = Object.freeze({
1654
1684
  stateRouteIdField,
1655
1685
  subscriptionLimit,
1656
1686
  tenantMismatch,
1657
- tenantOptions,
1658
1687
  tenantValue,
1659
1688
  topicTenant,
1660
1689
  uniqueContexts,