joopjs 2.0.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 +678 -0
- package/README.md +583 -0
- package/dist/a11y.service-C-DQQfgO.d.mts +143 -0
- package/dist/a11y.service-CauEJrJe.d.ts +143 -0
- package/dist/adapters-B6slG6hQ.d.mts +84 -0
- package/dist/adapters-B6slG6hQ.d.ts +84 -0
- package/dist/aes.service-CkoupAww.d.mts +95 -0
- package/dist/aes.service-CkoupAww.d.ts +95 -0
- package/dist/ai/index.d.mts +99 -0
- package/dist/ai/index.d.ts +99 -0
- package/dist/ai/index.js +307 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/index.mjs +304 -0
- package/dist/ai/index.mjs.map +1 -0
- package/dist/analytics/index.d.mts +42 -0
- package/dist/analytics/index.d.ts +42 -0
- package/dist/analytics/index.js +139 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +136 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/angular/index.d.mts +148 -0
- package/dist/angular/index.d.ts +148 -0
- package/dist/angular/index.js +122 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/angular/index.mjs +101 -0
- package/dist/angular/index.mjs.map +1 -0
- package/dist/api/index.d.mts +128 -0
- package/dist/api/index.d.ts +128 -0
- package/dist/api/index.js +1358 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/index.mjs +1332 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/auth/index.d.mts +105 -0
- package/dist/auth/index.d.ts +105 -0
- package/dist/auth/index.js +989 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +979 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/auth.service-DNVB-L4U.d.mts +16 -0
- package/dist/auth.service-PjUUSUIt.d.ts +16 -0
- package/dist/banking/index.d.mts +1530 -0
- package/dist/banking/index.d.ts +1530 -0
- package/dist/banking/index.js +4739 -0
- package/dist/banking/index.js.map +1 -0
- package/dist/banking/index.mjs +4661 -0
- package/dist/banking/index.mjs.map +1 -0
- package/dist/cache/index.d.mts +40 -0
- package/dist/cache/index.d.ts +40 -0
- package/dist/cache/index.js +174 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/index.mjs +172 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/client-profile.service-BuPeXVp5.d.mts +28 -0
- package/dist/client-profile.service-D5bRRYQp.d.ts +28 -0
- package/dist/config.models-Cqg04fAQ.d.mts +84 -0
- package/dist/config.models-Cqg04fAQ.d.ts +84 -0
- package/dist/config.service-CrCvI-JS.d.ts +31 -0
- package/dist/config.service-Cz4QQLlf.d.mts +31 -0
- package/dist/core/index.d.mts +4 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +631 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +619 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/crypto-utils-DriNhLdx.d.mts +30 -0
- package/dist/crypto-utils-DriNhLdx.d.ts +30 -0
- package/dist/data-storage.service-DT6xaTxE.d.ts +51 -0
- package/dist/data-storage.service-LvhGRCmw.d.mts +51 -0
- package/dist/deeplink/index.d.mts +39 -0
- package/dist/deeplink/index.d.ts +39 -0
- package/dist/deeplink/index.js +268 -0
- package/dist/deeplink/index.js.map +1 -0
- package/dist/deeplink/index.mjs +265 -0
- package/dist/deeplink/index.mjs.map +1 -0
- package/dist/deeplink.service-Ctd5u243.d.mts +35 -0
- package/dist/deeplink.service-uUuTnY9_.d.ts +35 -0
- package/dist/dev/index.d.mts +20 -0
- package/dist/dev/index.d.ts +20 -0
- package/dist/dev/index.js +51 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/index.mjs +49 -0
- package/dist/dev/index.mjs.map +1 -0
- package/dist/device/index.d.mts +108 -0
- package/dist/device/index.d.ts +108 -0
- package/dist/device/index.js +960 -0
- package/dist/device/index.js.map +1 -0
- package/dist/device/index.mjs +951 -0
- package/dist/device/index.mjs.map +1 -0
- package/dist/differential-privacy-BcAv1G80.d.mts +210 -0
- package/dist/differential-privacy-C8mAUjZr.d.ts +210 -0
- package/dist/encryption/index.d.mts +75 -0
- package/dist/encryption/index.d.ts +75 -0
- package/dist/encryption/index.js +605 -0
- package/dist/encryption/index.js.map +1 -0
- package/dist/encryption/index.mjs +598 -0
- package/dist/encryption/index.mjs.map +1 -0
- package/dist/form-validator-3tkmzr_o.d.mts +72 -0
- package/dist/form-validator-3tkmzr_o.d.ts +72 -0
- package/dist/forms/index.d.mts +59 -0
- package/dist/forms/index.d.ts +59 -0
- package/dist/forms/index.js +446 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/index.mjs +442 -0
- package/dist/forms/index.mjs.map +1 -0
- package/dist/i18n/index.d.mts +37 -0
- package/dist/i18n/index.d.ts +37 -0
- package/dist/i18n/index.js +147 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/index.mjs +145 -0
- package/dist/i18n/index.mjs.map +1 -0
- package/dist/idempotency.service-_6LqhivP.d.mts +372 -0
- package/dist/idempotency.service-eOKoISRD.d.ts +372 -0
- package/dist/index-B_ksKpS1.d.mts +202 -0
- package/dist/index-CqDKWTUP.d.mts +28 -0
- package/dist/index-CqDKWTUP.d.ts +28 -0
- package/dist/index-DFqEoX_l.d.ts +202 -0
- package/dist/index-Dz0gOur2.d.mts +36 -0
- package/dist/index-Dz0gOur2.d.ts +36 -0
- package/dist/index.d.mts +1336 -0
- package/dist/index.d.ts +1336 -0
- package/dist/index.js +19464 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +19155 -0
- package/dist/index.mjs.map +1 -0
- package/dist/india/index.d.mts +75 -0
- package/dist/india/index.d.ts +75 -0
- package/dist/india/index.js +325 -0
- package/dist/india/index.js.map +1 -0
- package/dist/india/index.mjs +303 -0
- package/dist/india/index.mjs.map +1 -0
- package/dist/joop-Bx7Iwj5p.d.mts +155 -0
- package/dist/joop-CA3DMeOO.d.ts +155 -0
- package/dist/native-bridge/index.d.mts +27 -0
- package/dist/native-bridge/index.d.ts +27 -0
- package/dist/native-bridge/index.js +98 -0
- package/dist/native-bridge/index.js.map +1 -0
- package/dist/native-bridge/index.mjs +96 -0
- package/dist/native-bridge/index.mjs.map +1 -0
- package/dist/network/index.d.mts +85 -0
- package/dist/network/index.d.ts +85 -0
- package/dist/network/index.js +454 -0
- package/dist/network/index.js.map +1 -0
- package/dist/network/index.mjs +451 -0
- package/dist/network/index.mjs.map +1 -0
- package/dist/network-monitor-BIwPSXme.d.mts +179 -0
- package/dist/network-monitor-Bqp2hvZr.d.ts +179 -0
- package/dist/notification.service-Dm4fvfZf.d.mts +25 -0
- package/dist/notification.service-tEMKatWJ.d.ts +25 -0
- package/dist/observability/index.d.mts +179 -0
- package/dist/observability/index.d.ts +179 -0
- package/dist/observability/index.js +559 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/index.mjs +552 -0
- package/dist/observability/index.mjs.map +1 -0
- package/dist/oidc-client-DIJcClmB.d.mts +190 -0
- package/dist/oidc-client-DxhyE59t.d.ts +190 -0
- package/dist/platform/index.d.mts +73 -0
- package/dist/platform/index.d.ts +73 -0
- package/dist/platform/index.js +127 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +125 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/pwa/index.d.mts +31 -0
- package/dist/pwa/index.d.ts +31 -0
- package/dist/pwa/index.js +247 -0
- package/dist/pwa/index.js.map +1 -0
- package/dist/pwa/index.mjs +244 -0
- package/dist/pwa/index.mjs.map +1 -0
- package/dist/react/index.d.mts +133 -0
- package/dist/react/index.d.ts +133 -0
- package/dist/react/index.js +632 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +630 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/router/index.d.mts +39 -0
- package/dist/router/index.d.ts +39 -0
- package/dist/router/index.js +168 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/index.mjs +166 -0
- package/dist/router/index.mjs.map +1 -0
- package/dist/security/index.d.mts +206 -0
- package/dist/security/index.d.ts +206 -0
- package/dist/security/index.js +1297 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/index.mjs +1285 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/session/index.d.mts +115 -0
- package/dist/session/index.d.ts +115 -0
- package/dist/session/index.js +297 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/index.mjs +292 -0
- package/dist/session/index.mjs.map +1 -0
- package/dist/state/index.d.mts +43 -0
- package/dist/state/index.d.ts +43 -0
- package/dist/state/index.js +156 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/index.mjs +152 -0
- package/dist/state/index.mjs.map +1 -0
- package/dist/statement-parser-BHQtXwCM.d.ts +260 -0
- package/dist/statement-parser-C2qNmb49.d.mts +260 -0
- package/dist/storage/index.d.mts +40 -0
- package/dist/storage/index.d.ts +40 -0
- package/dist/storage/index.js +256 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +252 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/sync/index.d.mts +69 -0
- package/dist/sync/index.d.ts +69 -0
- package/dist/sync/index.js +330 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/index.mjs +323 -0
- package/dist/sync/index.mjs.map +1 -0
- package/dist/sync-engine-DCIMRG5s.d.ts +61 -0
- package/dist/sync-engine-DZqyKHkK.d.mts +61 -0
- package/dist/theme/index.d.mts +53 -0
- package/dist/theme/index.d.ts +53 -0
- package/dist/theme/index.js +169 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/index.mjs +167 -0
- package/dist/theme/index.mjs.map +1 -0
- package/dist/ui/index.d.mts +66 -0
- package/dist/ui/index.d.ts +66 -0
- package/dist/ui/index.js +811 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/index.mjs +803 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/utilities/index.d.mts +199 -0
- package/dist/utilities/index.d.ts +199 -0
- package/dist/utilities/index.js +1991 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/utilities/index.mjs +1923 -0
- package/dist/utilities/index.mjs.map +1 -0
- package/dist/validation/index.d.mts +60 -0
- package/dist/validation/index.d.ts +60 -0
- package/dist/validation/index.js +460 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/index.mjs +455 -0
- package/dist/validation/index.mjs.map +1 -0
- package/dist/vue/index.d.mts +135 -0
- package/dist/vue/index.d.ts +135 -0
- package/dist/vue/index.js +621 -0
- package/dist/vue/index.js.map +1 -0
- package/dist/vue/index.mjs +619 -0
- package/dist/vue/index.mjs.map +1 -0
- package/dist/watermark.service-Detur5tq.d.ts +235 -0
- package/dist/watermark.service-QNegMeQZ.d.mts +235 -0
- package/dist/workers/index.d.mts +42 -0
- package/dist/workers/index.d.ts +42 -0
- package/dist/workers/index.js +359 -0
- package/dist/workers/index.js.map +1 -0
- package/dist/workers/index.mjs +356 -0
- package/dist/workers/index.mjs.map +1 -0
- package/dist/workflow/index.d.mts +99 -0
- package/dist/workflow/index.d.ts +99 -0
- package/dist/workflow/index.js +282 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/index.mjs +279 -0
- package/dist/workflow/index.mjs.map +1 -0
- package/package.json +226 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
// src/events/index.ts
|
|
2
|
+
var JoopSubject = class {
|
|
3
|
+
_listeners = [];
|
|
4
|
+
subscribe(listener) {
|
|
5
|
+
this._listeners.push(listener);
|
|
6
|
+
return () => {
|
|
7
|
+
this._listeners = this._listeners.filter((l) => l !== listener);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
next(value) {
|
|
11
|
+
for (const listener of this._listeners) {
|
|
12
|
+
listener(value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
asObservable() {
|
|
16
|
+
return new JoopObservable((listener) => this.subscribe(listener));
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var JoopBehaviorSubject = class extends JoopSubject {
|
|
20
|
+
_value;
|
|
21
|
+
constructor(initialValue) {
|
|
22
|
+
super();
|
|
23
|
+
this._value = initialValue;
|
|
24
|
+
}
|
|
25
|
+
getValue() {
|
|
26
|
+
return this._value;
|
|
27
|
+
}
|
|
28
|
+
next(value) {
|
|
29
|
+
this._value = value;
|
|
30
|
+
super.next(value);
|
|
31
|
+
}
|
|
32
|
+
subscribe(listener) {
|
|
33
|
+
listener(this._value);
|
|
34
|
+
return super.subscribe(listener);
|
|
35
|
+
}
|
|
36
|
+
asObservable() {
|
|
37
|
+
return new JoopObservable((listener) => this.subscribe(listener));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var JoopObservable = class {
|
|
41
|
+
constructor(_subscribeFn) {
|
|
42
|
+
this._subscribeFn = _subscribeFn;
|
|
43
|
+
}
|
|
44
|
+
_subscribeFn;
|
|
45
|
+
subscribe(listener) {
|
|
46
|
+
return this._subscribeFn(listener);
|
|
47
|
+
}
|
|
48
|
+
/** Returns the current value without subscribing (only meaningful for BehaviorSubject-backed observables). */
|
|
49
|
+
getOnce() {
|
|
50
|
+
let result;
|
|
51
|
+
const unsub = this.subscribe((v) => {
|
|
52
|
+
result = v;
|
|
53
|
+
});
|
|
54
|
+
unsub();
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/network/websocket.service.ts
|
|
60
|
+
var JoopWebSocketService = class {
|
|
61
|
+
_ws = null;
|
|
62
|
+
_url = "";
|
|
63
|
+
_protocols;
|
|
64
|
+
_state$ = new JoopBehaviorSubject("closed");
|
|
65
|
+
_message$ = new JoopSubject();
|
|
66
|
+
_error$ = new JoopSubject();
|
|
67
|
+
_reconnects = 0;
|
|
68
|
+
_reconnectTimer = null;
|
|
69
|
+
_pingTimer = null;
|
|
70
|
+
_queue = [];
|
|
71
|
+
_cfg;
|
|
72
|
+
constructor(config = {}) {
|
|
73
|
+
this._cfg = {
|
|
74
|
+
reconnectMs: config.reconnectMs ?? 3e3,
|
|
75
|
+
maxReconnects: config.maxReconnects ?? 5,
|
|
76
|
+
pingIntervalMs: config.pingIntervalMs ?? 0,
|
|
77
|
+
pingMessage: config.pingMessage ?? '{"type":"ping"}'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
connect(url, protocols) {
|
|
81
|
+
this._url = url;
|
|
82
|
+
this._protocols = protocols;
|
|
83
|
+
this._reconnects = 0;
|
|
84
|
+
this._open();
|
|
85
|
+
}
|
|
86
|
+
_open() {
|
|
87
|
+
if (typeof WebSocket === "undefined") return;
|
|
88
|
+
this._state$.next("connecting");
|
|
89
|
+
try {
|
|
90
|
+
this._ws = this._protocols ? new WebSocket(this._url, this._protocols) : new WebSocket(this._url);
|
|
91
|
+
} catch {
|
|
92
|
+
this._state$.next("closed");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this._ws.onopen = () => {
|
|
96
|
+
this._state$.next("open");
|
|
97
|
+
this._reconnects = 0;
|
|
98
|
+
if (this._queue.length) {
|
|
99
|
+
for (const msg of this._queue) this._ws.send(msg);
|
|
100
|
+
this._queue = [];
|
|
101
|
+
}
|
|
102
|
+
if (this._cfg.pingIntervalMs > 0) {
|
|
103
|
+
this._pingTimer = setInterval(() => this.send(this._cfg.pingMessage), this._cfg.pingIntervalMs);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
this._ws.onmessage = (event) => {
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(event.data);
|
|
109
|
+
this._message$.next({ type: parsed?.type, data: parsed?.data ?? parsed, raw: event.data });
|
|
110
|
+
} catch {
|
|
111
|
+
this._message$.next({ raw: event.data });
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
this._ws.onerror = (event) => {
|
|
115
|
+
this._error$.next(event);
|
|
116
|
+
};
|
|
117
|
+
this._ws.onclose = () => {
|
|
118
|
+
this._state$.next("closed");
|
|
119
|
+
this._clearPing();
|
|
120
|
+
if (this._reconnects < this._cfg.maxReconnects) {
|
|
121
|
+
this._reconnects++;
|
|
122
|
+
this._reconnectTimer = setTimeout(() => this._open(), this._cfg.reconnectMs);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
disconnect() {
|
|
127
|
+
if (this._reconnectTimer) {
|
|
128
|
+
clearTimeout(this._reconnectTimer);
|
|
129
|
+
this._reconnectTimer = null;
|
|
130
|
+
}
|
|
131
|
+
this._clearPing();
|
|
132
|
+
this._reconnects = this._cfg.maxReconnects;
|
|
133
|
+
if (this._ws) {
|
|
134
|
+
this._state$.next("closing");
|
|
135
|
+
this._ws.close();
|
|
136
|
+
this._ws = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
send(data) {
|
|
140
|
+
const payload = typeof data === "string" ? data : JSON.stringify(data);
|
|
141
|
+
if (this._ws?.readyState === WebSocket.OPEN) {
|
|
142
|
+
this._ws.send(payload);
|
|
143
|
+
} else {
|
|
144
|
+
this._queue.push(payload);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Subscribe to messages of a specific type */
|
|
148
|
+
on(type) {
|
|
149
|
+
return {
|
|
150
|
+
subscribe: (fn) => this._message$.subscribe((msg) => {
|
|
151
|
+
if (msg.type === type) fn(msg.data);
|
|
152
|
+
})
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
state$() {
|
|
156
|
+
return this._state$.asObservable();
|
|
157
|
+
}
|
|
158
|
+
message$() {
|
|
159
|
+
return this._message$.asObservable();
|
|
160
|
+
}
|
|
161
|
+
error$() {
|
|
162
|
+
return this._error$.asObservable();
|
|
163
|
+
}
|
|
164
|
+
isConnected() {
|
|
165
|
+
return this._ws?.readyState === WebSocket.OPEN;
|
|
166
|
+
}
|
|
167
|
+
getState() {
|
|
168
|
+
return this._state$.getValue();
|
|
169
|
+
}
|
|
170
|
+
_clearPing() {
|
|
171
|
+
if (this._pingTimer) {
|
|
172
|
+
clearInterval(this._pingTimer);
|
|
173
|
+
this._pingTimer = null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/platform/platform.ts
|
|
179
|
+
var _webStorage = (store) => ({
|
|
180
|
+
getItem: (k) => store.getItem(k),
|
|
181
|
+
setItem: (k, v) => store.setItem(k, v),
|
|
182
|
+
removeItem: (k) => store.removeItem(k),
|
|
183
|
+
clear: () => store.clear()
|
|
184
|
+
});
|
|
185
|
+
var _MemStorageImpl = class {
|
|
186
|
+
_m = /* @__PURE__ */ new Map();
|
|
187
|
+
getItem(k) {
|
|
188
|
+
return this._m.get(k) ?? null;
|
|
189
|
+
}
|
|
190
|
+
setItem(k, v) {
|
|
191
|
+
this._m.set(k, v);
|
|
192
|
+
}
|
|
193
|
+
removeItem(k) {
|
|
194
|
+
this._m.delete(k);
|
|
195
|
+
}
|
|
196
|
+
clear() {
|
|
197
|
+
this._m.clear();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
function _detect() {
|
|
201
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") return "react-native";
|
|
202
|
+
const _proc = globalThis["process"];
|
|
203
|
+
if (typeof window === "undefined" && _proc?.versions?.node) return "node";
|
|
204
|
+
if (typeof window !== "undefined") return "web";
|
|
205
|
+
return "unknown";
|
|
206
|
+
}
|
|
207
|
+
var JoopPlatform = class {
|
|
208
|
+
static _type = _detect();
|
|
209
|
+
static _adapter = {};
|
|
210
|
+
static _memStorage = null;
|
|
211
|
+
/**
|
|
212
|
+
* Call once at app startup to configure adapters for your platform.
|
|
213
|
+
*
|
|
214
|
+
* Web (default — no call needed):
|
|
215
|
+
* JoopPlatform.init();
|
|
216
|
+
*
|
|
217
|
+
* React Native with react-native-mmkv:
|
|
218
|
+
* import { MMKV } from 'react-native-mmkv';
|
|
219
|
+
* const mmkv = new MMKV();
|
|
220
|
+
* JoopPlatform.init({ adapter: { storage: mmkv } });
|
|
221
|
+
*
|
|
222
|
+
* React Native with custom compression (fflate):
|
|
223
|
+
* import { gzip, ungzip } from 'fflate';
|
|
224
|
+
* JoopPlatform.init({
|
|
225
|
+
* adapter: {
|
|
226
|
+
* compression: {
|
|
227
|
+
* compress: (d, fmt) => new Promise((res, rej) => gzip(d, (e, r) => e ? rej(e) : res(r))),
|
|
228
|
+
* decompress: (d, fmt) => new Promise((res, rej) => ungzip(d, (e, r) => e ? rej(e) : res(r))),
|
|
229
|
+
* },
|
|
230
|
+
* },
|
|
231
|
+
* });
|
|
232
|
+
*/
|
|
233
|
+
static init(config = {}) {
|
|
234
|
+
if (config.platform) this._type = config.platform;
|
|
235
|
+
this._adapter = { ...this._adapter, ...config.adapter ?? {} };
|
|
236
|
+
}
|
|
237
|
+
/** Current detected or overridden platform type */
|
|
238
|
+
static get type() {
|
|
239
|
+
return this._type;
|
|
240
|
+
}
|
|
241
|
+
static is(type) {
|
|
242
|
+
return this._type === type;
|
|
243
|
+
}
|
|
244
|
+
static isMobile() {
|
|
245
|
+
return this._type === "react-native";
|
|
246
|
+
}
|
|
247
|
+
static isWeb() {
|
|
248
|
+
return this._type === "web";
|
|
249
|
+
}
|
|
250
|
+
static isNode() {
|
|
251
|
+
return this._type === "node";
|
|
252
|
+
}
|
|
253
|
+
/** Raw adapter config registered via init() */
|
|
254
|
+
static getAdapter() {
|
|
255
|
+
return this._adapter;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Returns the best available storage adapter:
|
|
259
|
+
* 1. Custom adapter registered via init()
|
|
260
|
+
* 2. localStorage (web)
|
|
261
|
+
* 3. In-memory fallback (Node / RN without adapter)
|
|
262
|
+
*/
|
|
263
|
+
static getStorage() {
|
|
264
|
+
if (this._adapter.storage) return this._adapter.storage;
|
|
265
|
+
if (typeof localStorage !== "undefined") return _webStorage(localStorage);
|
|
266
|
+
if (!this._memStorage) this._memStorage = new _MemStorageImpl();
|
|
267
|
+
return this._memStorage;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Returns the best available session-scoped storage:
|
|
271
|
+
* 1. Custom adapter registered via init()
|
|
272
|
+
* 2. sessionStorage (web)
|
|
273
|
+
* 3. Shared in-memory fallback
|
|
274
|
+
*/
|
|
275
|
+
static getSessionStorage() {
|
|
276
|
+
if (this._adapter.storage) return this._adapter.storage;
|
|
277
|
+
if (typeof sessionStorage !== "undefined") return _webStorage(sessionStorage);
|
|
278
|
+
if (!this._memStorage) this._memStorage = new _MemStorageImpl();
|
|
279
|
+
return this._memStorage;
|
|
280
|
+
}
|
|
281
|
+
/** What APIs are available in the current environment */
|
|
282
|
+
static capabilities() {
|
|
283
|
+
return {
|
|
284
|
+
compression: typeof CompressionStream !== "undefined" || !!this._adapter.compression,
|
|
285
|
+
workers: typeof Worker !== "undefined" || !!this._adapter.worker,
|
|
286
|
+
cryptoSubtle: !!(typeof globalThis !== "undefined" && globalThis.crypto?.subtle),
|
|
287
|
+
persistentStorage: typeof localStorage !== "undefined" || !!this._adapter.storage,
|
|
288
|
+
dom: typeof document !== "undefined",
|
|
289
|
+
serviceWorker: typeof navigator !== "undefined" && "serviceWorker" in navigator
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/** Reset to auto-detected defaults — useful between tests */
|
|
293
|
+
static _reset() {
|
|
294
|
+
this._type = _detect();
|
|
295
|
+
this._adapter = {};
|
|
296
|
+
this._memStorage = null;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/network/sse.service.ts
|
|
301
|
+
var JoopStreamingService = class {
|
|
302
|
+
_state$ = new JoopBehaviorSubject("closed");
|
|
303
|
+
_message$ = new JoopSubject();
|
|
304
|
+
_error$ = new JoopSubject();
|
|
305
|
+
_es = null;
|
|
306
|
+
_abortCtrl = null;
|
|
307
|
+
_cfg = null;
|
|
308
|
+
_reconnects = 0;
|
|
309
|
+
_reconnectTimer = null;
|
|
310
|
+
_lastEventId = "";
|
|
311
|
+
connect(config) {
|
|
312
|
+
this.disconnect();
|
|
313
|
+
this._cfg = config;
|
|
314
|
+
this._reconnects = 0;
|
|
315
|
+
this._lastEventId = config.lastEventId ?? "";
|
|
316
|
+
this._open();
|
|
317
|
+
}
|
|
318
|
+
disconnect() {
|
|
319
|
+
if (this._reconnectTimer) {
|
|
320
|
+
clearTimeout(this._reconnectTimer);
|
|
321
|
+
this._reconnectTimer = null;
|
|
322
|
+
}
|
|
323
|
+
this._es?.close();
|
|
324
|
+
this._es = null;
|
|
325
|
+
this._abortCtrl?.abort();
|
|
326
|
+
this._abortCtrl = null;
|
|
327
|
+
this._state$.next("closed");
|
|
328
|
+
}
|
|
329
|
+
state$() {
|
|
330
|
+
return this._state$.asObservable();
|
|
331
|
+
}
|
|
332
|
+
getState() {
|
|
333
|
+
return this._state$.getValue();
|
|
334
|
+
}
|
|
335
|
+
message$() {
|
|
336
|
+
return this._message$.asObservable();
|
|
337
|
+
}
|
|
338
|
+
error$() {
|
|
339
|
+
return this._error$.asObservable();
|
|
340
|
+
}
|
|
341
|
+
/** Filter messages by event type */
|
|
342
|
+
on(type) {
|
|
343
|
+
const sub = new JoopSubject();
|
|
344
|
+
this._message$.subscribe((m) => {
|
|
345
|
+
if (m.type === type || type === "message" && !m.type) sub.next(m.data);
|
|
346
|
+
});
|
|
347
|
+
return sub.asObservable();
|
|
348
|
+
}
|
|
349
|
+
_open() {
|
|
350
|
+
if (!this._cfg) return;
|
|
351
|
+
this._state$.next("connecting");
|
|
352
|
+
const { url, headers, body, method = body ? "POST" : "GET", withCredentials, reconnectMs = 3e3, maxReconnects = 10 } = this._cfg;
|
|
353
|
+
const canUseNative = method === "GET" && !headers && typeof EventSource !== "undefined" && !JoopPlatform.isMobile();
|
|
354
|
+
if (canUseNative) {
|
|
355
|
+
this._openNative(url, withCredentials ?? false, reconnectMs, maxReconnects);
|
|
356
|
+
} else {
|
|
357
|
+
this._openFetch(url, method, headers, body, reconnectMs, maxReconnects);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
_openNative(url, withCredentials, reconnectMs, maxReconnects) {
|
|
361
|
+
const es = new EventSource(url, { withCredentials });
|
|
362
|
+
this._es = es;
|
|
363
|
+
es.onopen = () => {
|
|
364
|
+
this._state$.next("open");
|
|
365
|
+
this._reconnects = 0;
|
|
366
|
+
};
|
|
367
|
+
es.onmessage = (e) => {
|
|
368
|
+
this._lastEventId = e.lastEventId ?? this._lastEventId;
|
|
369
|
+
this._message$.next({ id: e.lastEventId, type: "message", data: e.data });
|
|
370
|
+
};
|
|
371
|
+
es.onerror = () => {
|
|
372
|
+
this._state$.next("error");
|
|
373
|
+
this._error$.next(new Error("SSE connection error"));
|
|
374
|
+
es.close();
|
|
375
|
+
this._scheduleReconnect(reconnectMs, maxReconnects);
|
|
376
|
+
};
|
|
377
|
+
const origAddEventListener = es.addEventListener.bind(es);
|
|
378
|
+
origAddEventListener("*", (e) => {
|
|
379
|
+
const me = e;
|
|
380
|
+
if (me.type !== "message") this._message$.next({ id: me.lastEventId, type: me.type, data: me.data });
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
async _openFetch(url, method, headers, body, reconnectMs, maxReconnects) {
|
|
384
|
+
this._abortCtrl = new AbortController();
|
|
385
|
+
try {
|
|
386
|
+
const fetchHeaders = { Accept: "text/event-stream", "Cache-Control": "no-cache", ...headers };
|
|
387
|
+
if (this._lastEventId) fetchHeaders["Last-Event-ID"] = this._lastEventId;
|
|
388
|
+
const res = await fetch(url, {
|
|
389
|
+
method,
|
|
390
|
+
headers: fetchHeaders,
|
|
391
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
392
|
+
signal: this._abortCtrl.signal
|
|
393
|
+
});
|
|
394
|
+
if (!res.ok || !res.body) throw new Error(`SSE: ${res.status}`);
|
|
395
|
+
this._state$.next("open");
|
|
396
|
+
this._reconnects = 0;
|
|
397
|
+
await this._readStream(res.body, reconnectMs, maxReconnects);
|
|
398
|
+
} catch (e) {
|
|
399
|
+
if (e?.name !== "AbortError") {
|
|
400
|
+
this._state$.next("error");
|
|
401
|
+
this._error$.next(e instanceof Error ? e : new Error(String(e)));
|
|
402
|
+
this._scheduleReconnect(reconnectMs, maxReconnects);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
async _readStream(body, reconnectMs, maxReconnects) {
|
|
407
|
+
const reader = body.getReader();
|
|
408
|
+
const dec = new TextDecoder();
|
|
409
|
+
let buf = "";
|
|
410
|
+
let currentEvent = { id: "", type: "message", data: "" };
|
|
411
|
+
while (true) {
|
|
412
|
+
const { done, value } = await reader.read();
|
|
413
|
+
if (done) {
|
|
414
|
+
this._scheduleReconnect(reconnectMs, maxReconnects);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
buf += dec.decode(value, { stream: true });
|
|
418
|
+
const lines = buf.split("\n");
|
|
419
|
+
buf = lines.pop() ?? "";
|
|
420
|
+
for (const line of lines) {
|
|
421
|
+
if (!line) {
|
|
422
|
+
if (currentEvent.data) {
|
|
423
|
+
if (currentEvent.id) this._lastEventId = currentEvent.id;
|
|
424
|
+
this._message$.next({ ...currentEvent });
|
|
425
|
+
}
|
|
426
|
+
currentEvent = { id: "", type: "message", data: "" };
|
|
427
|
+
} else if (line.startsWith("id:")) {
|
|
428
|
+
currentEvent.id = line.slice(3).trim();
|
|
429
|
+
} else if (line.startsWith("event:")) {
|
|
430
|
+
currentEvent.type = line.slice(6).trim();
|
|
431
|
+
} else if (line.startsWith("data:")) {
|
|
432
|
+
currentEvent.data += (currentEvent.data ? "\n" : "") + line.slice(5).trim();
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
_scheduleReconnect(reconnectMs, maxReconnects) {
|
|
438
|
+
if (this._reconnects >= maxReconnects) {
|
|
439
|
+
this._state$.next("closed");
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
this._reconnects++;
|
|
443
|
+
this._reconnectTimer = setTimeout(() => {
|
|
444
|
+
if (this._cfg) this._open();
|
|
445
|
+
}, reconnectMs);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
export { JoopStreamingService, JoopWebSocketService };
|
|
450
|
+
//# sourceMappingURL=index.mjs.map
|
|
451
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/events/index.ts","../../src/network/websocket.service.ts","../../src/platform/platform.ts","../../src/network/sse.service.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export type Unsubscribe = () => void;\nexport type Listener<T> = (value: T) => void;\n\n/**\n * Lightweight pub/sub subject — emits to all current subscribers.\n * Replaces RxJS Subject without pulling in the full RxJS dependency.\n */\nexport class JoopSubject<T> {\n private _listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>): Unsubscribe {\n this._listeners.push(listener);\n return () => {\n this._listeners = this._listeners.filter(l => l !== listener);\n };\n }\n\n next(value: T): void {\n for (const listener of this._listeners) {\n listener(value);\n }\n }\n\n asObservable(): JoopObservable<T> {\n return new JoopObservable<T>(listener => this.subscribe(listener));\n }\n}\n\n/**\n * Stateful subject — replays the current value to any new subscriber immediately.\n * Replaces RxJS BehaviorSubject.\n */\nexport class JoopBehaviorSubject<T> extends JoopSubject<T> {\n private _value: T;\n\n constructor(initialValue: T) {\n super();\n this._value = initialValue;\n }\n\n getValue(): T {\n return this._value;\n }\n\n override next(value: T): void {\n this._value = value;\n super.next(value);\n }\n\n override subscribe(listener: Listener<T>): Unsubscribe {\n listener(this._value);\n return super.subscribe(listener);\n }\n\n override asObservable(): JoopObservable<T> {\n return new JoopObservable<T>(listener => this.subscribe(listener));\n }\n}\n\n/**\n * Read-only observable handle returned from asObservable().\n */\nexport class JoopObservable<T> {\n constructor(private _subscribeFn: (listener: Listener<T>) => Unsubscribe) {}\n\n subscribe(listener: Listener<T>): Unsubscribe {\n return this._subscribeFn(listener);\n }\n\n /** Returns the current value without subscribing (only meaningful for BehaviorSubject-backed observables). */\n getOnce(): T | undefined {\n let result: T | undefined;\n const unsub = this.subscribe(v => { result = v; });\n unsub();\n return result;\n }\n}\n","import { JoopSubject, JoopBehaviorSubject } from '../events';\n\nexport type JoopWsState = 'connecting' | 'open' | 'closing' | 'closed';\n\nexport interface JoopWsMessage {\n type?: string;\n data?: unknown;\n raw?: unknown;\n}\n\nexport interface JoopWsConfig {\n reconnectMs?: number;\n maxReconnects?: number;\n pingIntervalMs?: number;\n pingMessage?: string;\n}\n\nexport class JoopWebSocketService {\n private _ws: WebSocket | null = null;\n private _url = '';\n private _protocols?: string | string[];\n private _state$ = new JoopBehaviorSubject<JoopWsState>('closed');\n private _message$ = new JoopSubject<JoopWsMessage>();\n private _error$ = new JoopSubject<Event>();\n private _reconnects = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _pingTimer: ReturnType<typeof setInterval> | null = null;\n private _queue: string[] = [];\n private _cfg: Required<JoopWsConfig>;\n\n constructor(config: JoopWsConfig = {}) {\n this._cfg = {\n reconnectMs: config.reconnectMs ?? 3000,\n maxReconnects: config.maxReconnects ?? 5,\n pingIntervalMs: config.pingIntervalMs ?? 0,\n pingMessage: config.pingMessage ?? '{\"type\":\"ping\"}',\n };\n }\n\n connect(url: string, protocols?: string | string[]): void {\n this._url = url;\n this._protocols = protocols;\n this._reconnects = 0;\n this._open();\n }\n\n private _open(): void {\n if (typeof WebSocket === 'undefined') return;\n this._state$.next('connecting');\n try {\n this._ws = this._protocols ? new WebSocket(this._url, this._protocols) : new WebSocket(this._url);\n } catch {\n this._state$.next('closed');\n return;\n }\n\n this._ws.onopen = () => {\n this._state$.next('open');\n this._reconnects = 0;\n if (this._queue.length) {\n for (const msg of this._queue) this._ws!.send(msg);\n this._queue = [];\n }\n if (this._cfg.pingIntervalMs > 0) {\n this._pingTimer = setInterval(() => this.send(this._cfg.pingMessage), this._cfg.pingIntervalMs);\n }\n };\n\n this._ws.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data as string);\n this._message$.next({ type: parsed?.type, data: parsed?.data ?? parsed, raw: event.data });\n } catch {\n this._message$.next({ raw: event.data });\n }\n };\n\n this._ws.onerror = (event) => {\n this._error$.next(event);\n };\n\n this._ws.onclose = () => {\n this._state$.next('closed');\n this._clearPing();\n if (this._reconnects < this._cfg.maxReconnects) {\n this._reconnects++;\n this._reconnectTimer = setTimeout(() => this._open(), this._cfg.reconnectMs);\n }\n };\n }\n\n disconnect(): void {\n if (this._reconnectTimer) { clearTimeout(this._reconnectTimer); this._reconnectTimer = null; }\n this._clearPing();\n this._reconnects = this._cfg.maxReconnects; // prevent auto-reconnect\n if (this._ws) {\n this._state$.next('closing');\n this._ws.close();\n this._ws = null;\n }\n }\n\n send(data: string | Record<string, unknown>): void {\n const payload = typeof data === 'string' ? data : JSON.stringify(data);\n if (this._ws?.readyState === WebSocket.OPEN) {\n this._ws.send(payload);\n } else {\n this._queue.push(payload);\n }\n }\n\n /** Subscribe to messages of a specific type */\n on(type: string): { subscribe: (fn: (data: unknown) => void) => () => void } {\n return {\n subscribe: (fn) => this._message$.subscribe(msg => { if (msg.type === type) fn(msg.data); }),\n };\n }\n\n state$() { return this._state$.asObservable(); }\n message$() { return this._message$.asObservable(); }\n error$() { return this._error$.asObservable(); }\n isConnected(): boolean { return this._ws?.readyState === WebSocket.OPEN; }\n getState(): JoopWsState { return this._state$.getValue(); }\n\n private _clearPing(): void {\n if (this._pingTimer) { clearInterval(this._pingTimer); this._pingTimer = null; }\n }\n}\n","import type { JoopPlatformAdapterConfig, JoopStorageAdapter } from './adapters';\n\nexport type JoopPlatformType = 'web' | 'react-native' | 'node' | 'unknown';\n\nexport interface JoopCapabilities {\n compression: boolean;\n workers: boolean;\n cryptoSubtle: boolean;\n persistentStorage: boolean;\n dom: boolean;\n serviceWorker: boolean;\n}\n\nexport interface JoopPlatformConfig {\n adapter?: JoopPlatformAdapterConfig;\n /** Override auto-detection — useful in tests or SSR */\n platform?: JoopPlatformType;\n}\n\n// ---------------------------------------------------------------------------\n// Built-in storage adapters\n// ---------------------------------------------------------------------------\n\nconst _webStorage = (store: Storage): JoopStorageAdapter => ({\n getItem: (k) => store.getItem(k),\n setItem: (k, v) => store.setItem(k, v),\n removeItem: (k) => store.removeItem(k),\n clear: () => store.clear(),\n});\n\nclass _MemStorageImpl implements JoopStorageAdapter {\n private _m = new Map<string, string>();\n getItem(k: string) { return this._m.get(k) ?? null; }\n setItem(k: string, v: string) { this._m.set(k, v); }\n removeItem(k: string) { this._m.delete(k); }\n clear() { this._m.clear(); }\n}\n\n// ---------------------------------------------------------------------------\n// Platform detection\n// ---------------------------------------------------------------------------\n\nfunction _detect(): JoopPlatformType {\n // React Native sets navigator.product = 'ReactNative'\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') return 'react-native';\n // Node.js: no window, has globalThis.process.versions.node\n const _proc = (globalThis as Record<string, unknown>)['process'] as { versions?: { node?: string } } | undefined;\n if (typeof window === 'undefined' && _proc?.versions?.node) return 'node';\n // Browser\n if (typeof window !== 'undefined') return 'web';\n return 'unknown';\n}\n\n// ---------------------------------------------------------------------------\n// JoopPlatform\n// ---------------------------------------------------------------------------\n\nexport class JoopPlatform {\n private static _type: JoopPlatformType = _detect();\n private static _adapter: JoopPlatformAdapterConfig = {};\n private static _memStorage: JoopStorageAdapter | null = null;\n\n /**\n * Call once at app startup to configure adapters for your platform.\n *\n * Web (default — no call needed):\n * JoopPlatform.init();\n *\n * React Native with react-native-mmkv:\n * import { MMKV } from 'react-native-mmkv';\n * const mmkv = new MMKV();\n * JoopPlatform.init({ adapter: { storage: mmkv } });\n *\n * React Native with custom compression (fflate):\n * import { gzip, ungzip } from 'fflate';\n * JoopPlatform.init({\n * adapter: {\n * compression: {\n * compress: (d, fmt) => new Promise((res, rej) => gzip(d, (e, r) => e ? rej(e) : res(r))),\n * decompress: (d, fmt) => new Promise((res, rej) => ungzip(d, (e, r) => e ? rej(e) : res(r))),\n * },\n * },\n * });\n */\n static init(config: JoopPlatformConfig = {}): void {\n if (config.platform) this._type = config.platform;\n this._adapter = { ...this._adapter, ...(config.adapter ?? {}) };\n }\n\n /** Current detected or overridden platform type */\n static get type(): JoopPlatformType { return this._type; }\n\n static is(type: JoopPlatformType): boolean { return this._type === type; }\n static isMobile(): boolean { return this._type === 'react-native'; }\n static isWeb(): boolean { return this._type === 'web'; }\n static isNode(): boolean { return this._type === 'node'; }\n\n /** Raw adapter config registered via init() */\n static getAdapter(): JoopPlatformAdapterConfig { return this._adapter; }\n\n /**\n * Returns the best available storage adapter:\n * 1. Custom adapter registered via init()\n * 2. localStorage (web)\n * 3. In-memory fallback (Node / RN without adapter)\n */\n static getStorage(): JoopStorageAdapter {\n if (this._adapter.storage) return this._adapter.storage;\n if (typeof localStorage !== 'undefined') return _webStorage(localStorage);\n if (!this._memStorage) this._memStorage = new _MemStorageImpl();\n return this._memStorage;\n }\n\n /**\n * Returns the best available session-scoped storage:\n * 1. Custom adapter registered via init()\n * 2. sessionStorage (web)\n * 3. Shared in-memory fallback\n */\n static getSessionStorage(): JoopStorageAdapter {\n if (this._adapter.storage) return this._adapter.storage;\n if (typeof sessionStorage !== 'undefined') return _webStorage(sessionStorage);\n if (!this._memStorage) this._memStorage = new _MemStorageImpl();\n return this._memStorage;\n }\n\n /** What APIs are available in the current environment */\n static capabilities(): JoopCapabilities {\n return {\n compression: typeof CompressionStream !== 'undefined' || !!this._adapter.compression,\n workers: typeof Worker !== 'undefined' || !!this._adapter.worker,\n cryptoSubtle: !!(typeof globalThis !== 'undefined' && (globalThis as typeof globalThis & { crypto?: Crypto }).crypto?.subtle),\n persistentStorage: typeof localStorage !== 'undefined' || !!this._adapter.storage,\n dom: typeof document !== 'undefined',\n serviceWorker: typeof navigator !== 'undefined' && 'serviceWorker' in navigator,\n };\n }\n\n /** Reset to auto-detected defaults — useful between tests */\n static _reset(): void {\n this._type = _detect();\n this._adapter = {};\n this._memStorage = null;\n }\n}\n","import { JoopBehaviorSubject, JoopSubject } from '../events';\nimport { JoopPlatform } from '../platform/platform';\n\nexport type JoopSSEState = 'connecting' | 'open' | 'closed' | 'error';\n\nexport interface JoopSSEConfig {\n url: string;\n headers?: Record<string, string>;\n body?: unknown; // if set, uses fetch POST (EventSource only supports GET)\n method?: 'GET' | 'POST';\n reconnectMs?: number;\n maxReconnects?: number;\n withCredentials?: boolean;\n lastEventId?: string;\n}\n\nexport interface JoopSSEMessage {\n id?: string;\n type: string;\n data: string;\n}\n\nexport class JoopStreamingService {\n private _state$ = new JoopBehaviorSubject<JoopSSEState>('closed');\n private _message$ = new JoopSubject<JoopSSEMessage>();\n private _error$ = new JoopSubject<Error>();\n private _es: EventSource | null = null;\n private _abortCtrl: AbortController | null = null;\n private _cfg: JoopSSEConfig | null = null;\n private _reconnects = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _lastEventId = '';\n\n connect(config: JoopSSEConfig): void {\n this.disconnect();\n this._cfg = config;\n this._reconnects = 0;\n this._lastEventId = config.lastEventId ?? '';\n this._open();\n }\n\n disconnect(): void {\n if (this._reconnectTimer) { clearTimeout(this._reconnectTimer); this._reconnectTimer = null; }\n this._es?.close(); this._es = null;\n this._abortCtrl?.abort(); this._abortCtrl = null;\n this._state$.next('closed');\n }\n\n state$() { return this._state$.asObservable(); }\n getState(): JoopSSEState { return this._state$.getValue(); }\n message$() { return this._message$.asObservable(); }\n error$() { return this._error$.asObservable(); }\n\n /** Filter messages by event type */\n on(type: string) {\n const sub = new JoopSubject<string>();\n this._message$.subscribe(m => { if (m.type === type || (type === 'message' && !m.type)) sub.next(m.data); });\n return sub.asObservable();\n }\n\n private _open(): void {\n if (!this._cfg) return;\n this._state$.next('connecting');\n const { url, headers, body, method = body ? 'POST' : 'GET', withCredentials, reconnectMs = 3000, maxReconnects = 10 } = this._cfg;\n\n // Use native EventSource only on web with a plain GET (no custom headers/body).\n // React Native does not have EventSource — always use the fetch-based path there.\n const canUseNative = method === 'GET' && !headers && typeof EventSource !== 'undefined' && !JoopPlatform.isMobile();\n if (canUseNative) {\n this._openNative(url, withCredentials ?? false, reconnectMs, maxReconnects);\n } else {\n this._openFetch(url, method, headers, body, reconnectMs, maxReconnects);\n }\n }\n\n private _openNative(url: string, withCredentials: boolean, reconnectMs: number, maxReconnects: number): void {\n const es = new EventSource(url, { withCredentials });\n this._es = es;\n es.onopen = () => { this._state$.next('open'); this._reconnects = 0; };\n es.onmessage = (e) => { this._lastEventId = e.lastEventId ?? this._lastEventId; this._message$.next({ id: e.lastEventId, type: 'message', data: e.data }); };\n es.onerror = () => {\n this._state$.next('error');\n this._error$.next(new Error('SSE connection error'));\n es.close();\n this._scheduleReconnect(reconnectMs, maxReconnects);\n };\n // Custom event types\n const origAddEventListener = es.addEventListener.bind(es);\n origAddEventListener('*', (e: Event) => {\n const me = e as MessageEvent;\n if (me.type !== 'message') this._message$.next({ id: me.lastEventId, type: me.type, data: me.data });\n });\n }\n\n private async _openFetch(url: string, method: string, headers: Record<string, string> | undefined, body: unknown, reconnectMs: number, maxReconnects: number): Promise<void> {\n this._abortCtrl = new AbortController();\n try {\n const fetchHeaders: Record<string, string> = { Accept: 'text/event-stream', 'Cache-Control': 'no-cache', ...headers };\n if (this._lastEventId) fetchHeaders['Last-Event-ID'] = this._lastEventId;\n const res = await fetch(url, {\n method, headers: fetchHeaders,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: this._abortCtrl.signal,\n });\n if (!res.ok || !res.body) throw new Error(`SSE: ${res.status}`);\n this._state$.next('open'); this._reconnects = 0;\n await this._readStream(res.body, reconnectMs, maxReconnects);\n } catch (e) {\n if ((e as Error)?.name !== 'AbortError') {\n this._state$.next('error');\n this._error$.next(e instanceof Error ? e : new Error(String(e)));\n this._scheduleReconnect(reconnectMs, maxReconnects);\n }\n }\n }\n\n private async _readStream(body: ReadableStream<Uint8Array>, reconnectMs: number, maxReconnects: number): Promise<void> {\n const reader = body.getReader();\n const dec = new TextDecoder();\n let buf = '';\n let currentEvent = { id: '', type: 'message', data: '' };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) { this._scheduleReconnect(reconnectMs, maxReconnects); break; }\n buf += dec.decode(value, { stream: true });\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n for (const line of lines) {\n if (!line) {\n // Dispatch event\n if (currentEvent.data) {\n if (currentEvent.id) this._lastEventId = currentEvent.id;\n this._message$.next({ ...currentEvent });\n }\n currentEvent = { id: '', type: 'message', data: '' };\n } else if (line.startsWith('id:')) { currentEvent.id = line.slice(3).trim(); }\n else if (line.startsWith('event:')) { currentEvent.type = line.slice(6).trim(); }\n else if (line.startsWith('data:')) { currentEvent.data += (currentEvent.data ? '\\n' : '') + line.slice(5).trim(); }\n }\n }\n }\n\n private _scheduleReconnect(reconnectMs: number, maxReconnects: number): void {\n if (this._reconnects >= maxReconnects) { this._state$.next('closed'); return; }\n this._reconnects++;\n this._reconnectTimer = setTimeout(() => { if (this._cfg) this._open(); }, reconnectMs);\n }\n}\n"]}
|