@spine-event-engine/server 2.0.0-snapshot.26 → 2.0.0-snapshot.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -8
- package/REFERENCE.md +151 -39
- package/dist/agent/agent-live-ai-runtime.d.ts +1 -1
- package/dist/agent/agent-live-ai-runtime.js +1 -1
- package/dist/agent/agent-live-budget.d.ts +1 -1
- package/dist/agent/agent-live-budget.js +1 -1
- package/dist/agent/agent-live-read-runtime.d.ts +1 -1
- package/dist/agent/agent-live-read-runtime.js +1 -1
- package/dist/browser/browser-admission.d.ts +47 -0
- package/dist/browser/browser-admission.d.ts.map +1 -0
- package/dist/browser/browser-admission.js +200 -0
- package/dist/browser/browser-admission.js.map +1 -0
- package/dist/browser/browser-server.d.ts +14 -7
- package/dist/browser/browser-server.d.ts.map +1 -1
- package/dist/browser/browser-server.js +462 -129
- package/dist/browser/browser-server.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/options.d.ts +48 -2
- package/dist/browser/options.d.ts.map +1 -1
- package/dist/bus/command-bus.d.ts +6 -1
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +6 -1
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/event-bus.d.ts +6 -1
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +6 -1
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/context/bounded-context.d.ts +30 -28
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +136 -82
- package/dist/context/bounded-context.js.map +1 -1
- package/dist/context/entity-inbox.d.ts +12 -17
- package/dist/context/entity-inbox.d.ts.map +1 -1
- package/dist/context/entity-inbox.js +106 -119
- package/dist/context/entity-inbox.js.map +1 -1
- package/dist/context/local-inbox-handoff.d.ts +13 -13
- package/dist/context/local-inbox-handoff.d.ts.map +1 -1
- package/dist/context/local-inbox-handoff.js +60 -32
- package/dist/context/local-inbox-handoff.js.map +1 -1
- package/dist/context/persistent-tenant-index.d.ts +81 -0
- package/dist/context/persistent-tenant-index.d.ts.map +1 -0
- package/dist/context/persistent-tenant-index.js +270 -0
- package/dist/context/persistent-tenant-index.js.map +1 -0
- package/dist/context/projection-handoff.d.ts +9 -9
- package/dist/context/projection-handoff.d.ts.map +1 -1
- package/dist/context/projection-handoff.js +31 -23
- package/dist/context/projection-handoff.js.map +1 -1
- package/dist/context/tenant-index.d.ts +7 -6
- package/dist/context/tenant-index.d.ts.map +1 -1
- package/dist/context/tenant-index.js +2 -188
- package/dist/context/tenant-index.js.map +1 -1
- package/dist/context/tenant-unknown-error.d.ts +2 -2
- package/dist/context/tenant-unknown-error.d.ts.map +1 -1
- package/dist/context/tenant-unknown-error.js +4 -1
- package/dist/context/tenant-unknown-error.js.map +1 -1
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
- package/dist/delivery/delivery-supervisor.js +12 -3
- package/dist/delivery/delivery-supervisor.js.map +1 -1
- package/dist/delivery/delivery.d.ts +4 -3
- package/dist/delivery/delivery.d.ts.map +1 -1
- package/dist/delivery/delivery.js +82 -34
- package/dist/delivery/delivery.js.map +1 -1
- package/dist/delivery/reception-observation.d.ts +3 -3
- package/dist/delivery/reception-observation.js +2 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/repository/command-routing.d.ts +1 -1
- package/dist/repository/command-routing.js +1 -1
- package/dist/repository/event-routing.d.ts +1 -1
- package/dist/repository/event-routing.js +1 -1
- package/dist/repository/repository.d.ts +23 -31
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +244 -218
- package/dist/repository/repository.js.map +1 -1
- package/dist/repository/state-update-routing.d.ts +1 -1
- package/dist/repository/state-update-routing.js +1 -1
- package/dist/runtime/signal-publisher.d.ts +23 -8
- package/dist/runtime/signal-publisher.d.ts.map +1 -1
- package/dist/runtime/signal-publisher.js +11 -8
- package/dist/runtime/signal-publisher.js.map +1 -1
- package/dist/server/node-coordinator.d.ts.map +1 -1
- package/dist/server/node-coordinator.js +220 -54
- package/dist/server/node-coordinator.js.map +1 -1
- package/dist/server/server-environment.d.ts +6 -5
- package/dist/server/server-environment.d.ts.map +1 -1
- package/dist/server/server-environment.js +13 -9
- package/dist/server/server-environment.js.map +1 -1
- package/dist/server/server.js +1 -0
- package/dist/server/server.js.map +1 -1
- package/dist/services/app-query-execution.js +2 -2
- package/dist/services/app-query-execution.js.map +1 -1
- package/dist/services/native-operation.d.ts +34 -0
- package/dist/services/native-operation.d.ts.map +1 -0
- package/dist/services/native-operation.js +77 -0
- package/dist/services/native-operation.js.map +1 -0
- package/dist/services/spine-services.d.ts +6 -0
- package/dist/services/spine-services.d.ts.map +1 -1
- package/dist/services/spine-services.js +128 -30
- package/dist/services/spine-services.js.map +1 -1
- package/dist/services/trusted-reader-ingress.d.ts +23 -5
- package/dist/services/trusted-reader-ingress.d.ts.map +1 -1
- package/dist/services/trusted-reader-ingress.js +18 -5
- package/dist/services/trusted-reader-ingress.js.map +1 -1
- package/dist/stand/stand.d.ts +1 -1
- package/dist/stand/stand.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -14
package/README.md
CHANGED
|
@@ -44,7 +44,6 @@ Add generated entities with `BoundedContext.add(...)` and use
|
|
|
44
44
|
`build()` to assemble the context and load generated repositories when configured. A
|
|
45
45
|
context is single-tenant or multitenant by construction; select that mode
|
|
46
46
|
before registering application handlers and storage.
|
|
47
|
-
|
|
48
47
|
`build()` prepares the current model storage and awaits the first subscription
|
|
49
48
|
registry pass. Set `createStorageStructures: false` or
|
|
50
49
|
`startSubscriptionMonitoring: false` in `build({...})` to defer either phase
|
|
@@ -112,6 +111,15 @@ A failed handler leaves previous persisted state unchanged and
|
|
|
112
111
|
suppresses produced output. An external HTTP request or other side effect may
|
|
113
112
|
already have happened.
|
|
114
113
|
|
|
114
|
+
Repository-backed Command and Event posting resolves after recipient Inbox
|
|
115
|
+
writes. Delivery starts immediately and handles the stored row separately, so
|
|
116
|
+
successful posting does not mean the handler has completed. Restart durability
|
|
117
|
+
depends on the configured storage provider; custom dispatchers define their own
|
|
118
|
+
completion contract. A committed Entity awaits one outgoing handoff attempt for
|
|
119
|
+
handler-produced domain Commands and Events. A failed attempt is reported without
|
|
120
|
+
rerunning successful source handling. Internal diagnostic publication remains
|
|
121
|
+
best effort.
|
|
122
|
+
|
|
115
123
|
Handler return declarations can use native unions for one of several results,
|
|
116
124
|
or fixed tuples for several ordered results. For example,
|
|
117
125
|
`CreateAccessGrant | ExtendAccessGrant` selects one Command, while
|
|
@@ -232,14 +240,36 @@ override. Single-tenant execution uses `SINGLE_TENANT`, even when the request
|
|
|
232
240
|
omits a tenant field. A named tenant cannot query a single-tenant context: that
|
|
233
241
|
fails before reading. See the [query contract](REFERENCE.md#handler-routing-and-operations)
|
|
234
242
|
for the complete tenant rules and empty-result behavior.
|
|
235
|
-
For a multitenant receiving
|
|
236
|
-
through `
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
For a multitenant receiving Bounded Context, supply an application tenant directory and
|
|
244
|
+
register each accepted tenant through its `keep(tenantId)` method before sending
|
|
245
|
+
tenant-scoped work. The optional environment setting defaults to `SINGLE_TENANT`;
|
|
246
|
+
that mode creates no directory or provider catalog. A multitenant Bounded Context
|
|
247
|
+
requires an environment directory before assembly, even when `withTenantIndex()`
|
|
248
|
+
selects another directory for that Bounded Context. In one environment, a
|
|
249
|
+
single-tenant Identity Bounded Context still uses `SINGLE_TENANT` while a
|
|
250
|
+
multitenant Orders Bounded Context uses the configured directory.
|
|
251
|
+
Both public `tenantIndex` getters return `TenantIndexSetting`, so narrow against
|
|
252
|
+
`SINGLE_TENANT` before calling directory methods. A single-tenant Bounded Context always
|
|
253
|
+
returns the marker, even when its environment has a directory for other Bounded Contexts.
|
|
254
|
+
Provider catalogs remain available as storage-provider APIs, but environment
|
|
255
|
+
assembly does not infer membership from them.
|
|
256
|
+
`PersistentTenantIndex.create({ storageFactory, group })` stores registrations
|
|
257
|
+
through the selected factory. Remote providers can retain them across process
|
|
258
|
+
restarts; memory storage remains process-local. Its async factory prepares supported structures by
|
|
259
|
+
default; set `createMissingStructures: false` when the application prepares
|
|
260
|
+
the selected record family separately before data access. Close the index after its admitted work
|
|
261
|
+
finishes; it leaves the supplied storage factory open.
|
|
241
262
|
Unknown tenants fail before reads or handlers run; input never registers them.
|
|
242
263
|
|
|
264
|
+
The [Orders application](https://github.com/SpineEventEngine/spine-ts/blob/master/examples/orders/README.md)
|
|
265
|
+
demonstrates this setup with memory, PostgreSQL, and Datastore. Its PostgreSQL
|
|
266
|
+
launch uses separate shared, Northwind, and Contoso databases, validates persisted
|
|
267
|
+
membership against configured routing, and prepares supported structures before
|
|
268
|
+
opening the listener. Orders focuses on tenant storage and has no sign-in UI.
|
|
269
|
+
The [Fieldnotes application](https://github.com/SpineEventEngine/spine-ts/blob/master/examples/fieldnotes/README.md)
|
|
270
|
+
demonstrates browser authentication and private data in a single-tenant Bounded
|
|
271
|
+
Context without a tenant index.
|
|
272
|
+
|
|
243
273
|
Use `EntityQuery.all(...)` when every predicate must match and
|
|
244
274
|
`EntityQuery.either(...)` when any branch may match.
|
|
245
275
|
|
|
@@ -411,6 +441,21 @@ and backend forwarding. Authenticated durable bindings are browser-host
|
|
|
411
441
|
infrastructure, not native `Server` state. See the [browser/auth extension guide](https://github.com/SpineEventEngine/spine-ts/blob/master/docs/BROWSER_CLIENT_AUTH_EXTENSION_GUIDE.md)
|
|
412
442
|
before exposing a backend to a browser.
|
|
413
443
|
|
|
444
|
+
For a standalone Gateway, call `BrowserServer.run` with a canonical
|
|
445
|
+
`backend.baseUrl` (or discovery), `origins`, `sessions`, `authorize`, `contexts`,
|
|
446
|
+
`clock`, and explicit `bindings`. Supply `credentials` with a
|
|
447
|
+
`BrowserCredentialReader` to select a browser auth engine. The reader and each
|
|
448
|
+
`authRoutes` entry declare `csrfHeaderNames`, `credentialCookieNames`, and
|
|
449
|
+
`stateCookieNames`; empty arrays state that a category is unused. The Gateway
|
|
450
|
+
validates those names before it starts listening, then checks raw headers before
|
|
451
|
+
calling the reader or a route. Preflight never calls the credential reader;
|
|
452
|
+
permitted origins receive credentialed CORS headers covering the configured CSRF
|
|
453
|
+
fields and Connect protocol headers. Connect stops waiting for a reader and
|
|
454
|
+
does not forward its late result when the request is cancelled or its deadline
|
|
455
|
+
passes. The reader receives no abort signal, so its underlying work may continue.
|
|
456
|
+
This setup does not construct a
|
|
457
|
+
`ServerEnvironment` or Bounded Context. Close the returned server at shutdown.
|
|
458
|
+
|
|
414
459
|
## Native reader admission
|
|
415
460
|
|
|
416
461
|
Native authentication is configured through `ServerOptions.services.readerAdmission`.
|
|
@@ -423,7 +468,10 @@ public and supplies no authenticated principal or authority facts.
|
|
|
423
468
|
|
|
424
469
|
Subscription activation and cancellation also check the admitted principal
|
|
425
470
|
against the stored reader binding. A different principal cannot reuse a
|
|
426
|
-
subscription even with the same ActorContext.
|
|
471
|
+
subscription even with the same ActorContext. The host supplies an operation
|
|
472
|
+
signal before authentication and closes it after unary work or the complete
|
|
473
|
+
Activate stream. A direct authenticator can return a separately captured
|
|
474
|
+
`invalidated` signal for local logout and session expiry. See [native reader admission](REFERENCE.md#native-reader-admission)
|
|
427
475
|
for adapter signatures, public admission, and managed application configuration.
|
|
428
476
|
|
|
429
477
|
## Limits
|
package/REFERENCE.md
CHANGED
|
@@ -168,7 +168,7 @@ phase without implying that every Entity model has been prepared.
|
|
|
168
168
|
awaits both model preparation and the first subscription reconciliation by
|
|
169
169
|
default. Each option can be set to `false` independently. A deferred phase runs
|
|
170
170
|
before the first valid Command, Event, query, subscription operation, or Inbox
|
|
171
|
-
|
|
171
|
+
delivery for the receiving context; lazy multitenant preparation selects only that
|
|
172
172
|
input's currently registered tenant. A failed lazy attempt rejects its current
|
|
173
173
|
input and can be retried by a later input. Monitoring requires the shared
|
|
174
174
|
subscription registry, so `startSubscriptionMonitoring: true` prepares that
|
|
@@ -208,20 +208,33 @@ contexts share one in-memory factory, including in production. Local and test
|
|
|
208
208
|
environments default storage in memory and use the core registry only as a
|
|
209
209
|
fallback; those defaults are not production configuration.
|
|
210
210
|
|
|
211
|
-
`tenantIndex`
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
`tenantIndex` is optional and defaults to `SINGLE_TENANT`, a marker that creates
|
|
212
|
+
no directory and never consults a provider catalog. The environment getter
|
|
213
|
+
returns `TenantIndexSetting`, either that marker or an application-supplied
|
|
214
|
+
`TenantIndex`. Narrow against the marker before calling `contains()`, `keep()`,
|
|
215
|
+
`page()`, or `all()`. A multitenant Bounded Context requires a real environment
|
|
216
|
+
index before business storage opens. A single-tenant Bounded Context always returns the
|
|
217
|
+
marker, including inside a multitenant environment. Provider catalogs remain
|
|
218
|
+
separate storage-provider APIs and do not select application membership.
|
|
219
|
+
|
|
220
|
+
`PersistentTenantIndex.create({ storageFactory, group })` opens a separate,
|
|
221
|
+
storage-backed registration directory without constructing a Bounded Context or
|
|
222
|
+
`ServerEnvironment`. It creates and checks supported storage structures before
|
|
223
|
+
returning. Set `createMissingStructures: false` to skip preparation on creation
|
|
224
|
+
and on later reads or writes. A skipped directory needs structures prepared by
|
|
225
|
+
the application before data access; missing structures cause provider errors.
|
|
226
|
+
Memory storage remains process-local. The injected factory remains usable after the
|
|
227
|
+
index closes. `keep()` registers a complete `TenantId` atomically and retains
|
|
228
|
+
the first registration time; `contains()` reads its primary key. `page()` uses
|
|
229
|
+
finite keyset traversal with an abort signal and a continuation accepted only
|
|
230
|
+
by that index instance. `all()` traverses the complete current directory, so
|
|
231
|
+
its result can be large. Traversal does not promise a snapshot while other
|
|
232
|
+
writers register tenants. Registration does not provision tenant databases or
|
|
233
|
+
authorize users. The environment defaults to the explicit `SINGLE_TENANT` marker.
|
|
221
234
|
|
|
222
235
|
Multitenant receiving contexts check current index membership before Command,
|
|
223
236
|
Event, query, direct repository/history, and subscription work. Inbox persistence
|
|
224
|
-
and later
|
|
237
|
+
and later delivery check separately, so removing a tenant prevents saved work from
|
|
225
238
|
restoring it. Input does not call `keep()` implicitly. A confirmed missing member
|
|
226
239
|
raises `TenantUnknownError` with the complete tenant ID and context name; native
|
|
227
240
|
services return `NotFound` with a structured `spine.base.Error` detail whose type
|
|
@@ -249,8 +262,8 @@ Direct `repository.routeCommand()` and `repository.routeEvent()` calls return
|
|
|
249
262
|
Promises and must be awaited. Commands select exactly one ID; Events and state
|
|
250
263
|
updates may select any number of distinct IDs. After validation and stable
|
|
251
264
|
deduplication, routes above 1,000 recipients emit one warning and retain every
|
|
252
|
-
recipient. Inbox
|
|
253
|
-
|
|
265
|
+
recipient. Inbox delivery uses stored target IDs without rerouting. The local
|
|
266
|
+
`catchUpReadSide()` helper is unrelated to retry: it resets and dispatches stored Events through
|
|
254
267
|
the whole local read side, and is not Projection catch-up.
|
|
255
268
|
|
|
256
269
|
## Message-valued Entity IDs
|
|
@@ -282,7 +295,7 @@ const id: MessageId = create(CommandIdSchema, { uuid: "shipment-7" });
|
|
|
282
295
|
```
|
|
283
296
|
|
|
284
297
|
Admission validates a message ID against the schema declared by the state
|
|
285
|
-
field, including generated validation rules, before it is stored or
|
|
298
|
+
field, including generated validation rules, before it is stored or delivered.
|
|
286
299
|
When default routing finds a message but the Entity ID type is primitive, a
|
|
287
300
|
custom route returns the intended primitive ID.
|
|
288
301
|
|
|
@@ -596,9 +609,9 @@ repeated declarations fail closed. It is not an alternative routing mechanism.
|
|
|
596
609
|
Configure logging through `ServerEnvironment` when assembling the server. The
|
|
597
610
|
server installs that logger into its runtime components, where warnings and
|
|
598
611
|
failures carry operation context. Durable facts belong in events or storage.
|
|
599
|
-
Ordinary Aggregate, Process Manager, and Projection Inbox
|
|
600
|
-
accepted work through the normal handler path
|
|
601
|
-
|
|
612
|
+
Ordinary Aggregate, Process Manager, and Projection Inbox delivery sends persisted
|
|
613
|
+
accepted work through the normal handler path. Handler effects must tolerate
|
|
614
|
+
redelivery after an uncertain acknowledgement. An accepted Agent interaction is recorded by its receipt and is
|
|
602
615
|
never re-executed after interruption.
|
|
603
616
|
|
|
604
617
|
## Context assembly and entities
|
|
@@ -607,9 +620,11 @@ Create a context with `BoundedContext.singleTenant(name)` or
|
|
|
607
620
|
`BoundedContext.multitenant(name)`. Names are immutable. Use
|
|
608
621
|
`ServerEnvironment.when(type).use({ storageFactory: factory })` before explicit
|
|
609
622
|
environment setup to supply record/event storage to all contexts.
|
|
610
|
-
`withTenantIndex(index)` selects a different live membership directory for
|
|
611
|
-
|
|
612
|
-
|
|
623
|
+
`withTenantIndex(index)` selects a different live membership directory for a
|
|
624
|
+
multitenant Bounded Context after its environment has a real index. The Bounded Context getter
|
|
625
|
+
returns its captured selection; single-tenant Bounded Contexts return `SINGLE_TENANT`.
|
|
626
|
+
Both getters have type `TenantIndexSetting` and require narrowing before index
|
|
627
|
+
methods. Closing a Bounded Context does not close the configured directory.
|
|
613
628
|
The application imports its generated `spine-bootstrap.js` before registering
|
|
614
629
|
classes with `add(EntityClass)`; `build()` then selects passive descriptions
|
|
615
630
|
by exact constructor and performs assembly, including
|
|
@@ -911,6 +926,12 @@ work to stop using dependencies, then closes contexts. Caller-managed `start()`
|
|
|
911
926
|
does not close global process facilities; close `ServerEnvironment.instance()`
|
|
912
927
|
separately when used. The final run-managed `run()` close closes its environment.
|
|
913
928
|
|
|
929
|
+
Native QueryService accepts `BytesValue` equality and range operands for annotated scalar `bytes`
|
|
930
|
+
columns and permits ascending or descending byte ordering. The shared evaluator uses unsigned
|
|
931
|
+
lexicographic comparison, including shorter equal prefixes first. This TypeScript query capability
|
|
932
|
+
does not assert JVM JDBC byte-range compatibility. `QUERY_SERVICE_MAX_RESULTS` from core is the
|
|
933
|
+
current capacity of 1,000 returned and evaluated records for one native read, not a collection-size limit.
|
|
934
|
+
|
|
914
935
|
`ServerOptions.appQueries` selects concrete generated `AppQueryHandler<Q, R>`
|
|
915
936
|
instances by exact constructor metadata before listening. Duplicate Q routes
|
|
916
937
|
or missing metadata fail startup. On direct app-node ingress,
|
|
@@ -1110,8 +1131,8 @@ export const scopedOnCreate =
|
|
|
1110
1131
|
```
|
|
1111
1132
|
|
|
1112
1133
|
`Stand` registers entity state for point/list reads, updates current state, and
|
|
1113
|
-
offers in-process subscriptions. The
|
|
1114
|
-
`catchUpReadSide(options?)` clears every registered Projection state and
|
|
1134
|
+
offers in-process subscriptions. The local read-side reset helper
|
|
1135
|
+
`catchUpReadSide(options?)` clears every registered Projection state and dispatches
|
|
1115
1136
|
the whole stored event history through matching Projection subscribers; it does
|
|
1116
1137
|
not append events. Single-tenant contexts reject a tenant option; multitenant
|
|
1117
1138
|
contexts require one. It cannot target a Projection repository, entity IDs, or
|
|
@@ -1263,11 +1284,14 @@ transport responses independently.
|
|
|
1263
1284
|
|
|
1264
1285
|
Configure app-node admission at `ServerOptions.services.readerAdmission`:
|
|
1265
1286
|
|
|
1266
|
-
- `direct.authenticate({ service, method, value, headers, peer })` authenticates
|
|
1267
|
-
a direct native request. It returns
|
|
1268
|
-
|
|
1287
|
+
- `direct.authenticate({ service, method, value, headers, peer, signal })` authenticates
|
|
1288
|
+
a direct native request. It returns `{ reader, invalidated? }` with verified
|
|
1289
|
+
`{ principal, authorityFacts }` in `reader`, `{ reader: {} }` for deliberate
|
|
1290
|
+
public access, or `undefined` to deny access. `signal` begins before
|
|
1291
|
+
authentication and ends on terminal unary or complete stream exit.
|
|
1269
1292
|
The request value contains canonical Proto bytes; `peer` is the Connect
|
|
1270
|
-
`HandlerContext` for that invocation.
|
|
1293
|
+
`HandlerContext` for that invocation. `invalidated` is a separate local
|
|
1294
|
+
AbortSignal captured by the host; it never enters reader facts or proofs.
|
|
1271
1295
|
- `forwarded.verify(bytes, proof, peer)` verifies an assertion from a trusted
|
|
1272
1296
|
forwarding peer. It must authenticate that peer and validate the supplied
|
|
1273
1297
|
proof over the exact bytes. `BrowserServerOptions.forwardingProof.issue(bytes)`
|
|
@@ -1294,6 +1318,69 @@ child Servers with their verifier as well. Each hop verifies its incoming
|
|
|
1294
1318
|
assertion and issues a new proof for the actual outgoing operation and payload.
|
|
1295
1319
|
Loopback placement alone does not authenticate a forwarding peer.
|
|
1296
1320
|
|
|
1321
|
+
A standard session strategy accepts the host signal during `resolve` and
|
|
1322
|
+
returns an invalidation signal for local logout, its captured session deadline,
|
|
1323
|
+
and strategy close. A custom direct authenticator may omit invalidation, but
|
|
1324
|
+
then the host cannot stop an active stream on local logout or expiry. A
|
|
1325
|
+
successful unary request releases its operation even while the transport peer
|
|
1326
|
+
stays connected. Subscribe releases its operation after definition creation;
|
|
1327
|
+
Activate authenticates again and holds its operation through ACK and every
|
|
1328
|
+
update. Normal completion, host handler iterator return, cancellation, and host
|
|
1329
|
+
close stop the operation. Raw Connect clients must abort an explicit
|
|
1330
|
+
`AbortSignal` or call `SubscriptionService.Cancel` to stop remote stream work;
|
|
1331
|
+
returning from the client iterator alone does not signal transport cancellation.
|
|
1332
|
+
An active stream in another process receives no local signal.
|
|
1333
|
+
|
|
1334
|
+
```ts
|
|
1335
|
+
import type { SignedSessions } from "@spine-event-engine/auth";
|
|
1336
|
+
import { Server, type ReaderAdmissionResult, type ServerOptions } from "@spine-event-engine/server";
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Configures direct native bearer admission with the local session strategy.
|
|
1340
|
+
*
|
|
1341
|
+
* @param sessions Strategy also used by the application's logout action.
|
|
1342
|
+
* @param contexts Application Bounded Contexts exposed by this Server.
|
|
1343
|
+
* @returns Server configured for native authentication.
|
|
1344
|
+
*/
|
|
1345
|
+
function nativeServer(
|
|
1346
|
+
sessions: SignedSessions,
|
|
1347
|
+
contexts: NonNullable<ServerOptions["contexts"]>,
|
|
1348
|
+
): Server {
|
|
1349
|
+
return new Server({
|
|
1350
|
+
contexts,
|
|
1351
|
+
services: {
|
|
1352
|
+
readerAdmission: {
|
|
1353
|
+
direct: {
|
|
1354
|
+
/**
|
|
1355
|
+
* Resolves the bearer credential within the host operation.
|
|
1356
|
+
*
|
|
1357
|
+
* @param request Native peer and lifetime facts.
|
|
1358
|
+
* @returns Verified reader and invalidation, or denial.
|
|
1359
|
+
*/
|
|
1360
|
+
async authenticate(request): Promise<ReaderAdmissionResult | undefined> {
|
|
1361
|
+
const authorization = request.peer.requestHeader.get("authorization");
|
|
1362
|
+
const token = /^Bearer ([^\s,]+)$/i.exec(authorization ?? "")?.[1];
|
|
1363
|
+
if (token === undefined) return undefined;
|
|
1364
|
+
const session = await sessions.resolve(
|
|
1365
|
+
{ kind: "bearer", value: token },
|
|
1366
|
+
request.signal,
|
|
1367
|
+
);
|
|
1368
|
+
if (session?.invalidated === undefined) return undefined;
|
|
1369
|
+
return { reader: { principal: session.principal }, invalidated: session.invalidated };
|
|
1370
|
+
},
|
|
1371
|
+
},
|
|
1372
|
+
},
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
The admitted logout action checks `await sessions.logout(credential)` on that
|
|
1379
|
+
same strategy. It reports durable success only for `revoked`; `unavailable`
|
|
1380
|
+
and `expiryOnly` remain distinct. Application shutdown closes both Server and
|
|
1381
|
+
strategy. A custom authenticator still enforces its own credential and policy
|
|
1382
|
+
checks; a token in a header alone does not establish authority.
|
|
1383
|
+
|
|
1297
1384
|
Authenticated forwarding now requires an issuer; configure both gateway and
|
|
1298
1385
|
receiving node when updating an existing authenticated deployment. The native
|
|
1299
1386
|
Server root still does not load the optional auth package. The forwarding ports
|
|
@@ -1305,6 +1392,11 @@ For `BrowserServer` from `@spine-event-engine/server/browser`, origins must be u
|
|
|
1305
1392
|
fragment, or trailing slash. Requests without an allowed exact `Origin` receive
|
|
1306
1393
|
403 before RPC handling. Allowed responses include credentialed exact-origin
|
|
1307
1394
|
CORS, `Vary: Origin`, protocol request headers, and exposed gRPC status headers.
|
|
1395
|
+
RPC and auth-route preflight completes before credential extraction. The host
|
|
1396
|
+
allows configured CSRF headers alongside Connect and gRPC-Web protocol headers;
|
|
1397
|
+
actual responses, including permitted-origin terminal errors, retain exact-origin
|
|
1398
|
+
credentialed CORS. Callback responses cannot replace that host policy with a
|
|
1399
|
+
wildcard origin.
|
|
1308
1400
|
|
|
1309
1401
|
Supplying `browser.backend.baseUrl` selects standalone mode. It is one canonical
|
|
1310
1402
|
HTTP(S) origin for a Spine TS or JVM backend; the gateway never closes
|
|
@@ -1324,7 +1416,8 @@ re-query authoritative state after duplicate updates or generic loss notices.
|
|
|
1324
1416
|
Each registration has one exact `GET` or `POST` canonical path, one method per
|
|
1325
1417
|
path, exact canonical origins, finite request bytes, and a finite timeout. A
|
|
1326
1418
|
missing Origin is rejected unless that route explicitly permits it for an OAuth
|
|
1327
|
-
callback
|
|
1419
|
+
callback and validates the protocol state in its handler.
|
|
1420
|
+
Unknown paths, method/origin failures, body overflow, timeout, and
|
|
1328
1421
|
handler failure return fixed 404, 405, 403, 413, 504, and 500 responses without
|
|
1329
1422
|
calling application code on rejected input. The 404 unknown-path result applies
|
|
1330
1423
|
only after framework Origin admission. Auth admission is bounded to 64 active
|
|
@@ -1335,10 +1428,18 @@ transfer. Response bodies use `writeMaxBytes`; overflow returns 413 before any
|
|
|
1335
1428
|
application status or headers are committed. Listener close ultimately aborts
|
|
1336
1429
|
an active request through its disconnect signal.
|
|
1337
1430
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1431
|
+
Every credential reader and auth route declares CSRF header names, primary
|
|
1432
|
+
credential-cookie names, and supporting state-cookie names. The host snapshots
|
|
1433
|
+
these declarations before startup, rejects repeated raw Authorization, Origin,
|
|
1434
|
+
configured CSRF headers, and selected cookies, and checks simultaneous primary
|
|
1435
|
+
credentials only for the active reader or selected route. Legal split Cookie
|
|
1436
|
+
fields and repeated unrelated cookies remain available to the reader.
|
|
1437
|
+
`OpaqueSessionCookies` provides a strict reader for a sealed opaque session or
|
|
1438
|
+
bearer credential, including CSRF checks; a primary cookie and bearer field
|
|
1439
|
+
together are rejected. The legacy `cookies` option cannot be combined with
|
|
1440
|
+
`credentials`. Without either, the host reads one valid Bearer field. A reader
|
|
1441
|
+
returns a credential, absence, or terminal rejection; a thrown read is terminal
|
|
1442
|
+
unavailability. Credentials and raw
|
|
1342
1443
|
transport metadata do not reach the private backend. The application-supplied
|
|
1343
1444
|
context resolver replaces caller actor and tenant facts before forwarding.
|
|
1344
1445
|
|
|
@@ -1381,11 +1482,21 @@ delivery, global update ordering, nor cluster-complete notification delivery.
|
|
|
1381
1482
|
local durable-row abstraction. Inbox and shard storage require a single-tenant
|
|
1382
1483
|
Delivery storage context shared by all tenants; the packed signal supplies
|
|
1383
1484
|
the dispatch tenant. Read limits are 1–1000; work leases are
|
|
1384
|
-
1000–2147483647 ms. Framework
|
|
1385
|
-
updates,
|
|
1485
|
+
1000–2147483647 ms. Framework delivery supports command handling, projection
|
|
1486
|
+
updates, process-manager reactions, and Aggregate Event reactions. Callbacks must tolerate redelivery:
|
|
1386
1487
|
lost renewal can prevent stale finalization but cannot undo a callback already
|
|
1387
1488
|
run. The package exposes no general raw worker callback API.
|
|
1388
1489
|
|
|
1490
|
+
Repository-backed Command and Event routing awaits recipient Inbox writes and
|
|
1491
|
+
notifies Delivery immediately. A successful post through this routing means
|
|
1492
|
+
recipient Inbox writes completed; it does not wait for handler execution or row
|
|
1493
|
+
acknowledgement. Restart durability depends on the configured storage provider.
|
|
1494
|
+
Custom dispatchers define their own completion contract. After an Entity commit, the
|
|
1495
|
+
source delivery awaits one outgoing bus handoff attempt through recipient Inbox
|
|
1496
|
+
acceptance. A failed publication is reported and contained without re-executing
|
|
1497
|
+
a successful source Command. Entity storage, recipient Inbox writes, and source
|
|
1498
|
+
acknowledgement remain separate operations.
|
|
1499
|
+
|
|
1389
1500
|
Inbox reads return `InboxMessage` with a full-precision Protobuf `Timestamp` in
|
|
1390
1501
|
`whenReceived`. `InboxMessageInput` requires a precise `Timestamp` for new
|
|
1391
1502
|
receipts. Writes, acknowledgements, and exact removals use an `InboxMessage`
|
|
@@ -1441,7 +1552,8 @@ internally and persist their envelope in the target Entity Inbox before any
|
|
|
1441
1552
|
handler runs. Projection input uses the same environment Delivery and shared
|
|
1442
1553
|
Inbox, with its separate recipient identity.
|
|
1443
1554
|
|
|
1444
|
-
Local posting waits for
|
|
1555
|
+
Local repository-backed posting waits for recipient Inbox writes and scheduling,
|
|
1556
|
+
while handler execution and row acknowledgement continue through Delivery. For an
|
|
1445
1557
|
Agent, successful acceptance and Inbox acknowledgement let posting finish while
|
|
1446
1558
|
the live handler continues. If Agent execution capacity is unavailable, the input
|
|
1447
1559
|
stays pending and the local call reports the existing command-posting error.
|
|
@@ -1487,9 +1599,9 @@ scan delivers nothing; it does not restart after each productive page.
|
|
|
1487
1599
|
Delivery compares pending rows with delivered rows in the current page
|
|
1488
1600
|
and a process-local cache of the 1,000 most recent deliveries, then removes
|
|
1489
1601
|
duplicates. The 30-second Inbox deduplication window controls how long delivered
|
|
1490
|
-
rows remain available as duplicate evidence; it is not a
|
|
1602
|
+
rows remain available as duplicate evidence; it is not a delivery-retention
|
|
1491
1603
|
period. Persisted accepted rows follow their delivery lifecycle and may be
|
|
1492
|
-
|
|
1604
|
+
redelivered according to that lifecycle after the deduplication window has elapsed.
|
|
1493
1605
|
|
|
1494
1606
|
Direct delivery uses the configured Inbox storage clock for both retained-row
|
|
1495
1607
|
duplicate checks and early cleanup filtering. Remote delivery uses local client
|
|
@@ -1510,8 +1622,8 @@ reception monitor. A custom reception action that completes normally completes
|
|
|
1510
1622
|
delivery and the enclosing stage acknowledges the row. An action that rejects
|
|
1511
1623
|
propagates its error and leaves the row `TO_DELIVER`. `markDelivered()` does not
|
|
1512
1624
|
mean domain processing succeeded.
|
|
1513
|
-
Managed repository
|
|
1514
|
-
storage and
|
|
1625
|
+
Managed repository delivery sends actual handler failures to the monitor while
|
|
1626
|
+
storage and delivery infrastructure errors propagate. A direct `onMessage` callback
|
|
1515
1627
|
is treated as the endpoint. Handler effects and acknowledgement are separate,
|
|
1516
1628
|
so a crash between them may redeliver.
|
|
1517
1629
|
The framework adds no persisted attempts, quarantine, dead-letter, receipt, or
|
|
@@ -280,7 +280,7 @@ export declare class AgentLiveAiRuntime implements AgentAi {
|
|
|
280
280
|
*/
|
|
281
281
|
private ticket;
|
|
282
282
|
/**
|
|
283
|
-
* Completes measured response bytes without
|
|
283
|
+
* Completes measured response bytes without recorded state.
|
|
284
284
|
*
|
|
285
285
|
* @param attempts Physical attempt state for this invocation.
|
|
286
286
|
* @param audit Retained history audit for this invocation.
|
|
@@ -578,7 +578,7 @@ export class AgentLiveAiRuntime {
|
|
|
578
578
|
return ticket;
|
|
579
579
|
}
|
|
580
580
|
/**
|
|
581
|
-
* Completes measured response bytes without
|
|
581
|
+
* Completes measured response bytes without recorded state.
|
|
582
582
|
*
|
|
583
583
|
* @param attempts Physical attempt state for this invocation.
|
|
584
584
|
* @param audit Retained history audit for this invocation.
|
|
@@ -13,7 +13,7 @@ export interface AgentPhysicalTicket {
|
|
|
13
13
|
readonly maxOutputBytes: number | undefined;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Counts one live invocation without durable
|
|
16
|
+
* Counts one live invocation without durable restart or unacknowledged writes.
|
|
17
17
|
*/
|
|
18
18
|
export declare class AgentLiveBudget {
|
|
19
19
|
#private;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { AgentLiveFault } from "./agent-live-fault.js";
|
|
15
15
|
/**
|
|
16
|
-
* Counts one live invocation without durable
|
|
16
|
+
* Counts one live invocation without durable restart or unacknowledged writes.
|
|
17
17
|
*/
|
|
18
18
|
export class AgentLiveBudget {
|
|
19
19
|
limits;
|
|
@@ -7,7 +7,7 @@ import type { AgentHistoryPage } from "@spine-event-engine/storage/provider";
|
|
|
7
7
|
import type { AgentHistoryScope } from "./agent-history.js";
|
|
8
8
|
import { AgentLiveBudget } from "./agent-live-budget.js";
|
|
9
9
|
/**
|
|
10
|
-
* Sequences live Agent reads without durable snapshots or
|
|
10
|
+
* Sequences live Agent reads without durable snapshots or recorded-operation restart.
|
|
11
11
|
*/
|
|
12
12
|
export declare class AgentLiveReadRuntime {
|
|
13
13
|
#private;
|
|
@@ -16,7 +16,7 @@ import { TypeUrls } from "@spine-event-engine/core";
|
|
|
16
16
|
import { AgentHistoryEntrySchema } from "@spine-event-engine/proto/agent";
|
|
17
17
|
import { AgentLiveBudget } from "./agent-live-budget.js";
|
|
18
18
|
/**
|
|
19
|
-
* Sequences live Agent reads without durable snapshots or
|
|
19
|
+
* Sequences live Agent reads without durable snapshots or recorded-operation restart.
|
|
20
20
|
*/
|
|
21
21
|
export class AgentLiveReadRuntime {
|
|
22
22
|
scope;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type * as http from "node:http";
|
|
2
|
+
import type { BrowserAuthRoute, BrowserCredentialAdmission } from "./options.js";
|
|
3
|
+
/**
|
|
4
|
+
* Immutable startup admission names and raw request inspection for one browser host.
|
|
5
|
+
*/
|
|
6
|
+
export declare class BrowserAdmissionPolicy {
|
|
7
|
+
#private;
|
|
8
|
+
/**
|
|
9
|
+
* Captured duplicate-sensitive names for the active RPC credential reader.
|
|
10
|
+
*/
|
|
11
|
+
readonly reader: BrowserCredentialAdmission;
|
|
12
|
+
/**
|
|
13
|
+
* Captured duplicate-sensitive names indexed by exact authentication route path.
|
|
14
|
+
*/
|
|
15
|
+
readonly routes: ReadonlyMap<string, BrowserCredentialAdmission>;
|
|
16
|
+
/**
|
|
17
|
+
* Stores the validated reader and route names for one listener.
|
|
18
|
+
* @param reader Active RPC credential reader declaration.
|
|
19
|
+
* @param routes Authentication route declarations indexed by exact path.
|
|
20
|
+
*/
|
|
21
|
+
private constructor();
|
|
22
|
+
/**
|
|
23
|
+
* Validates and copies every declaration before the first asynchronous startup step.
|
|
24
|
+
* @param reader Active RPC credential reader declaration.
|
|
25
|
+
* @param routes Validated route configurations indexed by pathname.
|
|
26
|
+
* @returns One immutable admission policy for startup and requests.
|
|
27
|
+
*/
|
|
28
|
+
static create(reader: unknown, routes: ReadonlyMap<string, BrowserAuthRoute>): BrowserAdmissionPolicy;
|
|
29
|
+
/**
|
|
30
|
+
* Rejects ambiguous raw headers and returns an immutable duplicate-preserving view.
|
|
31
|
+
* @param request Incoming Node request before WHATWG normalization.
|
|
32
|
+
* @param active Reader or selected route declaration for primary conflict checks.
|
|
33
|
+
* @returns Raw header view or a terminal admission rejection.
|
|
34
|
+
*/
|
|
35
|
+
inspect(request: http.IncomingMessage, active: BrowserCredentialAdmission): {
|
|
36
|
+
readonly kind: "admitted";
|
|
37
|
+
readonly headers: RawBrowserHeaders;
|
|
38
|
+
} | {
|
|
39
|
+
readonly kind: "rejected";
|
|
40
|
+
readonly reason: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Duplicate-preserving lowercase raw HTTP header values.
|
|
45
|
+
*/
|
|
46
|
+
export type RawBrowserHeaders = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
47
|
+
//# sourceMappingURL=browser-admission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-admission.d.ts","sourceRoot":"","sources":["../../src/browser/browser-admission.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AAEvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAEjF;;GAEG;AACH,qBAAa,sBAAsB;;IACjC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAE5C;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;IAMjE;;;;OAIG;IACH,OAAO;IAaP;;;;;OAKG;IACH,MAAM,CAAC,MAAM,CACX,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC5C,sBAAsB;IAMzB;;;;;OAKG;IACH,OAAO,CACL,OAAO,EAAE,IAAI,CAAC,eAAe,EAC7B,MAAM,EAAE,0BAA0B,GAEhC;QAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;KAAE,GAClE;QAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE;CAW3D;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC"}
|