cross-tab-worker-databus 0.7.0 → 0.10.0
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/CHANGELOG.md +28 -1
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/dist/centrifuge-protocol.d.ts +8 -0
- package/dist/centrifuge-protocol.d.ts.map +1 -1
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.d.ts +2 -1
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +31 -10
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +38 -4
- package/dist/centrifuge.shared.worker.js.map +3 -3
- package/dist/centrifuge.worker.js +38 -4
- package/dist/centrifuge.worker.js.map +3 -3
- package/dist/{chunk-SI7N5KNJ.js → chunk-ZOPNTR4E.js} +128 -22
- package/dist/chunk-ZOPNTR4E.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +157 -30
- package/dist/cjs/centrifuge.cjs.map +4 -4
- package/dist/cjs/index.cjs +166 -28
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/core/cluster.d.ts +4 -3
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +20 -2
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/publication.d.ts +10 -0
- package/dist/core/publication.d.ts.map +1 -0
- package/dist/core/replay-persistence.d.ts +2 -0
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/trace.d.ts +2 -11
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/types.d.ts +25 -6
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -8
- package/dist/index.js.map +2 -2
- package/dist/websocket.d.ts +2 -2
- package/dist/websocket.d.ts.map +1 -1
- package/docs/api.md +35 -4
- package/docs/architecture.md +3 -1
- package/docs/transports.md +9 -5
- package/docs/zh/api.md +35 -4
- package/docs/zh/architecture.md +3 -1
- package/docs/zh/transports.md +8 -4
- package/package.json +1 -1
- package/dist/chunk-SI7N5KNJ.js.map +0 -7
package/dist/core/cluster.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ClusterEnvironment } from './environment';
|
|
2
|
-
import type { WorkerControlAction, WorkerRecord, WorkerRoute, WorkerStatus } from './types';
|
|
2
|
+
import type { DataBusPublicationMetadata, WorkerControlAction, WorkerRecord, WorkerRoute, WorkerStatus } from './types';
|
|
3
3
|
/** Callbacks the cluster invokes to drive the transport and lifecycle. */
|
|
4
4
|
export interface WorkerClusterHandlers {
|
|
5
5
|
/** A SUBSCRIBE/UNSUBSCRIBE/PUBLISH control action was received for this worker. */
|
|
6
|
-
onControl: (action: WorkerControlAction, topic: string, data?: unknown) => void;
|
|
6
|
+
onControl: (action: WorkerControlAction, topic: string, data?: unknown, messageId?: string, timestamp?: number) => void;
|
|
7
7
|
/** A fan-out publication event was received from another Worker. */
|
|
8
8
|
onEvent: (eventType: string, payload: unknown, sourceWorkerId: string) => void;
|
|
9
9
|
/** The cluster suspended (tab hidden / pagehide). */
|
|
@@ -136,7 +136,8 @@ export declare class WorkerClusterRuntime {
|
|
|
136
136
|
* posted to a remote owner, so the caller can surface the failure instead of
|
|
137
137
|
* silently dropping the publication.
|
|
138
138
|
*/
|
|
139
|
-
publish(topic: string, data: unknown): boolean;
|
|
139
|
+
publish(topic: string, data: unknown, messageId?: string): boolean;
|
|
140
|
+
publish(topic: string, data: unknown, metadata?: DataBusPublicationMetadata): boolean;
|
|
140
141
|
/** True when `route` exists and its owner worker is among `workers`.
|
|
141
142
|
* Shared by subscribe (skip re-assignment) and publish (route to owner).
|
|
142
143
|
* Intentionally returns a plain boolean (not a type guard) so the caller
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/core/cluster.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,kBAAkB,EAAe,MAAM,eAAe,CAAC;AAQrF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/core/cluster.ts"],"names":[],"mappings":"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"}
|
package/dist/core/data-bus.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WorkerClusterOptions } from './cluster';
|
|
2
|
-
import type { DataBusErrorHandler, DataBusMessageHandler, DataBusStatusHandler, DataBusTransport, WorkerStatus } from './types';
|
|
2
|
+
import type { DataBusErrorHandler, DataBusMessageHandler, DataBusPublishOptions, DataBusStatusHandler, DataBusTransport, WorkerStatus } from './types';
|
|
3
3
|
import type { DataBusTraceOptions } from './trace';
|
|
4
4
|
import type { DataBusReplayPersistence } from './replay-persistence';
|
|
5
5
|
/** Constructor options for {@link CrossTabDataBus}. Extends WorkerClusterOptions
|
|
@@ -22,6 +22,12 @@ export interface DataBusDedupOptions {
|
|
|
22
22
|
maxEntries?: number;
|
|
23
23
|
ttlMs?: number;
|
|
24
24
|
}
|
|
25
|
+
export interface DataBusDedupStats {
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
tracked: number;
|
|
28
|
+
suppressed: number;
|
|
29
|
+
accepted: number;
|
|
30
|
+
}
|
|
25
31
|
export interface CrossTabDataBusOptions<TConfig, TData> extends Omit<WorkerClusterOptions, 'handlers'> {
|
|
26
32
|
transport: DataBusTransport<TConfig, TData>;
|
|
27
33
|
initialConfig?: TConfig;
|
|
@@ -58,6 +64,8 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
58
64
|
private readonly dedupTtlMs;
|
|
59
65
|
private readonly dedupEnabled;
|
|
60
66
|
private readonly seenMessageIds;
|
|
67
|
+
private dedupSuppressed;
|
|
68
|
+
private dedupAccepted;
|
|
61
69
|
private activeConfig;
|
|
62
70
|
private status;
|
|
63
71
|
private started;
|
|
@@ -104,8 +112,18 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
104
112
|
* caller used the `unsubscribe(topic)` form expecting a full teardown.
|
|
105
113
|
* The cluster is only notified on the n→0 transition (handlers.size === 0). */
|
|
106
114
|
unsubscribe(topic: string, handler?: DataBusMessageHandler<TData>): void;
|
|
115
|
+
/** Clear all in-memory replay buffers and, when supported, durable history. */
|
|
116
|
+
clearReplay(): Promise<void>;
|
|
117
|
+
/** Clear replay history for one exact topic, including durable storage. */
|
|
118
|
+
clearReplayTopic(topic: string): Promise<void>;
|
|
119
|
+
/** Remove replay entries older than an epoch-millisecond cutoff. */
|
|
120
|
+
clearReplayBefore(timestamp: number): Promise<void>;
|
|
121
|
+
/** Return bounded deduplication counters for diagnostics and health checks. */
|
|
122
|
+
getDedupStats(): DataBusDedupStats;
|
|
123
|
+
/** Drop all remembered IDs and reset dedup counters. */
|
|
124
|
+
resetDedup(): void;
|
|
107
125
|
/** Publish a message to `topic`. The owning Worker delivers it to the transport. */
|
|
108
|
-
publish(topic: string, data: unknown): void;
|
|
126
|
+
publish(topic: string, data: unknown, options?: DataBusPublishOptions): void;
|
|
109
127
|
/** Register a handler that fires on every transport status change. Immediately invoked with the current status. */
|
|
110
128
|
onStatus(handler: DataBusStatusHandler): () => void;
|
|
111
129
|
/** Register a handler for transport errors. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-bus.d.ts","sourceRoot":"","sources":["../../src/core/data-bus.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,KAAK,EACV,mBAAmB,EAEnB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAcrE;;wBAEwB;AACxB;;;;mBAImB;AACnB,MAAM,WAAW,oBAAoB,CAAC,KAAK,GAAG,OAAO;IACnD;6BACyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,WAAW,CAAC,EAAE,wBAAwB,CAAC,KAAK,CAAC,CAAC;CAC/C;AAED,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB,CAAC,OAAO,EAAE,KAAK,CACpD,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAC9C,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACrC,mFAAmF;IACnF,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwD;IAGtF,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqB;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAGhE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8C;IAC5E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyC;IAC3E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,SAAS,CAAiB;IAGlC,OAAO,CAAC,YAAY,CAA8B;IAGlD,OAAO,CAAC,cAAc,CAAK;IAG3B,OAAO,CAAC,SAAS,CAAS;IAK1B,OAAO,CAAC,WAAW,CAA8B;IAEjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;gBAExC,OAAO,EAAE,sBAAsB,CAAC,OAAO,EAAE,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"data-bus.d.ts","sourceRoot":"","sources":["../../src/core/data-bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DataBusPublication } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize legacy flat publications, metadata payload envelopes, and the
|
|
4
|
+
* canonical `{ op: 'publication', publication: ... }` shape.
|
|
5
|
+
*
|
|
6
|
+
* `fallbackTopic` is used by transports such as Centrifuge where the channel
|
|
7
|
+
* is supplied out-of-band by the client library rather than inside the data.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseDataBusPublication<TData = unknown>(value: unknown, fallbackTopic?: string): DataBusPublication<TData> | null;
|
|
10
|
+
//# sourceMappingURL=publication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publication.d.ts","sourceRoot":"","sources":["../../src/core/publication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,GAAG,OAAO,EACrD,KAAK,EAAE,OAAO,EACd,aAAa,CAAC,EAAE,MAAM,GACrB,kBAAkB,CAAC,KAAK,CAAC,GAAG,IAAI,CA0BlC"}
|
|
@@ -7,6 +7,8 @@ export interface DataBusReplayPersistence<TData = unknown> {
|
|
|
7
7
|
clear?(): Promise<void>;
|
|
8
8
|
/** Remove persisted replay history for one exact topic. */
|
|
9
9
|
clearTopic?(topic: string): Promise<void>;
|
|
10
|
+
/** Remove persisted messages older than the given epoch-millisecond cutoff. */
|
|
11
|
+
clearBefore?(timestamp: number): Promise<void>;
|
|
10
12
|
}
|
|
11
13
|
export interface IndexedDbReplayPersistenceOptions {
|
|
12
14
|
dbName?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay-persistence.d.ts","sourceRoot":"","sources":["../../src/core/replay-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,uDAAuD;AACvD,MAAM,WAAW,wBAAwB,CAAC,KAAK,GAAG,OAAO;IACvD,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,2CAA2C;IAC3C,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,2DAA2D;IAC3D,UAAU,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"replay-persistence.d.ts","sourceRoot":"","sources":["../../src/core/replay-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,uDAAuD;AACvD,MAAM,WAAW,wBAAwB,CAAC,KAAK,GAAG,OAAO;IACvD,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,2CAA2C;IAC3C,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,2DAA2D;IAC3D,UAAU,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,+EAA+E;IAC/E,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,iCAAiC;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,sDAAsD;AACtD,wBAAgB,gCAAgC,CAAC,KAAK,GAAG,OAAO,EAC9D,OAAO,EAAE,iCAAiC,GACzC,wBAAwB,CAAC,KAAK,CAAC,CAiFjC"}
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -51,16 +51,7 @@ export interface DataBusErrorTraceEvent {
|
|
|
51
51
|
/** Bounded reliability diagnostics for recovery, acknowledgments, and migrations. */
|
|
52
52
|
export interface DataBusReliabilityTraceEvent {
|
|
53
53
|
type: 'reliability';
|
|
54
|
-
operation: 'transport_recovery' | 'route_ack' | 'route_migration' | 'persistence_cleanup';
|
|
55
|
-
topic?: string;
|
|
56
|
-
attempt?: number;
|
|
57
|
-
durationMs?: number;
|
|
58
|
-
timestamp: number;
|
|
59
|
-
}
|
|
60
|
-
/** Emitted for bounded reliability diagnostics such as recovery attempts and route handoff. */
|
|
61
|
-
export interface DataBusReliabilityTraceEvent {
|
|
62
|
-
type: 'reliability';
|
|
63
|
-
operation: 'transport_recovery' | 'route_ack' | 'route_migration' | 'persistence_cleanup';
|
|
54
|
+
operation: 'transport_recovery' | 'route_ack' | 'route_migration' | 'persistence_cleanup' | 'dedup_suppressed';
|
|
64
55
|
topic?: string;
|
|
65
56
|
attempt?: number;
|
|
66
57
|
durationMs?: number;
|
|
@@ -84,7 +75,7 @@ export interface DataBusMetricsTraceEvent {
|
|
|
84
75
|
dispatchMaxMs: number;
|
|
85
76
|
timestamp: number;
|
|
86
77
|
}
|
|
87
|
-
export type DataBusTraceEvent = DataBusLifecycleTraceEvent | DataBusStatusTraceEvent | DataBusSubscriptionTraceEvent | DataBusCoordinationTraceEvent | DataBusErrorTraceEvent | DataBusReliabilityTraceEvent |
|
|
78
|
+
export type DataBusTraceEvent = DataBusLifecycleTraceEvent | DataBusStatusTraceEvent | DataBusSubscriptionTraceEvent | DataBusCoordinationTraceEvent | DataBusErrorTraceEvent | DataBusReliabilityTraceEvent | DataBusMetricsTraceEvent;
|
|
88
79
|
/** Distributive-conditional type: given a trace event union, derive the same shape minus `timestamp`. */
|
|
89
80
|
type DataBusTraceEventInput = DataBusTraceEvent extends infer TEvent ? TEvent extends DataBusTraceEvent ? Omit<TEvent, 'timestamp'> : never : never;
|
|
90
81
|
/** Configuration for {@link DataBusTraceReporter}. `sink` receives every
|
package/dist/core/trace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,uEAAuE;AACvE;;;yDAGyD;AACzD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE5D,oEAAoE;AACpE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IAChD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6DAA6D;AAC7D,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,WAAW,GAAG,aAAa,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,sFAAsF;AACtF,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0DAA0D;AAC1D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,WAAW,GAAG,WAAW,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,oBAAoB,GAAG,WAAW,GAAG,iBAAiB,GAAG,qBAAqB,
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"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"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -19,6 +19,25 @@ export type WorkerStatus = 'connecting' | 'connected' | 'disconnected' | 'error'
|
|
|
19
19
|
/** Control-plane actions routed between Workers via BroadcastChannel.
|
|
20
20
|
* These are the three operations a subscriber asks the owning worker to perform. */
|
|
21
21
|
export type WorkerControlAction = 'SUBSCRIBE' | 'UNSUBSCRIBE' | 'PUBLISH';
|
|
22
|
+
/** Transport-neutral metadata attached to a publication. */
|
|
23
|
+
export interface DataBusPublicationMetadata {
|
|
24
|
+
/** Stable caller/server identifier propagated through the cluster and wire protocol. */
|
|
25
|
+
messageId?: string;
|
|
26
|
+
/** Producer-assigned Unix timestamp in milliseconds, used by retention policies. */
|
|
27
|
+
timestamp?: number;
|
|
28
|
+
}
|
|
29
|
+
/** Optional metadata attached to an outbound publication. */
|
|
30
|
+
export type DataBusPublishOptions = DataBusPublicationMetadata;
|
|
31
|
+
/** Canonical transport-neutral publication shape. */
|
|
32
|
+
export interface DataBusPublication<TData = unknown> extends DataBusPublicationMetadata {
|
|
33
|
+
topic: string;
|
|
34
|
+
data: TData;
|
|
35
|
+
}
|
|
36
|
+
/** Forward-compatible JSON envelope accepted from WebSocket-style servers. */
|
|
37
|
+
export interface DataBusPublicationEnvelope<TData = unknown> {
|
|
38
|
+
op: 'publication';
|
|
39
|
+
publication: DataBusPublication<TData>;
|
|
40
|
+
}
|
|
22
41
|
/** Whether the tab is currently visible to the user. Only influences placement
|
|
23
42
|
* of NEW topic routes; existing routes are sticky and never migrated on hide. */
|
|
24
43
|
export type TabVisibilityState = 'visible' | 'hidden';
|
|
@@ -94,6 +113,10 @@ export type WorkerClusterMessage<TEvent = unknown> =
|
|
|
94
113
|
topicKey: string;
|
|
95
114
|
/** Present only for PUBLISH actions; the publication payload. */
|
|
96
115
|
data?: unknown;
|
|
116
|
+
/** Optional stable identifier for the publication. */
|
|
117
|
+
messageId?: string;
|
|
118
|
+
/** Optional producer timestamp propagated with PUBLISH actions. */
|
|
119
|
+
timestamp?: number;
|
|
97
120
|
}
|
|
98
121
|
/** The owning worker fans out a publication to every tab. `eventType`
|
|
99
122
|
* distinguishes databus publications from future event types. */
|
|
@@ -121,11 +144,7 @@ export type WorkerClusterMessage<TEvent = unknown> =
|
|
|
121
144
|
};
|
|
122
145
|
/** A publication delivered to a topic handler: the topic name and the
|
|
123
146
|
* structured-clone-safe payload. */
|
|
124
|
-
export interface DataBusMessage<TData = unknown> {
|
|
125
|
-
topic: string;
|
|
126
|
-
data: TData;
|
|
127
|
-
/** Optional caller/server supplied stable identifier used by opt-in dedup. */
|
|
128
|
-
messageId?: string;
|
|
147
|
+
export interface DataBusMessage<TData = unknown> extends DataBusPublication<TData> {
|
|
129
148
|
/** True when this delivery is a historical replay (see the `replay`
|
|
130
149
|
* subscription option) rather than a live publication. */
|
|
131
150
|
replayed?: boolean;
|
|
@@ -159,7 +178,7 @@ export interface DataBusTransport<TConfig = unknown, TData = unknown> {
|
|
|
159
178
|
/** Unsubscribe from a topic. Idempotent: unsubscribing a non-subscribed topic is a no-op. */
|
|
160
179
|
unsubscribe(topic: string): MaybePromise<void>;
|
|
161
180
|
/** Publish `data` to `topic` via the server. */
|
|
162
|
-
publish(topic: string, data: unknown): MaybePromise<void>;
|
|
181
|
+
publish(topic: string, data: unknown, options?: DataBusPublishOptions): MaybePromise<void>;
|
|
163
182
|
/** Close the connection and release all resources. Safe to call multiple times. */
|
|
164
183
|
stop(): MaybePromise<void>;
|
|
165
184
|
}
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;+DAE+D;AAC/D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C;;iFAEiF;AACjF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9C;0EAC0E;AAC1E,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAC;AAEjF;oFACoF;AACpF,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;AAE1E;iFACiF;AACjF,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,eAAe,EAAE,kBAAkB,CAAC;IACpC,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB;kEAC8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB;uDACmD;IACnD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;iFAC6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;8DAE8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,CAAC,MAAM,GAAG,OAAO;AAC/C;;sCAEsC;AACpC;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* ```
|
|
10
10
|
*/
|
|
11
11
|
export { CrossTabDataBus } from './core/data-bus';
|
|
12
|
-
export type { CrossTabDataBusOptions, DataBusDedupOptions, DataBusReplayOptions } from './core/data-bus';
|
|
12
|
+
export type { CrossTabDataBusOptions, DataBusDedupOptions, DataBusDedupStats, DataBusReplayOptions } from './core/data-bus';
|
|
13
13
|
export { createIndexedDbReplayPersistence } from './core/replay-persistence';
|
|
14
14
|
export type { DataBusReplayPersistence, IndexedDbReplayPersistenceOptions } from './core/replay-persistence';
|
|
15
15
|
export type { DataBusCoordinationTraceEvent, DataBusErrorTraceEvent, DataBusLifecycleTraceEvent, DataBusMetricsTraceEvent, DataBusReliabilityTraceEvent, DataBusStatusTraceEvent, DataBusSubscriptionTraceEvent, DataBusTraceEvent, DataBusTraceMode, DataBusTraceOptions } from './core/trace';
|
|
@@ -23,5 +23,5 @@ export type { CreateWebSocketDataBusOptions, WebSocketDataBusConfig, WebSocketLi
|
|
|
23
23
|
export { selectWorkerBackend } from './worker-mode';
|
|
24
24
|
export type { WorkerBackend, WorkerMode } from './worker-mode';
|
|
25
25
|
export { DEFAULT_MAX_ACTIVE_WORKERS, hasActiveOwner, isWildcardTopic, selectActiveWorkers, selectLeastLoadedWorker, selectRebalanceTarget, topicMatchesPattern } from './core/routing';
|
|
26
|
-
export type { DataBusErrorHandler, DataBusMessage, DataBusMessageHandler, DataBusStatusHandler, DataBusTransport, DataBusTransportHandlers, MaybePromise, TabVisibilityState, TopicSubscriberRecord, WorkerClusterMessage, WorkerControlAction, WorkerRecord, WorkerRole, WorkerRoute, WorkerStatus } from './core/types';
|
|
26
|
+
export type { DataBusErrorHandler, DataBusMessage, DataBusPublication, DataBusPublicationEnvelope, DataBusPublicationMetadata, DataBusPublishOptions, DataBusMessageHandler, DataBusStatusHandler, DataBusTransport, DataBusTransportHandlers, MaybePromise, TabVisibilityState, TopicSubscriberRecord, WorkerClusterMessage, WorkerControlAction, WorkerRecord, WorkerRole, WorkerRoute, WorkerStatus } from './core/types';
|
|
27
27
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5H,OAAO,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC;AAC7E,YAAY,EAAE,wBAAwB,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAC7G,YAAY,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,EACvB,6BAA6B,EAC7B,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,aAAa,EACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,wBAAwB,EACxB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,12 +7,13 @@ import {
|
|
|
7
7
|
getOrCreateTabId,
|
|
8
8
|
hasActiveOwner,
|
|
9
9
|
isWildcardTopic,
|
|
10
|
+
parseDataBusPublication,
|
|
10
11
|
selectActiveWorkers,
|
|
11
12
|
selectLeastLoadedWorker,
|
|
12
13
|
selectRebalanceTarget,
|
|
13
14
|
selectWorkerBackend,
|
|
14
15
|
topicMatchesPattern
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZOPNTR4E.js";
|
|
16
17
|
|
|
17
18
|
// src/core/replay-persistence.ts
|
|
18
19
|
function createIndexedDbReplayPersistence(options) {
|
|
@@ -76,6 +77,24 @@ function createIndexedDbReplayPersistence(options) {
|
|
|
76
77
|
transaction.oncomplete = () => resolve();
|
|
77
78
|
transaction.onerror = () => reject(transaction.error ?? new Error("Failed to clear topic replay history."));
|
|
78
79
|
});
|
|
80
|
+
},
|
|
81
|
+
async clearBefore(timestamp) {
|
|
82
|
+
const db = await open();
|
|
83
|
+
await new Promise((resolve, reject) => {
|
|
84
|
+
const transaction = db.transaction(storeName, "readwrite");
|
|
85
|
+
const store = transaction.objectStore(storeName);
|
|
86
|
+
const request = store.getAll();
|
|
87
|
+
request.onsuccess = () => {
|
|
88
|
+
for (const record of request.result) {
|
|
89
|
+
const messages = record.messages.filter((message) => (message.timestamp ?? 0) >= timestamp);
|
|
90
|
+
if (messages.length === 0) store.delete(record.topic);
|
|
91
|
+
else if (messages.length !== record.messages.length) store.put({ topic: record.topic, messages });
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
request.onerror = () => reject(request.error ?? new Error("Failed to read replay history."));
|
|
95
|
+
transaction.oncomplete = () => resolve();
|
|
96
|
+
transaction.onerror = () => reject(transaction.error ?? new Error("Failed to prune replay history."));
|
|
97
|
+
});
|
|
79
98
|
}
|
|
80
99
|
};
|
|
81
100
|
}
|
|
@@ -127,12 +146,18 @@ var WebSocketTransport = class {
|
|
|
127
146
|
this.sendFrame({ op: "unsubscribe", topic });
|
|
128
147
|
}
|
|
129
148
|
/** Publish `data` to `topic` as a JSON frame. Requires an open socket. */
|
|
130
|
-
publish(topic, data) {
|
|
149
|
+
publish(topic, data, options) {
|
|
131
150
|
if (data instanceof ArrayBuffer) {
|
|
132
|
-
this.sendBinaryFrame(topic, data);
|
|
151
|
+
this.sendBinaryFrame(topic, data, options?.messageId, options?.timestamp);
|
|
133
152
|
return;
|
|
134
153
|
}
|
|
135
|
-
this.sendFrame({
|
|
154
|
+
this.sendFrame({
|
|
155
|
+
op: "publish",
|
|
156
|
+
topic,
|
|
157
|
+
data,
|
|
158
|
+
...options?.messageId === void 0 ? {} : { messageId: options.messageId },
|
|
159
|
+
...options?.timestamp === void 0 ? {} : { timestamp: options.timestamp }
|
|
160
|
+
});
|
|
136
161
|
}
|
|
137
162
|
/** Close the socket and drop all state. Safe to call multiple times. */
|
|
138
163
|
stop() {
|
|
@@ -152,7 +177,17 @@ var WebSocketTransport = class {
|
|
|
152
177
|
}
|
|
153
178
|
this.socket.send(JSON.stringify(payload));
|
|
154
179
|
}
|
|
155
|
-
sendBinaryFrame(topic, data) {
|
|
180
|
+
sendBinaryFrame(topic, data, messageId, timestamp) {
|
|
181
|
+
if (messageId !== void 0 || timestamp !== void 0) {
|
|
182
|
+
this.sendFrame({
|
|
183
|
+
op: "publish",
|
|
184
|
+
topic,
|
|
185
|
+
data: Array.from(new Uint8Array(data)),
|
|
186
|
+
...messageId === void 0 ? {} : { messageId },
|
|
187
|
+
...timestamp === void 0 ? {} : { timestamp }
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
156
191
|
if (this.socket?.readyState !== WS_OPEN) {
|
|
157
192
|
this.handlers?.onError(new Error('WebSocket is not open; dropped "publish" frame.'));
|
|
158
193
|
return;
|
|
@@ -192,9 +227,8 @@ var WebSocketTransport = class {
|
|
|
192
227
|
return;
|
|
193
228
|
}
|
|
194
229
|
if (!parsed || typeof parsed !== "object") return;
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
197
|
-
this.handlers?.onMessage({ topic: frame.topic, data: frame.data });
|
|
230
|
+
const publication = parseDataBusPublication(parsed);
|
|
231
|
+
if (publication) this.handlers?.onMessage(publication);
|
|
198
232
|
}
|
|
199
233
|
};
|
|
200
234
|
function defaultWebSocketFactory(url, protocols) {
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/core/replay-persistence.ts", "../src/websocket.ts"],
|
|
4
|
-
"sourcesContent": ["import type { DataBusMessage } from './types';\n\n/** Optional persistence backend for replay history. */\nexport interface DataBusReplayPersistence<TData = unknown> {\n load(): Promise<ReadonlyArray<DataBusMessage<TData>>>;\n append(message: DataBusMessage<TData>): Promise<void>;\n /** Remove all persisted replay history. */\n clear?(): Promise<void>;\n /** Remove persisted replay history for one exact topic. */\n clearTopic?(topic: string): Promise<void>;\n}\n\nexport interface IndexedDbReplayPersistenceOptions {\n dbName?: string;\n maxPerTopic: number;\n}\n\n/** Create a browser IndexedDB-backed replay store. */\nexport function createIndexedDbReplayPersistence<TData = unknown>(\n options: IndexedDbReplayPersistenceOptions\n): DataBusReplayPersistence<TData> {\n const indexedDb = globalThis.indexedDB;\n if (!indexedDb) throw new Error('IndexedDB is unavailable in this environment.');\n const dbName = options.dbName ?? 'cross-tab-worker-databus';\n const storeName = 'replay';\n const maxPerTopic = options.maxPerTopic;\n if (!Number.isSafeInteger(maxPerTopic) || maxPerTopic <= 0) {\n throw new TypeError(`maxPerTopic must be a positive safe integer, got ${String(maxPerTopic)}.`);\n }\n let dbPromise: Promise<IDBDatabase> | null = null;\n const open = (): Promise<IDBDatabase> => {\n if (dbPromise) return dbPromise;\n dbPromise = new Promise((resolve, reject) => {\n const request = indexedDb.open(dbName, 1);\n request.onupgradeneeded = () => request.result.createObjectStore(storeName, { keyPath: 'topic' });\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('Failed to open replay database.'));\n });\n return dbPromise;\n };\n return {\n async load() {\n const db = await open();\n return new Promise((resolve, reject) => {\n const request = db.transaction(storeName, 'readonly').objectStore(storeName).getAll();\n request.onsuccess = () => resolve(request.result.flatMap(record => record.messages as DataBusMessage<TData>[]));\n request.onerror = () => reject(request.error ?? new Error('Failed to load replay history.'));\n });\n },\n async append(message) {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n const request = store.get(message.topic);\n request.onsuccess = () => {\n const messages = ((request.result?.messages ?? []) as DataBusMessage<TData>[]).concat(message).slice(-maxPerTopic);\n store.put({ topic: message.topic, messages });\n };\n request.onerror = () => reject(request.error ?? new Error('Failed to read replay history.'));\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to persist replay history.'));\n });\n },\n async clear() {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n transaction.objectStore(storeName).clear();\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to clear replay history.'));\n });\n },\n async clearTopic(topic) {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n transaction.objectStore(storeName).delete(topic);\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to clear topic replay history.'));\n });\n }\n };\n}\n", "/**\n * WebSocketTransport \u2014 a dependency-free transport over a plain WebSocket.\n *\n * Validates the `DataBusTransport` abstraction with a second, minimal backend:\n * any WebSocket server that speaks the tiny JSON protocol below can back the\n * same cross-tab clustering stack (owner dedup, sticky routes, EVENT fan-out)\n * that the Centrifuge backend uses.\n *\n * Wire protocol (JSON text frames):\n * - client \u2192 server: `{\"op\":\"subscribe\"|\"unsubscribe\"|\"publish\",\"topic\":...,\"data\":...}`\n * - server \u2192 client: `{\"topic\":...,\"data\":...}` for publications; anything\n * without a string `topic` field is ignored (forward-compatible).\n */\nimport { CrossTabDataBus } from './core/data-bus';\nimport type { CrossTabDataBusOptions } from './core/data-bus';\nimport type {\n DataBusTransport,\n DataBusTransportHandlers,\n MaybePromise,\n WorkerStatus\n} from './core/types';\n\n/** Minimal WebSocket surface used by the transport. Matches the browser\n * `WebSocket` subset the transport touches; injectable for tests and runtimes. */\nexport interface WebSocketLike {\n /** Current connection state; 1 (OPEN) means frames may be sent. */\n readonly readyState?: number;\n send(data: string | ArrayBuffer): void;\n close(code?: number, reason?: string): void;\n onopen: (() => void) | null;\n onclose: (() => void) | null;\n onerror: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/** Connection configuration for {@link WebSocketTransport}. */\nexport interface WebSocketDataBusConfig {\n /** WebSocket endpoint, e.g. `wss://example.test/ws`. */\n url: string;\n /** Subprotocol(s) passed to the WebSocket handshake. */\n protocols?: string | string[];\n /** Custom socket factory. Defaults to the global `WebSocket`; injectable\n * for tests and non-browser runtimes. */\n webSocketFactory?: (url: string, protocols?: string | string[]) => WebSocketLike;\n}\n\n/** Options for creating a fully-configured CrossTabDataBus with a WebSocket transport. */\nexport interface CreateWebSocketDataBusOptions<TData = unknown>\n extends Omit<\n CrossTabDataBusOptions<WebSocketDataBusConfig, TData>,\n 'autoStart' | 'clusterKey' | 'initialConfig' | 'transport'\n > {\n /** WebSocket connection configuration. */\n connection: WebSocketDataBusConfig;\n /** Cluster key for cross-tab coordination. Defaults to the connection URL. */\n clusterKey?: string;\n}\n\nconst WS_OPEN = 1;\n\n/** Transport that talks a minimal JSON protocol over a plain WebSocket.\n * Connection lifecycle maps directly to the DataBus status vocabulary:\n * open \u2192 `connected`, close \u2192 `disconnected`, error \u2192 `error` (which the\n * DataBus treats as its auto-recovery trigger). The transport holds no\n * reconnection logic of its own \u2014 reopening is the DataBus's job. */\nexport class WebSocketTransport<TData = unknown>\n implements DataBusTransport<WebSocketDataBusConfig, TData>\n{\n private socket: WebSocketLike | null = null;\n private handlers: DataBusTransportHandlers<TData> | null = null;\n private readonly subscribedTopics = new Set<string>();\n\n constructor(private readonly connection: WebSocketDataBusConfig) {}\n\n /** Open the WebSocket and wire lifecycle listeners. A factory failure is\n * reported through `onStatus('error')` so the DataBus can recover. */\n start(config: WebSocketDataBusConfig, handlers: DataBusTransportHandlers<TData>): MaybePromise<void> {\n if (this.socket) return;\n this.handlers = handlers;\n // The factory may live on the constructor connection (createWebSocketDataBus\n // path) or on the runtime config (direct transport use) \u2014 accept both.\n const factory = config.webSocketFactory ?? this.connection.webSocketFactory ?? defaultWebSocketFactory;\n const protocols = config.protocols ?? this.connection.protocols;\n let socket: WebSocketLike;\n try {\n socket = factory(config.url, protocols);\n } catch (error) {\n handlers.onStatus('error');\n handlers.onError(error);\n return;\n }\n socket.onopen = () => {\n // Re-assert every topic so a reopened socket (recovery path) restores\n // the server-side subscriptions without DataBus involvement.\n for (const topic of this.subscribedTopics) {\n this.sendFrame({ op: 'subscribe', topic });\n }\n handlers.onStatus('connected');\n };\n socket.onclose = () => handlers.onStatus('disconnected');\n socket.onerror = () => handlers.onStatus('error');\n socket.onmessage = event => this.handleMessage(event.data);\n this.socket = socket;\n }\n\n /** Idempotent: re-subscribing an active topic re-sends the frame but does\n * not duplicate the local tracking entry. */\n subscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.add(topic);\n this.sendFrame({ op: 'subscribe', topic });\n }\n\n /** Idempotent: unsubscribing an unknown topic is a no-op. */\n unsubscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.delete(topic);\n this.sendFrame({ op: 'unsubscribe', topic });\n }\n\n /** Publish `data` to `topic` as a JSON frame. Requires an open socket. */\n publish(topic: string, data: unknown): MaybePromise<void> {\n if (data instanceof ArrayBuffer) {\n this.sendBinaryFrame(topic, data);\n return;\n }\n this.sendFrame({ op: 'publish', topic, data });\n }\n\n /** Close the socket and drop all state. Safe to call multiple times. */\n stop(): MaybePromise<void> {\n const socket = this.socket;\n this.socket = null;\n this.handlers = null;\n this.subscribedTopics.clear();\n socket?.close();\n }\n\n /** Send one JSON frame. Frames are dropped with an `onError` report when\n * the socket is not open \u2014 subscribe frames are re-sent on open, so the\n * only real loss is a publish during a disconnect window. */\n private sendFrame(payload: { op: string; topic: string; data?: unknown }): void {\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error(`WebSocket is not open; dropped \"${payload.op}\" frame.`));\n return;\n }\n this.socket.send(JSON.stringify(payload));\n }\n\n private sendBinaryFrame(topic: string, data: ArrayBuffer): void {\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error('WebSocket is not open; dropped \"publish\" frame.'));\n return;\n }\n const topicBytes = new TextEncoder().encode(topic);\n if (topicBytes.length > 0xffff) {\n this.handlers?.onError(new Error('WebSocket topic is too long for a binary frame.'));\n return;\n }\n const frame = new Uint8Array(3 + topicBytes.length + data.byteLength);\n frame[0] = 0xc7;\n new DataView(frame.buffer).setUint16(1, topicBytes.length);\n frame.set(topicBytes, 3);\n frame.set(new Uint8Array(data), 3 + topicBytes.length);\n this.socket.send(frame.buffer);\n }\n\n /** Parse a server frame. Only objects carrying a string `topic` are\n * publications; malformed JSON and unknown shapes are ignored so a chatty\n * server cannot crash the message path. */\n private handleMessage(raw: unknown): void {\n let parsed: unknown;\n if (raw instanceof ArrayBuffer) {\n const bytes = new Uint8Array(raw);\n if (bytes[0] !== 0xc7 || bytes.length < 3) return;\n const topicLength = new DataView(raw).getUint16(1);\n if (bytes.length < 3 + topicLength) return;\n const topic = new TextDecoder().decode(bytes.subarray(3, 3 + topicLength));\n const data = bytes.slice(3 + topicLength).buffer;\n this.handlers?.onMessage({ topic, data: data as TData });\n return;\n }\n if (typeof raw !== 'string') return;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.handlers?.onError(new Error('WebSocket server sent a non-JSON frame.'));\n return;\n }\n if (!parsed || typeof parsed !== 'object') return;\n const frame = parsed as Record<string, unknown>;\n if (typeof frame.topic !== 'string') return;\n this.handlers?.onMessage({ topic: frame.topic, data: frame.data as TData });\n }\n}\n\n/** Resolve the platform WebSocket, or null in runtimes without one (SSR/Node). */\nfunction defaultWebSocketFactory(url: string, protocols?: string | string[]): WebSocketLike {\n if (typeof WebSocket === 'undefined') {\n throw new Error('WebSocketTransport requires a WebSocket implementation.');\n }\n return new WebSocket(url, protocols) as unknown as WebSocketLike;\n}\n\n/** Create a CrossTabDataBus backed by a plain WebSocket transport.\n * Cross-tab clustering (owner dedup, sticky routes, failover) works identically\n * to the Centrifuge backend \u2014 only the transport I/O differs. */\nexport function createWebSocketDataBus<TData = unknown>(\n options: CreateWebSocketDataBusOptions<TData>\n): CrossTabDataBus<WebSocketDataBusConfig, TData> {\n const { clusterKey, connection, ...dataBusOptions } = options;\n return new CrossTabDataBus({\n ...dataBusOptions,\n autoStart: true,\n clusterKey: clusterKey ?? connection.url,\n initialConfig: connection,\n transport: new WebSocketTransport<TData>(connection)\n });\n}\n\n/** Re-export for convenience: the status type used by the transport. */\nexport type { WorkerStatus };\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { DataBusMessage } from './types';\n\n/** Optional persistence backend for replay history. */\nexport interface DataBusReplayPersistence<TData = unknown> {\n load(): Promise<ReadonlyArray<DataBusMessage<TData>>>;\n append(message: DataBusMessage<TData>): Promise<void>;\n /** Remove all persisted replay history. */\n clear?(): Promise<void>;\n /** Remove persisted replay history for one exact topic. */\n clearTopic?(topic: string): Promise<void>;\n /** Remove persisted messages older than the given epoch-millisecond cutoff. */\n clearBefore?(timestamp: number): Promise<void>;\n}\n\nexport interface IndexedDbReplayPersistenceOptions {\n dbName?: string;\n maxPerTopic: number;\n}\n\n/** Create a browser IndexedDB-backed replay store. */\nexport function createIndexedDbReplayPersistence<TData = unknown>(\n options: IndexedDbReplayPersistenceOptions\n): DataBusReplayPersistence<TData> {\n const indexedDb = globalThis.indexedDB;\n if (!indexedDb) throw new Error('IndexedDB is unavailable in this environment.');\n const dbName = options.dbName ?? 'cross-tab-worker-databus';\n const storeName = 'replay';\n const maxPerTopic = options.maxPerTopic;\n if (!Number.isSafeInteger(maxPerTopic) || maxPerTopic <= 0) {\n throw new TypeError(`maxPerTopic must be a positive safe integer, got ${String(maxPerTopic)}.`);\n }\n let dbPromise: Promise<IDBDatabase> | null = null;\n const open = (): Promise<IDBDatabase> => {\n if (dbPromise) return dbPromise;\n dbPromise = new Promise((resolve, reject) => {\n const request = indexedDb.open(dbName, 1);\n request.onupgradeneeded = () => request.result.createObjectStore(storeName, { keyPath: 'topic' });\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('Failed to open replay database.'));\n });\n return dbPromise;\n };\n return {\n async load() {\n const db = await open();\n return new Promise((resolve, reject) => {\n const request = db.transaction(storeName, 'readonly').objectStore(storeName).getAll();\n request.onsuccess = () => resolve(request.result.flatMap(record => record.messages as DataBusMessage<TData>[]));\n request.onerror = () => reject(request.error ?? new Error('Failed to load replay history.'));\n });\n },\n async append(message) {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n const request = store.get(message.topic);\n request.onsuccess = () => {\n const messages = ((request.result?.messages ?? []) as DataBusMessage<TData>[]).concat(message).slice(-maxPerTopic);\n store.put({ topic: message.topic, messages });\n };\n request.onerror = () => reject(request.error ?? new Error('Failed to read replay history.'));\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to persist replay history.'));\n });\n },\n async clear() {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n transaction.objectStore(storeName).clear();\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to clear replay history.'));\n });\n },\n async clearTopic(topic) {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n transaction.objectStore(storeName).delete(topic);\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to clear topic replay history.'));\n });\n },\n async clearBefore(timestamp) {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n const transaction = db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n const request = store.getAll();\n request.onsuccess = () => {\n for (const record of request.result as Array<{ topic: string; messages: DataBusMessage<TData>[] }>) {\n const messages = record.messages.filter(message => (message.timestamp ?? 0) >= timestamp);\n if (messages.length === 0) store.delete(record.topic);\n else if (messages.length !== record.messages.length) store.put({ topic: record.topic, messages });\n }\n };\n request.onerror = () => reject(request.error ?? new Error('Failed to read replay history.'));\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error ?? new Error('Failed to prune replay history.'));\n });\n }\n };\n}\n", "/**\n * WebSocketTransport \u2014 a dependency-free transport over a plain WebSocket.\n *\n * Validates the `DataBusTransport` abstraction with a second, minimal backend:\n * any WebSocket server that speaks the tiny JSON protocol below can back the\n * same cross-tab clustering stack (owner dedup, sticky routes, EVENT fan-out)\n * that the Centrifuge backend uses.\n *\n * Wire protocol (JSON text frames):\n * - client \u2192 server: `{\"op\":\"subscribe\"|\"unsubscribe\"|\"publish\",\"topic\":...,\"data\":...}`\n * - server \u2192 client: `{\"topic\":...,\"data\":...}` for publications; anything\n * without a string `topic` field is ignored (forward-compatible).\n */\nimport { CrossTabDataBus } from './core/data-bus';\nimport { parseDataBusPublication } from './core/publication';\nimport type { CrossTabDataBusOptions } from './core/data-bus';\nimport type {\n DataBusTransport,\n DataBusTransportHandlers,\n DataBusPublishOptions,\n DataBusMessage,\n MaybePromise,\n WorkerStatus\n} from './core/types';\n\n/** Minimal WebSocket surface used by the transport. Matches the browser\n * `WebSocket` subset the transport touches; injectable for tests and runtimes. */\nexport interface WebSocketLike {\n /** Current connection state; 1 (OPEN) means frames may be sent. */\n readonly readyState?: number;\n send(data: string | ArrayBuffer): void;\n close(code?: number, reason?: string): void;\n onopen: (() => void) | null;\n onclose: (() => void) | null;\n onerror: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/** Connection configuration for {@link WebSocketTransport}. */\nexport interface WebSocketDataBusConfig {\n /** WebSocket endpoint, e.g. `wss://example.test/ws`. */\n url: string;\n /** Subprotocol(s) passed to the WebSocket handshake. */\n protocols?: string | string[];\n /** Custom socket factory. Defaults to the global `WebSocket`; injectable\n * for tests and non-browser runtimes. */\n webSocketFactory?: (url: string, protocols?: string | string[]) => WebSocketLike;\n}\n\n/** Options for creating a fully-configured CrossTabDataBus with a WebSocket transport. */\nexport interface CreateWebSocketDataBusOptions<TData = unknown>\n extends Omit<\n CrossTabDataBusOptions<WebSocketDataBusConfig, TData>,\n 'autoStart' | 'clusterKey' | 'initialConfig' | 'transport'\n > {\n /** WebSocket connection configuration. */\n connection: WebSocketDataBusConfig;\n /** Cluster key for cross-tab coordination. Defaults to the connection URL. */\n clusterKey?: string;\n}\n\nconst WS_OPEN = 1;\n\n/** Transport that talks a minimal JSON protocol over a plain WebSocket.\n * Connection lifecycle maps directly to the DataBus status vocabulary:\n * open \u2192 `connected`, close \u2192 `disconnected`, error \u2192 `error` (which the\n * DataBus treats as its auto-recovery trigger). The transport holds no\n * reconnection logic of its own \u2014 reopening is the DataBus's job. */\nexport class WebSocketTransport<TData = unknown>\n implements DataBusTransport<WebSocketDataBusConfig, TData>\n{\n private socket: WebSocketLike | null = null;\n private handlers: DataBusTransportHandlers<TData> | null = null;\n private readonly subscribedTopics = new Set<string>();\n\n constructor(private readonly connection: WebSocketDataBusConfig) {}\n\n /** Open the WebSocket and wire lifecycle listeners. A factory failure is\n * reported through `onStatus('error')` so the DataBus can recover. */\n start(config: WebSocketDataBusConfig, handlers: DataBusTransportHandlers<TData>): MaybePromise<void> {\n if (this.socket) return;\n this.handlers = handlers;\n // The factory may live on the constructor connection (createWebSocketDataBus\n // path) or on the runtime config (direct transport use) \u2014 accept both.\n const factory = config.webSocketFactory ?? this.connection.webSocketFactory ?? defaultWebSocketFactory;\n const protocols = config.protocols ?? this.connection.protocols;\n let socket: WebSocketLike;\n try {\n socket = factory(config.url, protocols);\n } catch (error) {\n handlers.onStatus('error');\n handlers.onError(error);\n return;\n }\n socket.onopen = () => {\n // Re-assert every topic so a reopened socket (recovery path) restores\n // the server-side subscriptions without DataBus involvement.\n for (const topic of this.subscribedTopics) {\n this.sendFrame({ op: 'subscribe', topic });\n }\n handlers.onStatus('connected');\n };\n socket.onclose = () => handlers.onStatus('disconnected');\n socket.onerror = () => handlers.onStatus('error');\n socket.onmessage = event => this.handleMessage(event.data);\n this.socket = socket;\n }\n\n /** Idempotent: re-subscribing an active topic re-sends the frame but does\n * not duplicate the local tracking entry. */\n subscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.add(topic);\n this.sendFrame({ op: 'subscribe', topic });\n }\n\n /** Idempotent: unsubscribing an unknown topic is a no-op. */\n unsubscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.delete(topic);\n this.sendFrame({ op: 'unsubscribe', topic });\n }\n\n /** Publish `data` to `topic` as a JSON frame. Requires an open socket. */\n publish(topic: string, data: unknown, options?: DataBusPublishOptions): MaybePromise<void> {\n if (data instanceof ArrayBuffer) {\n this.sendBinaryFrame(topic, data, options?.messageId, options?.timestamp);\n return;\n }\n this.sendFrame({\n op: 'publish',\n topic,\n data,\n ...(options?.messageId === undefined ? {} : { messageId: options.messageId }),\n ...(options?.timestamp === undefined ? {} : { timestamp: options.timestamp })\n });\n }\n\n /** Close the socket and drop all state. Safe to call multiple times. */\n stop(): MaybePromise<void> {\n const socket = this.socket;\n this.socket = null;\n this.handlers = null;\n this.subscribedTopics.clear();\n socket?.close();\n }\n\n /** Send one JSON frame. Frames are dropped with an `onError` report when\n * the socket is not open \u2014 subscribe frames are re-sent on open, so the\n * only real loss is a publish during a disconnect window. */\n private sendFrame(payload: { op: string; topic: string; data?: unknown; messageId?: string; timestamp?: number }): void {\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error(`WebSocket is not open; dropped \"${payload.op}\" frame.`));\n return;\n }\n this.socket.send(JSON.stringify(payload));\n }\n\n private sendBinaryFrame(topic: string, data: ArrayBuffer, messageId?: string, timestamp?: number): void {\n if (messageId !== undefined || timestamp !== undefined) {\n // Binary frames retain their compact legacy shape; metadata is sent as a\n // JSON envelope so IDs are never silently lost.\n this.sendFrame({\n op: 'publish',\n topic,\n data: Array.from(new Uint8Array(data)),\n ...(messageId === undefined ? {} : { messageId }),\n ...(timestamp === undefined ? {} : { timestamp })\n });\n return;\n }\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error('WebSocket is not open; dropped \"publish\" frame.'));\n return;\n }\n const topicBytes = new TextEncoder().encode(topic);\n if (topicBytes.length > 0xffff) {\n this.handlers?.onError(new Error('WebSocket topic is too long for a binary frame.'));\n return;\n }\n const frame = new Uint8Array(3 + topicBytes.length + data.byteLength);\n frame[0] = 0xc7;\n new DataView(frame.buffer).setUint16(1, topicBytes.length);\n frame.set(topicBytes, 3);\n frame.set(new Uint8Array(data), 3 + topicBytes.length);\n this.socket.send(frame.buffer);\n }\n\n /** Parse a server frame. Only objects carrying a string `topic` are\n * publications; malformed JSON and unknown shapes are ignored so a chatty\n * server cannot crash the message path. */\n private handleMessage(raw: unknown): void {\n let parsed: unknown;\n if (raw instanceof ArrayBuffer) {\n const bytes = new Uint8Array(raw);\n if (bytes[0] !== 0xc7 || bytes.length < 3) return;\n const topicLength = new DataView(raw).getUint16(1);\n if (bytes.length < 3 + topicLength) return;\n const topic = new TextDecoder().decode(bytes.subarray(3, 3 + topicLength));\n const data = bytes.slice(3 + topicLength).buffer;\n this.handlers?.onMessage({ topic, data: data as TData });\n return;\n }\n if (typeof raw !== 'string') return;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.handlers?.onError(new Error('WebSocket server sent a non-JSON frame.'));\n return;\n }\n if (!parsed || typeof parsed !== 'object') return;\n const publication = parseDataBusPublication<TData>(parsed);\n if (publication) this.handlers?.onMessage(publication as DataBusMessage<TData>);\n }\n}\n\n/** Resolve the platform WebSocket, or null in runtimes without one (SSR/Node). */\nfunction defaultWebSocketFactory(url: string, protocols?: string | string[]): WebSocketLike {\n if (typeof WebSocket === 'undefined') {\n throw new Error('WebSocketTransport requires a WebSocket implementation.');\n }\n return new WebSocket(url, protocols) as unknown as WebSocketLike;\n}\n\n/** Create a CrossTabDataBus backed by a plain WebSocket transport.\n * Cross-tab clustering (owner dedup, sticky routes, failover) works identically\n * to the Centrifuge backend \u2014 only the transport I/O differs. */\nexport function createWebSocketDataBus<TData = unknown>(\n options: CreateWebSocketDataBusOptions<TData>\n): CrossTabDataBus<WebSocketDataBusConfig, TData> {\n const { clusterKey, connection, ...dataBusOptions } = options;\n return new CrossTabDataBus({\n ...dataBusOptions,\n autoStart: true,\n clusterKey: clusterKey ?? connection.url,\n initialConfig: connection,\n transport: new WebSocketTransport<TData>(connection)\n });\n}\n\n/** Re-export for convenience: the status type used by the transport. */\nexport type { WorkerStatus };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/websocket.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { CrossTabDataBus } from './core/data-bus';
|
|
15
15
|
import type { CrossTabDataBusOptions } from './core/data-bus';
|
|
16
|
-
import type { DataBusTransport, DataBusTransportHandlers, MaybePromise, WorkerStatus } from './core/types';
|
|
16
|
+
import type { DataBusTransport, DataBusTransportHandlers, DataBusPublishOptions, MaybePromise, WorkerStatus } from './core/types';
|
|
17
17
|
/** Minimal WebSocket surface used by the transport. Matches the browser
|
|
18
18
|
* `WebSocket` subset the transport touches; injectable for tests and runtimes. */
|
|
19
19
|
export interface WebSocketLike {
|
|
@@ -65,7 +65,7 @@ export declare class WebSocketTransport<TData = unknown> implements DataBusTrans
|
|
|
65
65
|
/** Idempotent: unsubscribing an unknown topic is a no-op. */
|
|
66
66
|
unsubscribe(topic: string): MaybePromise<void>;
|
|
67
67
|
/** Publish `data` to `topic` as a JSON frame. Requires an open socket. */
|
|
68
|
-
publish(topic: string, data: unknown): MaybePromise<void>;
|
|
68
|
+
publish(topic: string, data: unknown, options?: DataBusPublishOptions): MaybePromise<void>;
|
|
69
69
|
/** Close the socket and drop all state. Safe to call multiple times. */
|
|
70
70
|
stop(): MaybePromise<void>;
|
|
71
71
|
/** Send one JSON frame. Frames are dropped with an `onError` report when
|
package/dist/websocket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"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"}
|
package/docs/api.md
CHANGED
|
@@ -99,10 +99,14 @@ When a handler is provided, only that callback is released; when omitted, all ha
|
|
|
99
99
|
|
|
100
100
|
Prefer using the cleanup function returned by `subscribe` to avoid accidentally removing callbacks from other modules.
|
|
101
101
|
|
|
102
|
-
### `publish(topic, data)`
|
|
102
|
+
### `publish(topic, data, options?)`
|
|
103
103
|
|
|
104
104
|
```ts
|
|
105
|
-
publish(
|
|
105
|
+
publish(
|
|
106
|
+
topic: string,
|
|
107
|
+
data: unknown,
|
|
108
|
+
options?: { messageId?: string; timestamp?: number }
|
|
109
|
+
): void
|
|
106
110
|
```
|
|
107
111
|
|
|
108
112
|
Routes the publish operation to the current topic owner; uses the current Worker when no valid route exists.
|
|
@@ -113,6 +117,30 @@ When the owning Worker is a remote Tab and the publish control message cannot be
|
|
|
113
117
|
|
|
114
118
|
Incoming messages may include a caller/server supplied `messageId`. Enable bounded duplicate suppression with `dedup: { maxEntries, ttlMs }`; repeated IDs within the window are ignored. This is disabled by default and does not provide an exactly-once server guarantee.
|
|
115
119
|
|
|
120
|
+
When supplied, `options.messageId` and `options.timestamp` are propagated through cross-tab routing, Worker boundaries, and supported transports. The server must echo or otherwise preserve them for inbound deduplication and replay retention.
|
|
121
|
+
|
|
122
|
+
`DataBusMessage` and `DataBusPublication` expose the same optional metadata.
|
|
123
|
+
`DataBusPublicationEnvelope<TData>` is the canonical JSON envelope type:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
{
|
|
127
|
+
op: 'publication',
|
|
128
|
+
publication: { topic, data, messageId?, timestamp? }
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `clearReplay()`
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
clearReplay(): Promise<void>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Clears in-memory replay buffers and invokes the persistence adapter's optional `clear()` hook. Useful for retention policies, logout, or tenant switching. Durable history is otherwise preserved across `stop()`.
|
|
139
|
+
|
|
140
|
+
`clearReplayTopic(topic)` applies the same cleanup to one exact topic. `getDedupStats()` returns bounded counters (`enabled`, `tracked`, `accepted`, `suppressed`), and `resetDedup()` clears remembered IDs and counters without changing configuration.
|
|
141
|
+
|
|
142
|
+
`clearReplayBefore(timestamp)` removes entries older than an epoch-millisecond cutoff from memory and from adapters that implement optional `clearBefore(timestamp)`. Incoming messages receive a bus timestamp when the transport does not provide one.
|
|
143
|
+
|
|
116
144
|
### `onStatus(handler)`
|
|
117
145
|
|
|
118
146
|
```ts
|
|
@@ -275,8 +303,11 @@ Implements `DataBusTransport`. Connection lifecycle maps to the DataBus status v
|
|
|
275
303
|
|
|
276
304
|
JSON text frames:
|
|
277
305
|
|
|
278
|
-
- client → server: `{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data"
|
|
279
|
-
- server → client: `{"topic":"...","data"
|
|
306
|
+
- client → server: `{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...,"messageId"?:...,"timestamp"?:...}`
|
|
307
|
+
- server → client (canonical): `{"op":"publication","publication":{"topic":"...","data":...,"messageId"?:...,"timestamp"?:...}}`
|
|
308
|
+
- server → client (legacy, still accepted): `{"topic":"...","data":...}`
|
|
309
|
+
|
|
310
|
+
Frames without a string publication `topic` are ignored; malformed JSON surfaces via `handlers.onError` without throwing.
|
|
280
311
|
|
|
281
312
|
A pattern-aware server may deliver publications tagged with the concrete topic (recommended); publications tagged with the pattern itself are delivered through the exact-match path.
|
|
282
313
|
|
package/docs/architecture.md
CHANGED
|
@@ -384,6 +384,8 @@ A publication travels publisher → current Topic owner → transport/server →
|
|
|
384
384
|
4. The owner broadcasts `EVENT/DATABUS_PUBLICATION` over the BroadcastChannel and, if its own Tab also has a local subscription, dispatches once directly. BroadcastChannel never echoes to the sender, so there is no duplicate dispatch.
|
|
385
385
|
5. Every other Tab receives the `EVENT` but invokes its local handlers only when it holds a `subscriber:{topicKey}:{tabId}` record for that topic; Tabs without a local subscription drop the message.
|
|
386
386
|
|
|
387
|
+
Optional publication metadata (`messageId` and `timestamp`) follows the same path as the payload: `CONTROL/PUBLISH` → transport/server → `DataBusMessage` → `EVENT` fan-out. It is never written to coordination storage. Transports normalize legacy payloads and the canonical `DataBusPublicationEnvelope` before the three dispatch gates.
|
|
388
|
+
|
|
387
389
|
At the transport layer, a Centrifuge client can emit a publication both on the `client` object and on the matching `Subscription` object. To avoid dispatching the same server publication twice, the Centrifuge session only handles the client-level `publication` for topics that have **no active client-side subscription** (server-side subscriptions); topics with an active subscription are delivered solely through the subscription-level listener.
|
|
388
390
|
|
|
389
391
|
```mermaid
|
|
@@ -407,7 +409,7 @@ sequenceDiagram
|
|
|
407
409
|
|
|
408
410
|
BroadcastChannel does not echo to its sender, so the owner receives no `EVENT` back for its own broadcast — its local dispatch is the only local delivery.
|
|
409
411
|
|
|
410
|
-
Publications are not written to localStorage. Message data only
|
|
412
|
+
Publications are not written to localStorage. Message data and publication metadata only exist in the BroadcastChannel in-memory event and within the transport; batch writes only cover coordination metadata.
|
|
411
413
|
|
|
412
414
|
### Dispatch flow: three gates
|
|
413
415
|
|