@spfn/core 0.3.0-beta.24 → 0.3.0-beta.26
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 +4 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/event/index.d.ts +3 -3
- package/dist/event/index.js +30 -10
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +2 -2
- package/dist/event/sse/client.js +12 -1
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +3 -3
- package/dist/event/sse/index.js +19 -9
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +2 -2
- package/dist/event/ws/index.d.ts +3 -3
- package/dist/{router-ZDV1ISTo.d.ts → router-D5B0cpVN.d.ts} +39 -10
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +19 -9
- package/dist/server/index.js.map +1 -1
- package/dist/state/client.d.ts +144 -0
- package/dist/state/client.js +478 -0
- package/dist/state/client.js.map +1 -0
- package/dist/state/index.d.ts +395 -0
- package/dist/state/index.js +578 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/react.d.ts +34 -0
- package/dist/state/react.js +44 -0
- package/dist/state/react.js.map +1 -0
- package/dist/{types-C_g6gD-w.d.ts → types-04gu3uMj.d.ts} +8 -1
- package/dist/{types-cwMX0cyJ.d.ts → types-CqTsDETV.d.ts} +1 -1
- package/package.json +26 -2
- package/src/event/README.md +48 -10
- package/src/state/README.md +539 -0
|
@@ -21,6 +21,18 @@ interface PubSubCache {
|
|
|
21
21
|
* Event handler function type
|
|
22
22
|
*/
|
|
23
23
|
type EventHandler<TPayload> = (payload: TPayload) => void | Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Options for `EventDef.subscribe`.
|
|
26
|
+
*/
|
|
27
|
+
interface EventSubscribeOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Run only for an `emit` made in this process, never for a delivery the
|
|
30
|
+
* cache transport received from another pod. For a handler whose work must
|
|
31
|
+
* happen once per emit, not once per pod (it would otherwise run on every
|
|
32
|
+
* pod the event is cached to). Without a cache it runs on every `emit`.
|
|
33
|
+
*/
|
|
34
|
+
localOnly?: boolean;
|
|
35
|
+
}
|
|
24
36
|
/**
|
|
25
37
|
* Job queue sender function type (used by job module)
|
|
26
38
|
*/
|
|
@@ -87,15 +99,24 @@ interface FrameContext {
|
|
|
87
99
|
* the connection asked for and never reads storage.
|
|
88
100
|
*/
|
|
89
101
|
type FramePredicate<TPayload> = (payload: TPayload, ctx: FrameContext) => boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Which of a subject's connections a `grant` / `revoke` reaches: those whose
|
|
104
|
+
* connect query carries every named field with exactly that value. A
|
|
105
|
+
* connection that declared no such field never matches. Plain JSON, so it
|
|
106
|
+
* travels to other pods unchanged.
|
|
107
|
+
*/
|
|
108
|
+
interface SubscriptionMatch {
|
|
109
|
+
query: Record<string, string>;
|
|
110
|
+
}
|
|
90
111
|
/**
|
|
91
112
|
* How an SSE handler lets `grant` / `revoke` reach its open connections.
|
|
92
113
|
* Each handler registers one per router key of the event.
|
|
93
114
|
*/
|
|
94
115
|
interface SubscriptionControl {
|
|
95
|
-
/** Activate the event on every open connection of `subject` where it is denied. */
|
|
96
|
-
grant: (subject: string) => void;
|
|
97
|
-
/** Deactivate the event on every open connection of `subject` where it is allowed. */
|
|
98
|
-
revoke: (subject: string) => void;
|
|
116
|
+
/** Activate the event on every open connection of `subject` (matching `match`, when given) where it is denied. */
|
|
117
|
+
grant: (subject: string, match?: SubscriptionMatch) => void;
|
|
118
|
+
/** Deactivate the event on every open connection of `subject` (matching `match`, when given) where it is allowed. */
|
|
119
|
+
revoke: (subject: string, match?: SubscriptionMatch) => void;
|
|
99
120
|
}
|
|
100
121
|
/**
|
|
101
122
|
* Event definition interface
|
|
@@ -163,11 +184,13 @@ interface EventDef<TPayload = void> {
|
|
|
163
184
|
*/
|
|
164
185
|
filter: (predicate: FramePredicate<TPayload>) => EventDef<TPayload>;
|
|
165
186
|
/**
|
|
166
|
-
* Subscribe to this event (in-memory handler)
|
|
187
|
+
* Subscribe to this event (in-memory handler). With the cache transport a
|
|
188
|
+
* handler runs on every pod for every emit, unless `{ localOnly: true }`
|
|
189
|
+
* keeps it to emits made in this process.
|
|
167
190
|
*/
|
|
168
|
-
subscribe: (handler: EventHandler<TPayload
|
|
191
|
+
subscribe: (handler: EventHandler<TPayload>, options?: EventSubscribeOptions) => () => void;
|
|
169
192
|
/**
|
|
170
|
-
* Unsubscribe all handlers
|
|
193
|
+
* Unsubscribe all handlers, `localOnly` ones included
|
|
171
194
|
*/
|
|
172
195
|
unsubscribeAll: () => void;
|
|
173
196
|
/**
|
|
@@ -181,16 +204,22 @@ interface EventDef<TPayload = void> {
|
|
|
181
204
|
* Reaches every pod when the cache transport is attached. Never re-runs
|
|
182
205
|
* `canSubscribe`: it is an operator's statement about an open connection.
|
|
183
206
|
* An event the connection never requested cannot be granted.
|
|
207
|
+
*
|
|
208
|
+
* With `match`, only the connections whose connect query carries every
|
|
209
|
+
* field of `match.query` with that value are activated.
|
|
184
210
|
*/
|
|
185
|
-
grant: (subject: string) => Promise<void>;
|
|
211
|
+
grant: (subject: string, match?: SubscriptionMatch) => Promise<void>;
|
|
186
212
|
/**
|
|
187
213
|
* Deactivate this event on every open SSE connection of `subject` where it
|
|
188
214
|
* is allowed; the client receives a `subscription.revoked` frame.
|
|
189
215
|
*
|
|
190
216
|
* Reaches every pod when the cache transport is attached. Affects open
|
|
191
217
|
* connections only — the next connect asks `canSubscribe` afresh.
|
|
218
|
+
*
|
|
219
|
+
* With `match`, only the connections whose connect query carries every
|
|
220
|
+
* field of `match.query` with that value are deactivated.
|
|
192
221
|
*/
|
|
193
|
-
revoke: (subject: string) => Promise<void>;
|
|
222
|
+
revoke: (subject: string, match?: SubscriptionMatch) => Promise<void>;
|
|
194
223
|
/**
|
|
195
224
|
* Enable cache-based pub/sub for multi-instance support
|
|
196
225
|
* Must await before emitting events to ensure subscription is ready
|
|
@@ -346,4 +375,4 @@ type InferEventPayloads<T extends EventRouterBase<any>> = T['_types'];
|
|
|
346
375
|
*/
|
|
347
376
|
declare function defineEventRouter<TEvents extends Record<string, EventDef<any>>>(events: TEvents): EventRouterDef<TEvents>;
|
|
348
377
|
|
|
349
|
-
export { type EventRouterDef as E, type FrameContext as F, type InferEventNames as I, type JobQueueSender as J, type PubSubCache as P, type SubscribeContext as S, type EventDef as a, type EventContract as b, type EventHandler as c, type EventRouterContract as d, type
|
|
378
|
+
export { type EventRouterDef as E, type FrameContext as F, type InferEventNames as I, type JobQueueSender as J, type PubSubCache as P, type SubscribeContext as S, type EventDef as a, type EventContract as b, type EventHandler as c, type EventRouterContract as d, type EventSubscribeOptions as e, type FramePredicate as f, type InferEventPayload$1 as g, type InferEventPayloads as h, type InferEventPayload as i, type SubscribePredicate as j, type SubscribeRequest as k, type SubscriptionMatch as l, defineEventRouter as m, type EventRouterBase as n };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ import { NamedMiddleware, Router } from '@spfn/core/route';
|
|
|
6
6
|
import { OnErrorContext, ProxyGuardConfig, RateLimitOptions } from '@spfn/core/middleware';
|
|
7
7
|
import { SafeFetchPolicy } from '@spfn/core/security';
|
|
8
8
|
import { c as JobRouter, f as BossOptions } from '../boss-BiDCbbAQ.js';
|
|
9
|
-
import { E as EventRouterDef, a as EventDef } from '../router-
|
|
10
|
-
import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-
|
|
11
|
-
import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-
|
|
9
|
+
import { E as EventRouterDef, a as EventDef } from '../router-D5B0cpVN.js';
|
|
10
|
+
import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-04gu3uMj.js';
|
|
11
|
+
import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-CqTsDETV.js';
|
|
12
12
|
import { DatabaseProvider, MigrationStatus, MigrationStatusDb } from '@spfn/core/db';
|
|
13
13
|
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
14
14
|
import { Static } from '@sinclair/typebox';
|
package/dist/server/index.js
CHANGED
|
@@ -475,7 +475,7 @@ function createConnectionRegistry() {
|
|
|
475
475
|
const bySubject = /* @__PURE__ */ new Map();
|
|
476
476
|
const pendingBySubject = /* @__PURE__ */ new Map();
|
|
477
477
|
const beginAdmission = (subject, signal) => {
|
|
478
|
-
const changes =
|
|
478
|
+
const changes = [];
|
|
479
479
|
addToSet(pendingBySubject, subject, changes);
|
|
480
480
|
const end = endOnce(signal, () => removeFromSet(pendingBySubject, subject, changes));
|
|
481
481
|
return {
|
|
@@ -490,11 +490,15 @@ function createConnectionRegistry() {
|
|
|
490
490
|
}
|
|
491
491
|
};
|
|
492
492
|
};
|
|
493
|
-
const change = (kind, subject, eventName) => {
|
|
493
|
+
const change = (kind, subject, eventName, match) => {
|
|
494
494
|
const pending = pendingBySubject.get(subject);
|
|
495
495
|
const connections = bySubject.get(subject);
|
|
496
|
-
pending?.forEach((changes) => changes.
|
|
497
|
-
connections?.forEach((connection) =>
|
|
496
|
+
pending?.forEach((changes) => changes.push({ kind, eventName, match }));
|
|
497
|
+
connections?.forEach((connection) => {
|
|
498
|
+
if (matchesRequest(match, connection.request)) {
|
|
499
|
+
(kind === "grant" ? grantOn : revokeOn)(connection, eventName);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
498
502
|
if (!pending && !connections) {
|
|
499
503
|
registryLogger.debug("SSE subscription change for a subject with no open connection ignored", {
|
|
500
504
|
event: eventName,
|
|
@@ -512,8 +516,8 @@ function createConnectionRegistry() {
|
|
|
512
516
|
/** Open connections only — a pending admission is not counted. */
|
|
513
517
|
count: (subject) => bySubject.get(subject)?.size ?? 0,
|
|
514
518
|
pendingCount: (subject) => pendingBySubject.get(subject)?.size ?? 0,
|
|
515
|
-
grant: (subject, eventName) => change("grant", subject, eventName),
|
|
516
|
-
revoke: (subject, eventName) => change("revoke", subject, eventName)
|
|
519
|
+
grant: (subject, eventName, match) => change("grant", subject, eventName, match),
|
|
520
|
+
revoke: (subject, eventName, match) => change("revoke", subject, eventName, match)
|
|
517
521
|
};
|
|
518
522
|
}
|
|
519
523
|
function endOnce(signal, remove) {
|
|
@@ -545,8 +549,14 @@ function removeFromSet(map, key, value) {
|
|
|
545
549
|
map.delete(key);
|
|
546
550
|
}
|
|
547
551
|
}
|
|
552
|
+
function matchesRequest(match, request) {
|
|
553
|
+
return match === void 0 || Object.entries(match.query).every(([field, value]) => Object.hasOwn(request.query, field) && request.query[field] === value);
|
|
554
|
+
}
|
|
548
555
|
function applyRecorded(connection, changes) {
|
|
549
|
-
changes.forEach((kind, eventName) => {
|
|
556
|
+
changes.forEach(({ kind, eventName, match }) => {
|
|
557
|
+
if (!matchesRequest(match, connection.request)) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
550
560
|
const [from, to] = kind === "grant" ? [connection.denied, connection.allowed] : [connection.allowed, connection.denied];
|
|
551
561
|
if (from.delete(eventName)) {
|
|
552
562
|
to.add(eventName);
|
|
@@ -635,8 +645,8 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
635
645
|
function registerSubscriptionControls(router, registry) {
|
|
636
646
|
for (const eventName of router.eventNames) {
|
|
637
647
|
router.events[eventName]._registerSubscriptionControl({
|
|
638
|
-
grant: (subject) => registry.grant(subject, eventName),
|
|
639
|
-
revoke: (subject) => registry.revoke(subject, eventName)
|
|
648
|
+
grant: (subject, match) => registry.grant(subject, eventName, match),
|
|
649
|
+
revoke: (subject, match) => registry.revoke(subject, eventName, match)
|
|
640
650
|
});
|
|
641
651
|
}
|
|
642
652
|
}
|