lanka 1.0.0 → 1.1.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.
Files changed (52) hide show
  1. package/README.md +5 -3
  2. package/dist/{ALankaGateway-ExlRGT3D.d.ts → ALankaGateway-CkW1LbKE.d.ts} +50 -4
  3. package/dist/{ILankaScenarioMetadata-Bu-yggTZ.d.ts → ILankaScenarioMetadata-GoWWNEQL.d.ts} +1 -1
  4. package/dist/{ILankaScenarioVM-DuCyPoyT.d.ts → ILankaScenarioVM-DUsI-fSc.d.ts} +116 -3
  5. package/dist/{LankaScenarioLocator-BGQHwf3n.d.ts → LankaScenarioLocator-D86TIwiu.d.ts} +11 -6
  6. package/dist/{LankaSharedStoreLocator-MvCpav5F.d.ts → LankaSharedStoreLocator-zS2kLu-S.d.ts} +21 -0
  7. package/dist/_extend/index.d.ts +7 -7
  8. package/dist/_extend/index.js +3 -3
  9. package/dist/_internal/index.d.ts +6 -6
  10. package/dist/{activeRuntime-FcsSJvUg.d.ts → activeRuntime-B336NU5I.d.ts} +3 -3
  11. package/dist/bootstrap/index.d.ts +14 -145
  12. package/dist/bootstrap/index.js +5 -5
  13. package/dist/{chunk-FIR4XTBL.js → chunk-5MAQVBI2.js} +27 -24
  14. package/dist/chunk-5MAQVBI2.js.map +1 -0
  15. package/dist/chunk-D5WKKEIR.js +54 -0
  16. package/dist/chunk-D5WKKEIR.js.map +1 -0
  17. package/dist/{chunk-RYFZCAQ3.js → chunk-LMKLLEHA.js} +111 -25
  18. package/dist/chunk-LMKLLEHA.js.map +1 -0
  19. package/dist/{chunk-EWVDJYCC.js → chunk-O5ROO7QF.js} +28 -9
  20. package/dist/chunk-O5ROO7QF.js.map +1 -0
  21. package/dist/{chunk-ILQNYQY5.js → chunk-UE2C76OR.js} +32 -18
  22. package/dist/chunk-UE2C76OR.js.map +1 -0
  23. package/dist/{chunk-Q3SOVBIJ.js → chunk-UGXSGQPW.js} +8 -4
  24. package/dist/chunk-UGXSGQPW.js.map +1 -0
  25. package/dist/createLanka-DI1CSy2Q.d.ts +139 -0
  26. package/dist/{createLankaScope-Bc_vChRs.d.ts → createLankaScope-BiFxNQgl.d.ts} +1 -1
  27. package/dist/gateway/index.d.ts +77 -67
  28. package/dist/gateway/index.js +51 -56
  29. package/dist/gateway/index.js.map +1 -1
  30. package/dist/index.d.ts +9 -8
  31. package/dist/index.js +5 -5
  32. package/dist/locator/index.d.ts +7 -55
  33. package/dist/locator/index.js +1 -1
  34. package/dist/scenario/index.d.ts +6 -4
  35. package/dist/scenario/index.js +2 -2
  36. package/dist/stream/index.d.ts +386 -0
  37. package/dist/stream/index.js +287 -0
  38. package/dist/stream/index.js.map +1 -0
  39. package/dist/validation/index.js +4 -48
  40. package/dist/validation/index.js.map +1 -1
  41. package/dist/viewmodel/index.d.ts +1 -1
  42. package/dist/viewmodel/index.js +4 -2
  43. package/dist/viewmodel/index.js.map +1 -1
  44. package/package.json +7 -3
  45. package/skills/lanka-core/SKILL.md +1 -1
  46. package/skills/lanka-core/reference.md +110 -17
  47. package/skills/lanka-packages/SKILL.md +1 -1
  48. package/dist/chunk-EWVDJYCC.js.map +0 -1
  49. package/dist/chunk-FIR4XTBL.js.map +0 -1
  50. package/dist/chunk-ILQNYQY5.js.map +0 -1
  51. package/dist/chunk-Q3SOVBIJ.js.map +0 -1
  52. package/dist/chunk-RYFZCAQ3.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,16 +1,17 @@
1
- export { ALankaPlugin, ILankaBootstrapConfig, ILankaInstance, ILankaInstanceConfig, ILankaPlugin, ILankaScenarioBootstrapConfig, ILankaServiceConfig, ILankaStartOptions, TLankaStartConfig, createLanka, resetActiveLanka, startLanka } from './bootstrap/index.js';
1
+ export { I as ILankaBootstrapConfig, a as ILankaInstance, b as ILankaInstanceConfig, c as ILankaPlugin, d as ILankaScenarioBootstrapConfig, e as ILankaServiceConfig, f as createLanka } from './createLanka-DI1CSy2Q.js';
2
+ export { ALankaPlugin, ILankaStartOptions, TLankaStartConfig, resetActiveLanka, startLanka } from './bootstrap/index.js';
2
3
  export { ILankaRoleFactory, ILankaRoleOpening, TLankaRoleOpener, defineLankaRole } from './role/index.js';
3
4
  export { ILankaHostConfig, createLankaHost, getLankaFlags, getLankaHost } from './config/index.js';
4
5
  export { I as ILankaFlags, a as ILankaHost, b as ILankaRuntimeConfig } from './ILankaRuntimeConfig-Vl436GWK.js';
5
- export { I as ILankaLocatorConfig } from './LankaSharedStoreLocator-MvCpav5F.js';
6
+ export { I as ILankaLocatorConfig } from './LankaSharedStoreLocator-zS2kLu-S.js';
6
7
  export { lankaLogger } from './logger/index.js';
7
8
  export { I as ILankaErrorInit, L as LankaError, T as TLankaErrorKind } from './LankaError-B1HtuIkw.js';
8
- import './createLankaScope-Bc_vChRs.js';
9
- import './activeRuntime-FcsSJvUg.js';
10
- import './ILankaScenarioVM-DuCyPoyT.js';
11
- import './LankaScenarioLocator-BGQHwf3n.js';
12
- import './ILankaScenarioMetadata-Bu-yggTZ.js';
13
- import './ALankaGateway-ExlRGT3D.js';
9
+ import './createLankaScope-BiFxNQgl.js';
10
+ import './activeRuntime-B336NU5I.js';
11
+ import './ILankaScenarioVM-DUsI-fSc.js';
12
+ import './LankaScenarioLocator-D86TIwiu.js';
13
+ import './ILankaScenarioMetadata-GoWWNEQL.js';
14
+ import './ALankaGateway-CkW1LbKE.js';
14
15
  import './lankaStandardValidator-CL-r-zEV.js';
15
16
  import '@standard-schema/spec';
16
17
  import './lankaHttpInFlight-Bk1eIuSx.js';
package/dist/index.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  createLanka,
7
7
  resetActiveLanka,
8
8
  startLanka
9
- } from "./chunk-RYFZCAQ3.js";
10
- import "./chunk-Q3SOVBIJ.js";
11
- import "./chunk-FIR4XTBL.js";
12
- import "./chunk-EWVDJYCC.js";
9
+ } from "./chunk-LMKLLEHA.js";
10
+ import "./chunk-UGXSGQPW.js";
11
+ import "./chunk-5MAQVBI2.js";
12
+ import "./chunk-O5ROO7QF.js";
13
13
  import {
14
14
  createLankaHost
15
15
  } from "./chunk-XESL274R.js";
@@ -20,7 +20,7 @@ import {
20
20
  import {
21
21
  getLankaHost
22
22
  } from "./chunk-RKYKK6MN.js";
23
- import "./chunk-ILQNYQY5.js";
23
+ import "./chunk-UE2C76OR.js";
24
24
  import {
25
25
  lankaLogger
26
26
  } from "./chunk-C2HP7CRD.js";
@@ -1,24 +1,15 @@
1
- export { I as ILankaScope } from '../createLankaScope-Bc_vChRs.js';
2
- export { a as ILankaLocator, I as ILankaLocatorConfig, c as ILankaSharedStoreLocatorConfig, d as ILankaSingletonLocatorConfig } from '../LankaSharedStoreLocator-MvCpav5F.js';
3
- import { A as ALankaGateway } from '../ALankaGateway-ExlRGT3D.js';
1
+ export { I as ILankaScope } from '../createLankaScope-BiFxNQgl.js';
2
+ export { a as ILankaLocator, I as ILankaLocatorConfig, c as ILankaSharedStoreLocatorConfig, d as ILankaSingletonLocatorConfig } from '../LankaSharedStoreLocator-zS2kLu-S.js';
3
+ import * as GatewaysModule from '@lanka_di/Gateways';
4
+ import { A as ALankaGateway } from '../ALankaGateway-CkW1LbKE.js';
5
+ import * as ScenariosModule from '@lanka_di/Scenarios';
6
+ import * as SingletonsModule from '@lanka_di/Singletons';
7
+ import * as SharedStoresModule from '@lanka_di/SharedStores';
4
8
  import { A as ALankaSharedStore } from '../ALankaSharedStore-B7uepuuk.js';
5
9
  import '../lankaStandardValidator-CL-r-zEV.js';
6
10
  import '@standard-schema/spec';
7
11
  import 'zustand/vanilla';
8
12
 
9
- /**
10
- * The singletons this app publishes to `lanka`.
11
- *
12
- * Add one export line per class; the framework derives the locator from these
13
- * exports, so there is nothing else to register.
14
- *
15
- * @example export { AnalyticsService } from "../src/...";
16
- */
17
-
18
- declare namespace SingletonsModule {
19
- export { };
20
- }
21
-
22
13
  /**
23
14
  * The singleton marker: "this class is published by the application in
24
15
  * `lankaSingletons`".
@@ -87,19 +78,6 @@ type TLankaSingletons = {
87
78
 
88
79
  declare const lankaSingletons: TLankaSingletons;
89
80
 
90
- /**
91
- * The shared stores this app publishes to `lanka`.
92
- *
93
- * Add one export line per class; the framework derives the locator from these
94
- * exports, so there is nothing else to register.
95
- *
96
- * @example export { UserSharedStore } from "../src/...";
97
- */
98
-
99
- declare namespace SharedStoresModule {
100
- export { };
101
- }
102
-
103
81
  /**
104
82
  * Shared-store classes read from the consumer's barrel.
105
83
  *
@@ -135,19 +113,6 @@ type TLankaSharedStores = {
135
113
  */
136
114
  declare const lankaSharedStores: TLankaSharedStores;
137
115
 
138
- /**
139
- * The gateways this app publishes to `lanka`.
140
- *
141
- * Add one export line per class; the framework derives the locator from these
142
- * exports, so there is nothing else to register.
143
- *
144
- * @example export { UserGateway } from "../src/...";
145
- */
146
-
147
- declare namespace GatewaysModule {
148
- export { };
149
- }
150
-
151
116
  /**
152
117
  * Gateway classes read from the consumer's barrel.
153
118
  *
@@ -190,19 +155,6 @@ type TLankaGateways = {
190
155
  */
191
156
  declare const lankaGateways: TLankaGateways;
192
157
 
193
- /**
194
- * The scenarios this app publishes to `lanka`.
195
- *
196
- * Add one export line per class; the framework derives the locator from these
197
- * exports, so there is nothing else to register.
198
- *
199
- * @example export { SessionScenario } from "../src/...";
200
- */
201
-
202
- declare namespace ScenariosModule {
203
- export { };
204
- }
205
-
206
158
  /**
207
159
  * Scenario classes read from the consumer's barrel.
208
160
  *
@@ -3,7 +3,7 @@ import {
3
3
  lankaSharedStores,
4
4
  lankaSingletons
5
5
  } from "../chunk-DTO27QFR.js";
6
- import "../chunk-ILQNYQY5.js";
6
+ import "../chunk-UE2C76OR.js";
7
7
  import {
8
8
  requireActiveRuntime
9
9
  } from "../chunk-BGVDPDX4.js";
@@ -1,6 +1,6 @@
1
- import { a as ILankaScenario, T as TLankaReplayRequest, b as ILankaEventMetadata, c as TLankaEventBusMiddleware, d as ILankaEventLog, I as ILankaScenarioVM } from '../ILankaScenarioVM-DuCyPoyT.js';
2
- export { e as TLankaEventBusDecision } from '../ILankaScenarioVM-DuCyPoyT.js';
3
- export { I as ILankaScenarioMetadata } from '../ILankaScenarioMetadata-Bu-yggTZ.js';
1
+ import { a as ILankaScenario, T as TLankaReplayRequest, b as ILankaEventMetadata, c as TLankaEventBusMiddleware, d as TLankaEventBusObserver, e as ILankaEventLog, I as ILankaScenarioVM } from '../ILankaScenarioVM-DUsI-fSc.js';
2
+ export { f as ILankaEventBusOutcome, g as TLankaEventBusDecision } from '../ILankaScenarioVM-DUsI-fSc.js';
3
+ export { I as ILankaScenarioMetadata } from '../ILankaScenarioMetadata-GoWWNEQL.js';
4
4
 
5
5
  /**
6
6
  * The base of a scenario — a named unit of coordination over the event bus.
@@ -115,6 +115,8 @@ declare const lankaEventBus: Readonly<{
115
115
  dispatch: <T>(eventType: string, data?: T, usedBy?: string) => void;
116
116
  addMiddleware: <T>(middleware: TLankaEventBusMiddleware<T>) => void;
117
117
  removeMiddleware: <T>(middleware: TLankaEventBusMiddleware<T>) => void;
118
+ addObserver: (observer: TLankaEventBusObserver) => void;
119
+ removeObserver: (observer: TLankaEventBusObserver) => void;
118
120
  getEventLogs: (eventType?: string, limit?: number) => ILankaEventLog[];
119
121
  clearEvent: (eventType: string) => void;
120
122
  clearAllEvents: () => void;
@@ -201,4 +203,4 @@ declare class LankaScenarioBootstrap {
201
203
  /** The one every caller wants. */
202
204
  declare const lankaScenarioBootstrap: LankaScenarioBootstrap;
203
205
 
204
- export { ALankaScenario, ILankaEventLog, ILankaEventMetadata, ILankaScenario, type ILankaScenarioConfig, ILankaScenarioVM, TLankaEventBusMiddleware, TLankaReplayRequest, createLankaScenario, lankaEventBus, lankaScenarioBootstrap };
206
+ export { ALankaScenario, ILankaEventLog, ILankaEventMetadata, ILankaScenario, type ILankaScenarioConfig, ILankaScenarioVM, TLankaEventBusMiddleware, TLankaEventBusObserver, TLankaReplayRequest, createLankaScenario, lankaEventBus, lankaScenarioBootstrap };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  lankaScenarioBootstrap
3
- } from "../chunk-Q3SOVBIJ.js";
3
+ } from "../chunk-UGXSGQPW.js";
4
4
  import {
5
5
  ALankaScenario,
6
6
  lankaEventBus
7
- } from "../chunk-EWVDJYCC.js";
7
+ } from "../chunk-O5ROO7QF.js";
8
8
  import "../chunk-C2HP7CRD.js";
9
9
  import "../chunk-D27MREPB.js";
10
10
  import "../chunk-BGVDPDX4.js";
@@ -0,0 +1,386 @@
1
+ import { c as ILankaPlugin } from '../createLanka-DI1CSy2Q.js';
2
+ import '../createLankaScope-BiFxNQgl.js';
3
+ import '../LankaSharedStoreLocator-zS2kLu-S.js';
4
+ import '../ALankaSharedStore-B7uepuuk.js';
5
+ import 'zustand/vanilla';
6
+ import '../activeRuntime-B336NU5I.js';
7
+ import '../ILankaScenarioVM-DUsI-fSc.js';
8
+ import '../LankaScenarioLocator-D86TIwiu.js';
9
+ import '../ILankaScenarioMetadata-GoWWNEQL.js';
10
+ import '../ALankaGateway-CkW1LbKE.js';
11
+ import '../lankaStandardValidator-CL-r-zEV.js';
12
+ import '@standard-schema/spec';
13
+ import '../ILankaRuntimeConfig-Vl436GWK.js';
14
+ import '../lankaHttpInFlight-Bk1eIuSx.js';
15
+ import '../lankaRequestMiddleware-DAC5kCb7.js';
16
+
17
+ /**
18
+ * What a subscriber is handed when a named event arrives.
19
+ *
20
+ * A record rather than a generic: the transport parsed bytes and knows nothing
21
+ * about what they mean, and a bridge is the layer that names the shape. Typing
22
+ * it here would be the transport asserting something it did not check.
23
+ */
24
+ type TLankaStreamEventCallback = (payload: Record<string, unknown>) => void;
25
+
26
+ /**
27
+ * Called after a connection is re-established, never on the first one.
28
+ *
29
+ * No arguments deliberately: the one fact it carries is "there is a gap in what
30
+ * you were told", and nothing about the gap is knowable from this side.
31
+ */
32
+ type TLankaStreamReconnectCallback = () => void;
33
+
34
+ /**
35
+ * A connection that pushes named events, whatever carries them.
36
+ *
37
+ * The port is the whole reason a screen can stop caring which protocol it is
38
+ * on. `text/event-stream`, a WebSocket, a `graphql-ws` subscription and a gRPC
39
+ * server stream differ in how bytes arrive and in nothing a bridge can see —
40
+ * and the choice between them is made by a proxy, a load balancer or a backend
41
+ * team, none of which the application controls.
42
+ *
43
+ * What a bridge needs is exactly this: subscribe to a named event, learn that
44
+ * the connection came back, and be told whether there is a connection to be had
45
+ * at all. Everything else — reconnect backoff, envelope parsing, credentials,
46
+ * a handshake — is an implementation's own business, and `ALankaStreamTransport`
47
+ * is where the parts that are the same for all of them live.
48
+ *
49
+ * ## Kept minimal on purpose
50
+ *
51
+ * A CONSUMER implements this — a native bridge, a mock, a socket the
52
+ * application already had — so every member added later is a compile error in
53
+ * code nobody touched (`skills/surface/SKILL.md` §6b). Five members is not
54
+ * meanness; it is the only form the promise survives in.
55
+ */
56
+ interface ILankaServerEventTransport {
57
+ /** Whether this engine can carry server events at all. */
58
+ isSupported: () => boolean;
59
+ connect: () => void;
60
+ disconnect: () => void;
61
+ /** Subscribes to one named event. Returns the unsubscribe. */
62
+ on: (eventType: string, callback: TLankaStreamEventCallback) => () => void;
63
+ /**
64
+ * Called after a connection is re-established, never on the first one.
65
+ *
66
+ * What it is for: everything that happened while the connection was down was
67
+ * missed, so a screen refetches rather than assuming it is current.
68
+ */
69
+ onReconnect: (callback: TLankaStreamReconnectCallback) => () => void;
70
+ }
71
+
72
+ interface ILankaStreamTriggerContext {
73
+ /** Whether a SERVER event handler is running right now. */
74
+ isActive: () => boolean;
75
+ /** Runs a function marked as triggered by a server event. */
76
+ run: <T>(fn: () => T) => T;
77
+ }
78
+ /**
79
+ * The "this change came from outside" marker.
80
+ *
81
+ * A handler that updates state cannot tell its own change from someone else's:
82
+ * the user pressed a button and then receives the server event about that
83
+ * button. Without the marker the screen notifies the user about their own
84
+ * action, and an optimistic update is rolled back by a "foreign" response that
85
+ * in fact confirms it.
86
+ *
87
+ * ## An instance, not a module variable
88
+ *
89
+ * A module-level flag is one per process: two framework instances (a test beside
90
+ * the app) would share it, and one instance's handler would see a marker set by
91
+ * the other.
92
+ *
93
+ * ## Boundaries
94
+ *
95
+ * The marker is SYNCHRONOUS: it holds for the duration of the call and is
96
+ * cleared in `finally`. An async continuation (an `await` inside the handler)
97
+ * no longer sees it — honestly so: after an `await` control has been anywhere,
98
+ * and claiming "we are still inside a server event" would be untrue.
99
+ */
100
+ declare const createLankaStreamTriggerContext: () => ILankaStreamTriggerContext;
101
+
102
+ /**
103
+ * A server-event -> application-scenario bridge.
104
+ *
105
+ * It KNOWS about the event stream and the "came from outside" marker, and knows
106
+ * no concrete event type: which bridges exist is the application's domain.
107
+ *
108
+ * ## Inbound only, even where the wire is two-way
109
+ *
110
+ * A WebSocket can send and a bridge cannot, deliberately. A bridge translates
111
+ * what arrived into a fact the rest of the application already understands, and
112
+ * a bridge that also sends becomes the one object that both starts and finishes
113
+ * a conversation — untestable without a socket, and the place every screen
114
+ * eventually reaches into. Sending belongs to whatever already owns the action:
115
+ * a gateway holding the channel, or a ViewModel calling it.
116
+ *
117
+ * ## Why subscriptions are collected rather than forgotten
118
+ *
119
+ * A bridge that subscribes in its constructor and never unsubscribes cannot
120
+ * notice the problem while it lives exactly as long as the application. In a
121
+ * package it is different — the plugin is removed, the instance is disposed, a
122
+ * dev server reloads the module — and a leftover subscription keeps triggering a
123
+ * dead instance's scenarios.
124
+ */
125
+ declare abstract class ALankaStreamBridge {
126
+ private readonly unsubscribes;
127
+ protected readonly stream: ILankaServerEventTransport;
128
+ protected readonly trigger: ILankaStreamTriggerContext;
129
+ constructor(stream: ILankaServerEventTransport, trigger: ILankaStreamTriggerContext);
130
+ /** Subscribes the bridge to its events. Called by the plugin on install. */
131
+ abstract register(): void;
132
+ /** Removes everything the bridge subscribed to. */
133
+ dispose(): void;
134
+ /**
135
+ * Subscribes to an event type; the handler runs with the "from outside"
136
+ * marker.
137
+ *
138
+ * The marker is set HERE rather than left to each bridge: a handler that
139
+ * forgot it is indistinguishable from a user action, and the difference is
140
+ * silent — the screen simply behaves oddly in a rare case.
141
+ */
142
+ protected on(eventType: string, handler: TLankaStreamEventCallback): void;
143
+ /** The same for an event with no payload. */
144
+ protected onSignal(eventType: string, handler: () => void): void;
145
+ /** Catch-up for what was missed while disconnected. */
146
+ protected onReconnect(handler: () => void): void;
147
+ }
148
+
149
+ /** What the bridges factory is handed: the connection, and the marker. */
150
+ interface ILankaStreamPluginContext {
151
+ stream: ILankaServerEventTransport;
152
+ trigger: ILankaStreamTriggerContext;
153
+ }
154
+ interface ILankaStreamPluginConfig {
155
+ /** The connection this plugin owns for its lifetime. */
156
+ transport: ILankaServerEventTransport;
157
+ /**
158
+ * The application's bridges, created by it — the framework knows no event type.
159
+ *
160
+ * A factory rather than ready objects: a bridge needs the transport and the
161
+ * "from outside" marker, and both belong to THIS plugin installation. The same
162
+ * bridge declaration can therefore be handed to two instances in one process.
163
+ */
164
+ bridges?: (context: ILankaStreamPluginContext) => readonly ALankaStreamBridge[];
165
+ /**
166
+ * Connect as soon as the plugin is registered. Off by default.
167
+ *
168
+ * Deliberately off: the stream is opened for an AUTHENTICATED user, and when
169
+ * that happens is the application's knowledge. A plugin that connected by
170
+ * itself would open a connection on the sign-in screen.
171
+ */
172
+ connectOnInstall?: boolean;
173
+ /**
174
+ * The name the plugin is registered under. Defaults to `lanka/stream`.
175
+ *
176
+ * A protocol package passes its own npm name, so a duplicate registration
177
+ * names the package a reader can go and look at. Registering twice is refused
178
+ * by the registry, and two copies of one connection is exactly the mistake
179
+ * worth refusing.
180
+ */
181
+ name?: string;
182
+ }
183
+ interface ILankaStreamPlugin extends ILankaPlugin {
184
+ /** The connection: subscribe, connect, disconnect. */
185
+ readonly stream: ILankaServerEventTransport;
186
+ /** The "this change came from the server" marker. */
187
+ readonly trigger: ILankaStreamTriggerContext;
188
+ }
189
+ /**
190
+ * A pushing connection wired into the framework's lifetime.
191
+ *
192
+ * Usable directly — hand it any `ILankaServerEventTransport` and your bridges —
193
+ * and it is also what every protocol plugin in this repository is built on, so
194
+ * the four of them cannot disagree about installation order.
195
+ *
196
+ * ## The order in teardown is the whole point
197
+ *
198
+ * Bridges are detached BEFORE the connection is closed. The other order leaves
199
+ * them a window in which a dying connection delivers one last event into
200
+ * scenarios belonging to an instance that is being disposed.
201
+ */
202
+ declare const lankaStream: (config: ILankaStreamPluginConfig) => ILankaStreamPlugin;
203
+
204
+ /** What every pushing connection is configured with, whatever carries it. */
205
+ interface ILankaStreamConfig {
206
+ /** How many reconnect attempts before giving up. Defaults to 10. */
207
+ maxReconnectAttempts?: number;
208
+ /** The first pause between attempts; it doubles from there. Defaults to 1s. */
209
+ reconnectDelayMs?: number;
210
+ /** Ceiling on the pause between attempts. Defaults to 30 seconds. */
211
+ maxReconnectDelayMs?: number;
212
+ /**
213
+ * Refreshes authorization when attempts are exhausted. `true` means try again.
214
+ *
215
+ * A function, not a URL: a framework that knew the endpoint would also know
216
+ * the response shape and how the session is stored.
217
+ */
218
+ refreshAuth?: () => Promise<boolean>;
219
+ /** The session is lost for good: the application signs the user out. */
220
+ onSessionLost?: () => void;
221
+ }
222
+ /**
223
+ * How a subclass reports what happened on the wire.
224
+ *
225
+ * Three facts and no more, because they are the three the ladder above branches
226
+ * on. A subclass able to reach further into the base could reset the attempt
227
+ * counter or fire a reconnect by hand, which is exactly the state this class
228
+ * exists to own.
229
+ */
230
+ interface ILankaStreamTransportHandlers {
231
+ /**
232
+ * The connection is up and ready to carry subscriptions.
233
+ *
234
+ * Called by the subclass when the far end is actually usable — after a
235
+ * handshake, not merely after a socket opened — because the base answers it by
236
+ * subscribing to every event type somebody is waiting for.
237
+ */
238
+ opened: () => void;
239
+ /** A named event arrived. */
240
+ received: (eventType: string, payload: Record<string, unknown>) => void;
241
+ /** The connection is gone: dropped, refused, or closed by the far end. */
242
+ lost: () => void;
243
+ }
244
+ /**
245
+ * The half of a pushing connection that is the same for every protocol.
246
+ *
247
+ * Four transports in this repository — server-sent events, a WebSocket, a
248
+ * `graphql-ws` subscription and a gRPC server stream — differ in how a
249
+ * connection is opened and in how bytes become a named event. They do not
250
+ * differ in anything below:
251
+ *
252
+ * - who is listening to which event type, and dispatch to a COPY of that set;
253
+ * - the reconnect ladder: growing backoff with a ceiling, an attempt count that
254
+ * only resets on a connection that actually opened, one auth refresh at the
255
+ * end of it, and an explicit disconnect that outranks everything scheduled;
256
+ * - `onReconnect` meaning "you missed something", so it never fires the first
257
+ * time.
258
+ *
259
+ * A subclass writes `open` and `close`, and `subscribeTo` / `unsubscribeFrom`
260
+ * when the wire has to be told which events are wanted.
261
+ *
262
+ * ## A subclass never says "connected"
263
+ *
264
+ * Everything the ladder decides is decided here, off the three handlers. That is
265
+ * what keeps a protocol package free of the bug this shape is prone to:
266
+ * resetting the attempt counter where reconnection STARTS rather than where it
267
+ * SUCCEEDS, after which a ten-attempt ceiling exists, reads as a guard, and can
268
+ * never fire.
269
+ */
270
+ declare abstract class ALankaStreamTransport implements ILankaServerEventTransport {
271
+ private readonly listeners;
272
+ private readonly reconnectCallbacks;
273
+ private readonly config;
274
+ private readonly handlers;
275
+ private reconnectTimer;
276
+ private reconnectAttempts;
277
+ /** A connection exists as far as this class is concerned. */
278
+ private live;
279
+ /**
280
+ * A connection has opened at least once.
281
+ *
282
+ * Separate from `hadError`, and both are needed. A first attempt that FAILS
283
+ * and a second that succeeds is not a reconnection — nothing was ever
284
+ * delivered, so nothing was missed — and announcing one there makes every
285
+ * screen refetch the data it has just loaded.
286
+ */
287
+ private everConnected;
288
+ /** The last connection ended badly, so the next `opened` is a RE-connection. */
289
+ private hadError;
290
+ /** Disconnected EXPLICITLY: everything scheduled after this must die. */
291
+ private stopped;
292
+ constructor(config?: ILankaStreamConfig);
293
+ /**
294
+ * Whether this engine can carry the connection at all.
295
+ *
296
+ * `true` unless a subclass says otherwise, because most transports need
297
+ * nothing the platform might be missing. Where one does, the answer is a
298
+ * `typeof` guard: an engine without the global gets the plugin switched off
299
+ * for the session rather than a `ReferenceError` from inside the framework.
300
+ */
301
+ isSupported(): boolean;
302
+ connect(): void;
303
+ disconnect(): void;
304
+ /** Subscribes to an event type. Returns an unsubscribe function. */
305
+ on(eventType: string, callback: TLankaStreamEventCallback): () => void;
306
+ /** Notification after EVERY reconnection. Returns an unsubscribe function. */
307
+ onReconnect(callback: TLankaStreamReconnectCallback): () => void;
308
+ /**
309
+ * Opens the connection and reports through the handlers.
310
+ *
311
+ * Called once per attempt. Throwing is allowed and means "this engine cannot
312
+ * do it": the transport goes quiet instead of retrying.
313
+ */
314
+ protected abstract open(handlers: ILankaStreamTransportHandlers): void;
315
+ /**
316
+ * Closes whatever `open` opened.
317
+ *
318
+ * Called at most ONCE per connection, and never for one `open` did not finish
319
+ * — so a subclass may assume it holds what it created. The base owns that
320
+ * guarantee (see `acceptLoss`) precisely so a subclass does not carry a flag
321
+ * to make it true.
322
+ */
323
+ protected abstract close(): void;
324
+ /**
325
+ * Tells the wire that an event type is wanted, where the wire needs telling.
326
+ *
327
+ * Called for every already-known type as soon as a connection opens, and for
328
+ * each new one after that — so a subclass never has to remember what was
329
+ * subscribed before the link came back.
330
+ */
331
+ protected subscribeTo(eventType: string): void;
332
+ /** The reverse, when the last subscriber of a type goes away. */
333
+ protected unsubscribeFrom(eventType: string): void;
334
+ /** Every event type somebody is waiting for right now. */
335
+ protected subscribedEventTypes(): readonly string[];
336
+ private acceptOpen;
337
+ private acceptLoss;
338
+ private forget;
339
+ private dispatch;
340
+ private clearReconnect;
341
+ private scheduleReconnect;
342
+ /**
343
+ * The attempts are spent: refresh the session once, or report it lost.
344
+ *
345
+ * `stopped` is re-read after the await. The refresh is a network call of
346
+ * unknown length, and an application that signed the user out while it was in
347
+ * flight must not be handed a connection afterwards.
348
+ */
349
+ private lastResort;
350
+ }
351
+
352
+ /**
353
+ * A bridge's protected surface, handed to whoever writes one by calling.
354
+ *
355
+ * The names match `ALankaStreamBridge`'s protected members exactly: a consumer
356
+ * who switches styles moves `this.on(...)` to `on(...)` and changes nothing
357
+ * else. Checked by `scripts/check-parity.mjs`.
358
+ */
359
+ interface ILankaStreamBridgeContext {
360
+ /** Subscribes to an event type; the handler runs with the "from outside" marker. */
361
+ on: (eventType: string, handler: TLankaStreamEventCallback) => void;
362
+ /** The same for an event with no payload. */
363
+ onSignal: (eventType: string, handler: () => void) => void;
364
+ /** Catch-up for what was missed while disconnected. */
365
+ onReconnect: (handler: () => void) => void;
366
+ }
367
+
368
+ /**
369
+ * A bridge, without writing a class.
370
+ *
371
+ * What a bridge does is one function — subscribe these events, trigger those
372
+ * scenarios — and the class exists to carry the subscriptions and the "from
373
+ * outside" marker, which this carries identically: it IS an `ALankaStreamBridge`.
374
+ *
375
+ * The plugin receives what a class-style bridge would give it, `dispose`
376
+ * included, so nothing above knows which style wrote it.
377
+ *
378
+ * Two steps rather than one call: a bridge needs the transport and the marker,
379
+ * and both belong to the plugin INSTALLATION rather than to the module that
380
+ * declares the bridge. So the declaration is written once at module level and
381
+ * the plugin applies it, which is what lets the same bridge be handed to two
382
+ * instances in one process.
383
+ */
384
+ declare const createLankaStreamBridge: (register: (context: ILankaStreamBridgeContext) => void) => (stream: ILankaServerEventTransport, trigger: ILankaStreamTriggerContext) => ALankaStreamBridge;
385
+
386
+ export { ALankaStreamBridge, ALankaStreamTransport, type ILankaServerEventTransport, type ILankaStreamBridgeContext, type ILankaStreamConfig, type ILankaStreamPlugin, type ILankaStreamPluginConfig, type ILankaStreamPluginContext, type ILankaStreamTransportHandlers, type ILankaStreamTriggerContext, type TLankaStreamEventCallback, type TLankaStreamReconnectCallback, createLankaStreamBridge, createLankaStreamTriggerContext, lankaStream };