@remember-web/sse 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +238 -0
- package/dist/src/core/BaseSseClient.cjs.js +97 -0
- package/dist/src/core/BaseSseClient.cjs.js.map +1 -0
- package/dist/src/core/BaseSseClient.d.ts +33 -0
- package/dist/src/core/BaseSseClient.d.ts.map +1 -0
- package/dist/src/core/BaseSseClient.esm.js +89 -0
- package/dist/src/core/BaseSseClient.esm.js.map +1 -0
- package/dist/src/core/SseClient.cjs.js +382 -0
- package/dist/src/core/SseClient.cjs.js.map +1 -0
- package/dist/src/core/SseClient.d.ts +24 -0
- package/dist/src/core/SseClient.d.ts.map +1 -0
- package/dist/src/core/SseClient.esm.js +370 -0
- package/dist/src/core/SseClient.esm.js.map +1 -0
- package/dist/src/core/backoff.cjs.js +30 -0
- package/dist/src/core/backoff.cjs.js.map +1 -0
- package/dist/src/core/backoff.d.ts +12 -0
- package/dist/src/core/backoff.d.ts.map +1 -0
- package/dist/src/core/backoff.esm.js +28 -0
- package/dist/src/core/backoff.esm.js.map +1 -0
- package/dist/src/core/emitter.cjs.js +57 -0
- package/dist/src/core/emitter.cjs.js.map +1 -0
- package/dist/src/core/emitter.d.ts +14 -0
- package/dist/src/core/emitter.d.ts.map +1 -0
- package/dist/src/core/emitter.esm.js +51 -0
- package/dist/src/core/emitter.esm.js.map +1 -0
- package/dist/src/core/eventNames.cjs.js +64 -0
- package/dist/src/core/eventNames.cjs.js.map +1 -0
- package/dist/src/core/eventNames.d.ts +31 -0
- package/dist/src/core/eventNames.d.ts.map +1 -0
- package/dist/src/core/eventNames.esm.js +54 -0
- package/dist/src/core/eventNames.esm.js.map +1 -0
- package/dist/src/core/retryWait.cjs.js +20 -0
- package/dist/src/core/retryWait.cjs.js.map +1 -0
- package/dist/src/core/retryWait.d.ts +6 -0
- package/dist/src/core/retryWait.d.ts.map +1 -0
- package/dist/src/core/retryWait.esm.js +18 -0
- package/dist/src/core/retryWait.esm.js.map +1 -0
- package/dist/src/core/statusMachine.cjs.js +56 -0
- package/dist/src/core/statusMachine.cjs.js.map +1 -0
- package/dist/src/core/statusMachine.d.ts +9 -0
- package/dist/src/core/statusMachine.d.ts.map +1 -0
- package/dist/src/core/statusMachine.esm.js +54 -0
- package/dist/src/core/statusMachine.esm.js.map +1 -0
- package/dist/src/core/stream.cjs.js +136 -0
- package/dist/src/core/stream.cjs.js.map +1 -0
- package/dist/src/core/stream.d.ts +42 -0
- package/dist/src/core/stream.d.ts.map +1 -0
- package/dist/src/core/stream.esm.js +128 -0
- package/dist/src/core/stream.esm.js.map +1 -0
- package/dist/src/core/ticket.cjs.js +237 -0
- package/dist/src/core/ticket.cjs.js.map +1 -0
- package/dist/src/core/ticket.d.ts +31 -0
- package/dist/src/core/ticket.d.ts.map +1 -0
- package/dist/src/core/ticket.esm.js +220 -0
- package/dist/src/core/ticket.esm.js.map +1 -0
- package/dist/src/core/types.cjs.js +54 -0
- package/dist/src/core/types.cjs.js.map +1 -0
- package/dist/src/core/types.d.ts +131 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.esm.js +50 -0
- package/dist/src/core/types.esm.js.map +1 -0
- package/dist/src/core/wire.cjs.js +29 -0
- package/dist/src/core/wire.cjs.js.map +1 -0
- package/dist/src/core/wire.d.ts +7 -0
- package/dist/src/core/wire.d.ts.map +1 -0
- package/dist/src/core/wire.esm.js +23 -0
- package/dist/src/core/wire.esm.js.map +1 -0
- package/dist/src/createSseClient.cjs.js +30 -0
- package/dist/src/createSseClient.cjs.js.map +1 -0
- package/dist/src/createSseClient.d.ts +8 -0
- package/dist/src/createSseClient.d.ts.map +1 -0
- package/dist/src/createSseClient.esm.js +28 -0
- package/dist/src/createSseClient.esm.js.map +1 -0
- package/dist/src/index.cjs.js +10 -0
- package/dist/src/index.cjs.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.esm.js +3 -0
- package/dist/src/index.esm.js.map +1 -0
- package/dist/src/react/createSseContext.cjs.js +130 -0
- package/dist/src/react/createSseContext.cjs.js.map +1 -0
- package/dist/src/react/createSseContext.d.ts +32 -0
- package/dist/src/react/createSseContext.d.ts.map +1 -0
- package/dist/src/react/createSseContext.esm.js +122 -0
- package/dist/src/react/createSseContext.esm.js.map +1 -0
- package/dist/src/react/index.cjs.js +8 -0
- package/dist/src/react/index.cjs.js.map +1 -0
- package/dist/src/react/index.d.ts +3 -0
- package/dist/src/react/index.d.ts.map +1 -0
- package/dist/src/react/index.esm.js +2 -0
- package/dist/src/react/index.esm.js.map +1 -0
- package/dist/src/testing/doubles.d.ts +50 -0
- package/dist/src/testing/doubles.d.ts.map +1 -0
- package/dist/src/testing/harness.d.ts +43 -0
- package/dist/src/testing/harness.d.ts.map +1 -0
- package/dist/src/testing/index.d.ts +3 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/worker/WorkerSseClient.cjs.js +240 -0
- package/dist/src/worker/WorkerSseClient.cjs.js.map +1 -0
- package/dist/src/worker/WorkerSseClient.d.ts +23 -0
- package/dist/src/worker/WorkerSseClient.d.ts.map +1 -0
- package/dist/src/worker/WorkerSseClient.esm.js +228 -0
- package/dist/src/worker/WorkerSseClient.esm.js.map +1 -0
- package/dist/src/worker/hub.d.ts +19 -0
- package/dist/src/worker/hub.d.ts.map +1 -0
- package/dist/src/worker/portRegistry.d.ts +41 -0
- package/dist/src/worker/portRegistry.d.ts.map +1 -0
- package/dist/src/worker/protocol.d.ts +63 -0
- package/dist/src/worker/protocol.d.ts.map +1 -0
- package/dist/src/worker/sse.worker.d.ts +2 -0
- package/dist/src/worker/sse.worker.d.ts.map +1 -0
- package/dist/src/worker/ticketDelegate.d.ts +16 -0
- package/dist/src/worker/ticketDelegate.d.ts.map +1 -0
- package/dist/src/worker/workerConnection.d.ts +28 -0
- package/dist/src/worker/workerConnection.d.ts.map +1 -0
- package/dist/worker/sse.worker.js +1865 -0
- package/dist/worker/sse.worker.js.map +1 -0
- package/package.json +87 -0
- package/src/core/BaseSseClient.test.ts +142 -0
- package/src/core/BaseSseClient.ts +103 -0
- package/src/core/SseClient.connection.test.ts +863 -0
- package/src/core/SseClient.scenarios.test.ts +164 -0
- package/src/core/SseClient.test.ts +63 -0
- package/src/core/SseClient.ts +310 -0
- package/src/core/backoff.test.ts +43 -0
- package/src/core/backoff.ts +39 -0
- package/src/core/emitter.ts +56 -0
- package/src/core/eventNames.ts +53 -0
- package/src/core/retryWait.ts +18 -0
- package/src/core/statusMachine.ts +56 -0
- package/src/core/stream.ts +141 -0
- package/src/core/ticket.ts +146 -0
- package/src/core/types.ts +182 -0
- package/src/core/wire.ts +23 -0
- package/src/createSseClient.test.ts +82 -0
- package/src/createSseClient.ts +34 -0
- package/src/index.ts +21 -0
- package/src/react/SseProvider.test.tsx +129 -0
- package/src/react/createSseContext.test.ts +47 -0
- package/src/react/createSseContext.tsx +140 -0
- package/src/react/index.ts +2 -0
- package/src/react/useSseEvent.test.tsx +71 -0
- package/src/react/useSseStatus.test.tsx +40 -0
- package/src/testing/doubles.ts +128 -0
- package/src/testing/harness.ts +82 -0
- package/src/testing/index.ts +3 -0
- package/src/worker/SseClientLike.contract.test.ts +270 -0
- package/src/worker/WorkerSseClient.scenarios.test.ts +179 -0
- package/src/worker/WorkerSseClient.test.ts +377 -0
- package/src/worker/WorkerSseClient.ts +190 -0
- package/src/worker/hub.connection.test.ts +1023 -0
- package/src/worker/hub.test.ts +213 -0
- package/src/worker/hub.ts +120 -0
- package/src/worker/portRegistry.test.ts +59 -0
- package/src/worker/portRegistry.ts +86 -0
- package/src/worker/protocol.ts +80 -0
- package/src/worker/sse.worker.ts +19 -0
- package/src/worker/ticketDelegate.test.ts +99 -0
- package/src/worker/ticketDelegate.ts +80 -0
- package/src/worker/workerConnection.ts +134 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator');
|
|
4
|
+
var _classCallCheck = require('@babel/runtime/helpers/classCallCheck');
|
|
5
|
+
var _createClass = require('@babel/runtime/helpers/createClass');
|
|
6
|
+
var _possibleConstructorReturn = require('@babel/runtime/helpers/possibleConstructorReturn');
|
|
7
|
+
var _getPrototypeOf = require('@babel/runtime/helpers/getPrototypeOf');
|
|
8
|
+
var _inherits = require('@babel/runtime/helpers/inherits');
|
|
9
|
+
var _regeneratorRuntime = require('@babel/runtime/regenerator');
|
|
10
|
+
var BaseSseClient = require('../core/BaseSseClient.cjs.js');
|
|
11
|
+
var eventNames = require('../core/eventNames.cjs.js');
|
|
12
|
+
var ticket = require('../core/ticket.cjs.js');
|
|
13
|
+
var types = require('../core/types.cjs.js');
|
|
14
|
+
|
|
15
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
|
|
17
|
+
var _asyncToGenerator__default = /*#__PURE__*/_interopDefault(_asyncToGenerator);
|
|
18
|
+
var _classCallCheck__default = /*#__PURE__*/_interopDefault(_classCallCheck);
|
|
19
|
+
var _createClass__default = /*#__PURE__*/_interopDefault(_createClass);
|
|
20
|
+
var _possibleConstructorReturn__default = /*#__PURE__*/_interopDefault(_possibleConstructorReturn);
|
|
21
|
+
var _getPrototypeOf__default = /*#__PURE__*/_interopDefault(_getPrototypeOf);
|
|
22
|
+
var _inherits__default = /*#__PURE__*/_interopDefault(_inherits);
|
|
23
|
+
var _regeneratorRuntime__default = /*#__PURE__*/_interopDefault(_regeneratorRuntime);
|
|
24
|
+
|
|
25
|
+
function _callSuper(t, o, e) { return o = _getPrototypeOf__default.default(o), _possibleConstructorReturn__default.default(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf__default.default(t).constructor) : o.apply(t, e)); }
|
|
26
|
+
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
27
|
+
function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
|
|
28
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
29
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
30
|
+
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
|
|
31
|
+
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
|
|
32
|
+
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @description SseClient와 같은 시그니처의 탭 측 프록시 — 연결은 SharedWorker가 소유합니다.
|
|
36
|
+
* 워커와 주고받는 메시지의 방향·배선 현황은 protocol.ts의 지도를 보세요.
|
|
37
|
+
*
|
|
38
|
+
* 프록시는 상태를 소유하지 않고 워커의 status 통지를 반영합니다 — 스스로 status를 정하는 건
|
|
39
|
+
* disconnect의 closed와 워커 생성 실패의 fatal뿐이며, (재)connect 직후에는 워커 스냅샷이
|
|
40
|
+
* 도착할 때까지 직전 상태가 잠시 유지됩니다.
|
|
41
|
+
*
|
|
42
|
+
* 얼어붙은 탭에 복귀 절차는 없습니다 — 밀린 메시지는 깨어날 때 그대로 받습니다.
|
|
43
|
+
* 전원이 침묵해 워커가 접은 연결만 connect()가 다시 엽니다 (배선은 attachAutoResume).
|
|
44
|
+
*/
|
|
45
|
+
var _sentSubscriptions = /*#__PURE__*/new WeakMap();
|
|
46
|
+
var _status = /*#__PURE__*/new WeakMap();
|
|
47
|
+
var _port = /*#__PURE__*/new WeakMap();
|
|
48
|
+
var _WorkerSseClient_brand = /*#__PURE__*/new WeakSet();
|
|
49
|
+
var WorkerSseClient = /*#__PURE__*/function (_BaseSseClient) {
|
|
50
|
+
function WorkerSseClient(options) {
|
|
51
|
+
var _this;
|
|
52
|
+
_classCallCheck__default.default(this, WorkerSseClient);
|
|
53
|
+
_this = _callSuper(this, WorkerSseClient, [options.logger]);
|
|
54
|
+
// 워커는 baseUrl을 모르므로 탭이 알려줍니다 — 이미 연결이 서 있으면 워커가 무시합니다
|
|
55
|
+
_classPrivateMethodInitSpec(_this, _WorkerSseClient_brand);
|
|
56
|
+
// 워커의 구독 장부(hub)는 탭 구분 없는 전역 union이라, 같은 이벤트는 탭당 한 번만 알리면 됩니다
|
|
57
|
+
_classPrivateFieldInitSpec(_this, _sentSubscriptions, new Set());
|
|
58
|
+
_classPrivateFieldInitSpec(_this, _status, 'idle');
|
|
59
|
+
_classPrivateFieldInitSpec(_this, _port, null);
|
|
60
|
+
_this.options = options;
|
|
61
|
+
return _this;
|
|
62
|
+
}
|
|
63
|
+
_inherits__default.default(WorkerSseClient, _BaseSseClient);
|
|
64
|
+
return _createClass__default.default(WorkerSseClient, [{
|
|
65
|
+
key: "status",
|
|
66
|
+
get: function get() {
|
|
67
|
+
return _classPrivateFieldGet(_status, this);
|
|
68
|
+
}
|
|
69
|
+
}, {
|
|
70
|
+
key: "connect",
|
|
71
|
+
value: function connect() {
|
|
72
|
+
var _this2 = this;
|
|
73
|
+
// 팩토리 없는 생성은 방어적 no-op — 정상 경로(createSseClient)는 항상 팩토리와 함께 이 클래스를 고릅니다
|
|
74
|
+
var factory = this.options.sharedWorker;
|
|
75
|
+
if (factory === undefined) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// 직결 클라이언트와 같은 회복 계약 — fatal은 disconnect()로 정리한 뒤에만 다시 시도할 수 있습니다
|
|
79
|
+
if (_classPrivateFieldGet(_status, this) === 'fatal') {
|
|
80
|
+
var _this$options$logger;
|
|
81
|
+
(_this$options$logger = this.options.logger) === null || _this$options$logger === void 0 || _this$options$logger.warn('sse: fatal 상태에서는 connect()가 무시됩니다 — disconnect()로 정리한 뒤 다시 connect() 하세요');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// 연결이 접혔는지(전원 침묵)는 탭의 status로 알 수 없습니다 — 거르지 않고 매번 보냅니다
|
|
85
|
+
if (_classPrivateFieldGet(_port, this) !== null) {
|
|
86
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _requestConnection).call(this);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
var worker = _assertClassBrand(_WorkerSseClient_brand, this, _createWorker).call(this, factory);
|
|
90
|
+
if (worker === null) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
var port = worker.port;
|
|
94
|
+
_classPrivateFieldSet(_port, this, port);
|
|
95
|
+
port.onmessage = function (event) {
|
|
96
|
+
var isSamePort = _classPrivateFieldGet(_port, _this2) === port;
|
|
97
|
+
if (!isSamePort) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
_assertClassBrand(_WorkerSseClient_brand, _this2, _handleWorkerMessage).call(_this2, event.data, port);
|
|
101
|
+
};
|
|
102
|
+
port.start();
|
|
103
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _requestConnection).call(this);
|
|
104
|
+
this.emitter.handlerTypes().forEach(function (type) {
|
|
105
|
+
return _assertClassBrand(_WorkerSseClient_brand, _this2, _sendSubscribe).call(_this2, type);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}, {
|
|
109
|
+
key: "disconnect",
|
|
110
|
+
value: function disconnect() {
|
|
111
|
+
var _classPrivateFieldGet2;
|
|
112
|
+
// 이 탭만 해제합니다 — 마지막 탭 판단과 연결 정리는 워커 몫이라 close를 상태 알림보다 먼저 보냅니다
|
|
113
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
114
|
+
kind: 'close'
|
|
115
|
+
});
|
|
116
|
+
(_classPrivateFieldGet2 = _classPrivateFieldGet(_port, this)) === null || _classPrivateFieldGet2 === void 0 || _classPrivateFieldGet2.close();
|
|
117
|
+
_classPrivateFieldSet(_port, this, null);
|
|
118
|
+
_classPrivateFieldGet(_sentSubscriptions, this).clear();
|
|
119
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _setStatus).call(this, 'closed');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 포트가 없으면 조용히 넘어갑니다 — connect()가 그때까지 모인 구독을 한 번에 보냅니다
|
|
123
|
+
}, {
|
|
124
|
+
key: "registerSubscription",
|
|
125
|
+
value: function registerSubscription(type) {
|
|
126
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _sendSubscribe).call(this, type);
|
|
127
|
+
}
|
|
128
|
+
}]);
|
|
129
|
+
}(BaseSseClient.BaseSseClient);
|
|
130
|
+
function _requestConnection() {
|
|
131
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
132
|
+
kind: 'init',
|
|
133
|
+
baseUrl: this.options.baseUrl
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// 팩토리는 소비처 코드고 new SharedWorker도 URL 오류·CSP 차단 시 동기로 던집니다 — 직결 클라이언트의 EventSource 생성 실패와 같은 fatal 처리
|
|
137
|
+
function _createWorker(factory) {
|
|
138
|
+
try {
|
|
139
|
+
return factory();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
var _this$options$logger2;
|
|
142
|
+
(_this$options$logger2 = this.options.logger) === null || _this$options$logger2 === void 0 || _this$options$logger2.error('sse: SharedWorker 생성 실패 — 워커 URL·CSP를 확인하세요', error);
|
|
143
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _setStatus).call(this, 'fatal');
|
|
144
|
+
this.emitter.emit(eventNames.SDK_EVENTS.fatal, {
|
|
145
|
+
cause: 'blocked'
|
|
146
|
+
});
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function _sendSubscribe(type) {
|
|
151
|
+
if (_classPrivateFieldGet(_port, this) === null || _classPrivateFieldGet(_sentSubscriptions, this).has(type)) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (!eventNames.isSubscribableEvent(type)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
_classPrivateFieldGet(_sentSubscriptions, this).add(type);
|
|
158
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
159
|
+
kind: 'subscribe',
|
|
160
|
+
event: type
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function _handleWorkerMessage(data, port) {
|
|
164
|
+
var message = data;
|
|
165
|
+
switch (message === null || message === void 0 ? void 0 : message.kind) {
|
|
166
|
+
case 'status':
|
|
167
|
+
// 포트 너머는 신뢰 경계 — 검증 없이 반영하면 status 게터의 타입 보증이 깨집니다
|
|
168
|
+
if (types.isSseStatus(message.status)) {
|
|
169
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _setStatus).call(this, message.status);
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
case 'event':
|
|
173
|
+
if (typeof message.event === 'string') {
|
|
174
|
+
this.emitter.emit(message.event, message.payload);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
case 'ping':
|
|
178
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
179
|
+
kind: 'pong'
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
case 'ticket-request':
|
|
183
|
+
// 토큰은 탭에만 있습니다 — 발급까지 여기서 하고 워커에는 결과만 넘깁니다
|
|
184
|
+
void _assertClassBrand(_WorkerSseClient_brand, this, _issueTicketForWorker).call(this, port);
|
|
185
|
+
return;
|
|
186
|
+
default:
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function _issueTicketForWorker(_x) {
|
|
191
|
+
return _issueTicketForWorker2.apply(this, arguments);
|
|
192
|
+
}
|
|
193
|
+
function _issueTicketForWorker2() {
|
|
194
|
+
_issueTicketForWorker2 = _asyncToGenerator__default.default(/*#__PURE__*/_regeneratorRuntime__default.default.mark(function _callee(port) {
|
|
195
|
+
var result, ticketResponse;
|
|
196
|
+
return _regeneratorRuntime__default.default.wrap(function _callee$(_context) {
|
|
197
|
+
while (1) switch (_context.prev = _context.next) {
|
|
198
|
+
case 0:
|
|
199
|
+
_context.next = 2;
|
|
200
|
+
return ticket.issueTicketWithTimeout(this.options);
|
|
201
|
+
case 2:
|
|
202
|
+
result = _context.sent;
|
|
203
|
+
if (!(_classPrivateFieldGet(_port, this) !== port)) {
|
|
204
|
+
_context.next = 5;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
return _context.abrupt("return");
|
|
208
|
+
case 5:
|
|
209
|
+
ticketResponse = result.ok ? {
|
|
210
|
+
kind: 'ticket-response',
|
|
211
|
+
ok: true,
|
|
212
|
+
ticket: result.ticket
|
|
213
|
+
} : {
|
|
214
|
+
kind: 'ticket-response',
|
|
215
|
+
ok: false,
|
|
216
|
+
error: result.error
|
|
217
|
+
};
|
|
218
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, ticketResponse);
|
|
219
|
+
case 7:
|
|
220
|
+
case "end":
|
|
221
|
+
return _context.stop();
|
|
222
|
+
}
|
|
223
|
+
}, _callee, this);
|
|
224
|
+
}));
|
|
225
|
+
return _issueTicketForWorker2.apply(this, arguments);
|
|
226
|
+
}
|
|
227
|
+
function _post(message) {
|
|
228
|
+
var _classPrivateFieldGet3;
|
|
229
|
+
(_classPrivateFieldGet3 = _classPrivateFieldGet(_port, this)) === null || _classPrivateFieldGet3 === void 0 || _classPrivateFieldGet3.postMessage(message);
|
|
230
|
+
}
|
|
231
|
+
function _setStatus(next) {
|
|
232
|
+
if (_classPrivateFieldGet(_status, this) === next) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
_classPrivateFieldSet(_status, this, next);
|
|
236
|
+
this.emitter.emitStatus(next);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
exports.WorkerSseClient = WorkerSseClient;
|
|
240
|
+
//# sourceMappingURL=WorkerSseClient.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkerSseClient.cjs.js","sources":["../../../src/worker/WorkerSseClient.ts"],"sourcesContent":["import type {\n Incoming,\n TabToWorkerMessage,\n TicketResponseMessage,\n WorkerToTabMessage,\n} from './protocol';\n\nimport { BaseSseClient } from '../core/BaseSseClient';\nimport { isSubscribableEvent, SDK_EVENTS } from '../core/eventNames';\nimport { issueTicketWithTimeout } from '../core/ticket';\nimport type {\n FatalPayload,\n SseClientOptions,\n SseEventMap,\n SseStatus,\n} from '../core/types';\nimport { isSseStatus } from '../core/types';\n\n/**\n * @description SseClient와 같은 시그니처의 탭 측 프록시 — 연결은 SharedWorker가 소유합니다.\n * 워커와 주고받는 메시지의 방향·배선 현황은 protocol.ts의 지도를 보세요.\n *\n * 프록시는 상태를 소유하지 않고 워커의 status 통지를 반영합니다 — 스스로 status를 정하는 건\n * disconnect의 closed와 워커 생성 실패의 fatal뿐이며, (재)connect 직후에는 워커 스냅샷이\n * 도착할 때까지 직전 상태가 잠시 유지됩니다.\n *\n * 얼어붙은 탭에 복귀 절차는 없습니다 — 밀린 메시지는 깨어날 때 그대로 받습니다.\n * 전원이 침묵해 워커가 접은 연결만 connect()가 다시 엽니다 (배선은 attachAutoResume).\n */\nexport class WorkerSseClient<\n M extends object = SseEventMap,\n> extends BaseSseClient<M> {\n // 워커의 구독 장부(hub)는 탭 구분 없는 전역 union이라, 같은 이벤트는 탭당 한 번만 알리면 됩니다\n readonly #sentSubscriptions = new Set<string>();\n\n #status: SseStatus = 'idle';\n #port: MessagePort | null = null;\n\n constructor(\n protected readonly options: Omit<SseClientOptions, 'eventSource'>\n ) {\n super(options.logger);\n }\n\n get status(): SseStatus {\n return this.#status;\n }\n\n connect(): void {\n // 팩토리 없는 생성은 방어적 no-op — 정상 경로(createSseClient)는 항상 팩토리와 함께 이 클래스를 고릅니다\n const factory = this.options.sharedWorker;\n if (factory === undefined) {\n return;\n }\n // 직결 클라이언트와 같은 회복 계약 — fatal은 disconnect()로 정리한 뒤에만 다시 시도할 수 있습니다\n if (this.#status === 'fatal') {\n this.options.logger?.warn(\n 'sse: fatal 상태에서는 connect()가 무시됩니다 — disconnect()로 정리한 뒤 다시 connect() 하세요'\n );\n return;\n }\n // 연결이 접혔는지(전원 침묵)는 탭의 status로 알 수 없습니다 — 거르지 않고 매번 보냅니다\n if (this.#port !== null) {\n this.#requestConnection();\n return;\n }\n\n const worker = this.#createWorker(factory);\n if (worker === null) {\n return;\n }\n\n const port = worker.port;\n this.#port = port;\n port.onmessage = (event) => {\n const isSamePort = this.#port === port;\n if (!isSamePort) {\n return;\n }\n this.#handleWorkerMessage(event.data, port);\n };\n port.start();\n\n this.#requestConnection();\n this.emitter.handlerTypes().forEach((type) => this.#sendSubscribe(type));\n }\n\n disconnect(): void {\n // 이 탭만 해제합니다 — 마지막 탭 판단과 연결 정리는 워커 몫이라 close를 상태 알림보다 먼저 보냅니다\n this.#post({ kind: 'close' });\n this.#port?.close();\n this.#port = null;\n this.#sentSubscriptions.clear();\n this.#setStatus('closed');\n }\n\n // 포트가 없으면 조용히 넘어갑니다 — connect()가 그때까지 모인 구독을 한 번에 보냅니다\n protected registerSubscription(type: string): void {\n this.#sendSubscribe(type);\n }\n\n // 워커는 baseUrl을 모르므로 탭이 알려줍니다 — 이미 연결이 서 있으면 워커가 무시합니다\n #requestConnection(): void {\n this.#post({ kind: 'init', baseUrl: this.options.baseUrl });\n }\n\n // 팩토리는 소비처 코드고 new SharedWorker도 URL 오류·CSP 차단 시 동기로 던집니다 — 직결 클라이언트의 EventSource 생성 실패와 같은 fatal 처리\n #createWorker(factory: () => SharedWorker): SharedWorker | null {\n try {\n return factory();\n } catch (error) {\n this.options.logger?.error(\n 'sse: SharedWorker 생성 실패 — 워커 URL·CSP를 확인하세요',\n error\n );\n this.#setStatus('fatal');\n this.emitter.emit(SDK_EVENTS.fatal, {\n cause: 'blocked',\n } satisfies FatalPayload);\n return null;\n }\n }\n\n #sendSubscribe(type: string): void {\n if (this.#port === null || this.#sentSubscriptions.has(type)) {\n return;\n }\n if (!isSubscribableEvent(type)) {\n return;\n }\n\n this.#sentSubscriptions.add(type);\n this.#post({ kind: 'subscribe', event: type });\n }\n\n #handleWorkerMessage(data: unknown, port: MessagePort): void {\n const message = data as Incoming<WorkerToTabMessage> | null;\n switch (message?.kind) {\n case 'status':\n // 포트 너머는 신뢰 경계 — 검증 없이 반영하면 status 게터의 타입 보증이 깨집니다\n if (isSseStatus(message.status)) {\n this.#setStatus(message.status);\n }\n return;\n case 'event':\n if (typeof message.event === 'string') {\n this.emitter.emit(message.event, message.payload);\n }\n return;\n case 'ping':\n this.#post({ kind: 'pong' });\n return;\n case 'ticket-request':\n // 토큰은 탭에만 있습니다 — 발급까지 여기서 하고 워커에는 결과만 넘깁니다\n void this.#issueTicketForWorker(port);\n return;\n default:\n // 여기 도달하는 건 미지 kind(신·구 코드가 섞인 배포)뿐이라 무시합니다\n message satisfies null;\n return;\n }\n }\n\n async #issueTicketForWorker(port: MessagePort): Promise<void> {\n const result = await issueTicketWithTimeout(this.options);\n // 발급 중 재연결로 포트가 바뀌었으면, 이 티켓은 이미 사라진 세션의 것입니다\n if (this.#port !== port) {\n return;\n }\n\n const ticketResponse: TicketResponseMessage = result.ok\n ? { kind: 'ticket-response', ok: true, ticket: result.ticket }\n : { kind: 'ticket-response', ok: false, error: result.error };\n\n this.#post(ticketResponse);\n }\n\n #post(message: TabToWorkerMessage): void {\n this.#port?.postMessage(message);\n }\n\n #setStatus(next: SseStatus): void {\n if (this.#status === next) {\n return;\n }\n\n this.#status = next;\n this.emitter.emitStatus(next);\n }\n}\n"],"names":["_sentSubscriptions","WeakMap","_status","_port","_WorkerSseClient_brand","WeakSet","WorkerSseClient","_BaseSseClient","options","_this","_classCallCheck","_callSuper","logger","_classPrivateMethodInitSpec","_classPrivateFieldInitSpec","Set","_inherits","_createClass","key","get","_classPrivateFieldGet","value","connect","_this2","factory","sharedWorker","undefined","_this$options$logger","warn","_assertClassBrand","_requestConnection","call","worker","_createWorker","port","_classPrivateFieldSet","onmessage","event","isSamePort","_handleWorkerMessage","data","start","emitter","handlerTypes","forEach","type","_sendSubscribe","disconnect","_classPrivateFieldGet2","_post","kind","close","clear","_setStatus","registerSubscription","BaseSseClient","baseUrl","error","_this$options$logger2","emit","SDK_EVENTS","fatal","cause","has","isSubscribableEvent","add","message","isSseStatus","status","payload","_issueTicketForWorker","_x","_issueTicketForWorker2","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee","result","ticketResponse","wrap","_callee$","_context","prev","next","issueTicketWithTimeout","sent","abrupt","ok","ticket","stop","_classPrivateFieldGet3","postMessage","emitStatus"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BaseSseClient } from '../core/BaseSseClient';
|
|
2
|
+
import type { SseClientOptions, SseEventMap, SseStatus } from '../core/types';
|
|
3
|
+
/**
|
|
4
|
+
* @description SseClient와 같은 시그니처의 탭 측 프록시 — 연결은 SharedWorker가 소유합니다.
|
|
5
|
+
* 워커와 주고받는 메시지의 방향·배선 현황은 protocol.ts의 지도를 보세요.
|
|
6
|
+
*
|
|
7
|
+
* 프록시는 상태를 소유하지 않고 워커의 status 통지를 반영합니다 — 스스로 status를 정하는 건
|
|
8
|
+
* disconnect의 closed와 워커 생성 실패의 fatal뿐이며, (재)connect 직후에는 워커 스냅샷이
|
|
9
|
+
* 도착할 때까지 직전 상태가 잠시 유지됩니다.
|
|
10
|
+
*
|
|
11
|
+
* 얼어붙은 탭에 복귀 절차는 없습니다 — 밀린 메시지는 깨어날 때 그대로 받습니다.
|
|
12
|
+
* 전원이 침묵해 워커가 접은 연결만 connect()가 다시 엽니다 (배선은 attachAutoResume).
|
|
13
|
+
*/
|
|
14
|
+
export declare class WorkerSseClient<M extends object = SseEventMap> extends BaseSseClient<M> {
|
|
15
|
+
#private;
|
|
16
|
+
protected readonly options: Omit<SseClientOptions, 'eventSource'>;
|
|
17
|
+
constructor(options: Omit<SseClientOptions, 'eventSource'>);
|
|
18
|
+
get status(): SseStatus;
|
|
19
|
+
connect(): void;
|
|
20
|
+
disconnect(): void;
|
|
21
|
+
protected registerSubscription(type: string): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=WorkerSseClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkerSseClient.d.ts","sourceRoot":"","sources":["../../../src/worker/WorkerSseClient.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,KAAK,EAEV,gBAAgB,EAChB,WAAW,EACX,SAAS,EACV,MAAM,eAAe,CAAC;AAGvB;;;;;;;;;;GAUG;AACH,qBAAa,eAAe,CAC1B,CAAC,SAAS,MAAM,GAAG,WAAW,CAC9B,SAAQ,aAAa,CAAC,CAAC,CAAC;;IAQtB,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC;gBAA9C,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC;IAKnE,IAAI,MAAM,IAAI,SAAS,CAEtB;IAED,OAAO,IAAI,IAAI;IAuCf,UAAU,IAAI,IAAI;IAUlB,SAAS,CAAC,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CA4FnD"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
|
|
2
|
+
import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
|
|
3
|
+
import _createClass from '@babel/runtime/helpers/createClass';
|
|
4
|
+
import _possibleConstructorReturn from '@babel/runtime/helpers/possibleConstructorReturn';
|
|
5
|
+
import _getPrototypeOf from '@babel/runtime/helpers/getPrototypeOf';
|
|
6
|
+
import _inherits from '@babel/runtime/helpers/inherits';
|
|
7
|
+
import _regeneratorRuntime from '@babel/runtime/regenerator';
|
|
8
|
+
import { BaseSseClient } from '../core/BaseSseClient.esm.js';
|
|
9
|
+
import { SDK_EVENTS, isSubscribableEvent } from '../core/eventNames.esm.js';
|
|
10
|
+
import { issueTicketWithTimeout } from '../core/ticket.esm.js';
|
|
11
|
+
import { isSseStatus } from '../core/types.esm.js';
|
|
12
|
+
|
|
13
|
+
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
|
14
|
+
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
15
|
+
function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
|
|
16
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
17
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
18
|
+
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
|
|
19
|
+
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
|
|
20
|
+
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @description SseClient와 같은 시그니처의 탭 측 프록시 — 연결은 SharedWorker가 소유합니다.
|
|
24
|
+
* 워커와 주고받는 메시지의 방향·배선 현황은 protocol.ts의 지도를 보세요.
|
|
25
|
+
*
|
|
26
|
+
* 프록시는 상태를 소유하지 않고 워커의 status 통지를 반영합니다 — 스스로 status를 정하는 건
|
|
27
|
+
* disconnect의 closed와 워커 생성 실패의 fatal뿐이며, (재)connect 직후에는 워커 스냅샷이
|
|
28
|
+
* 도착할 때까지 직전 상태가 잠시 유지됩니다.
|
|
29
|
+
*
|
|
30
|
+
* 얼어붙은 탭에 복귀 절차는 없습니다 — 밀린 메시지는 깨어날 때 그대로 받습니다.
|
|
31
|
+
* 전원이 침묵해 워커가 접은 연결만 connect()가 다시 엽니다 (배선은 attachAutoResume).
|
|
32
|
+
*/
|
|
33
|
+
var _sentSubscriptions = /*#__PURE__*/new WeakMap();
|
|
34
|
+
var _status = /*#__PURE__*/new WeakMap();
|
|
35
|
+
var _port = /*#__PURE__*/new WeakMap();
|
|
36
|
+
var _WorkerSseClient_brand = /*#__PURE__*/new WeakSet();
|
|
37
|
+
var WorkerSseClient = /*#__PURE__*/function (_BaseSseClient) {
|
|
38
|
+
function WorkerSseClient(options) {
|
|
39
|
+
var _this;
|
|
40
|
+
_classCallCheck(this, WorkerSseClient);
|
|
41
|
+
_this = _callSuper(this, WorkerSseClient, [options.logger]);
|
|
42
|
+
// 워커는 baseUrl을 모르므로 탭이 알려줍니다 — 이미 연결이 서 있으면 워커가 무시합니다
|
|
43
|
+
_classPrivateMethodInitSpec(_this, _WorkerSseClient_brand);
|
|
44
|
+
// 워커의 구독 장부(hub)는 탭 구분 없는 전역 union이라, 같은 이벤트는 탭당 한 번만 알리면 됩니다
|
|
45
|
+
_classPrivateFieldInitSpec(_this, _sentSubscriptions, new Set());
|
|
46
|
+
_classPrivateFieldInitSpec(_this, _status, 'idle');
|
|
47
|
+
_classPrivateFieldInitSpec(_this, _port, null);
|
|
48
|
+
_this.options = options;
|
|
49
|
+
return _this;
|
|
50
|
+
}
|
|
51
|
+
_inherits(WorkerSseClient, _BaseSseClient);
|
|
52
|
+
return _createClass(WorkerSseClient, [{
|
|
53
|
+
key: "status",
|
|
54
|
+
get: function get() {
|
|
55
|
+
return _classPrivateFieldGet(_status, this);
|
|
56
|
+
}
|
|
57
|
+
}, {
|
|
58
|
+
key: "connect",
|
|
59
|
+
value: function connect() {
|
|
60
|
+
var _this2 = this;
|
|
61
|
+
// 팩토리 없는 생성은 방어적 no-op — 정상 경로(createSseClient)는 항상 팩토리와 함께 이 클래스를 고릅니다
|
|
62
|
+
var factory = this.options.sharedWorker;
|
|
63
|
+
if (factory === undefined) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// 직결 클라이언트와 같은 회복 계약 — fatal은 disconnect()로 정리한 뒤에만 다시 시도할 수 있습니다
|
|
67
|
+
if (_classPrivateFieldGet(_status, this) === 'fatal') {
|
|
68
|
+
var _this$options$logger;
|
|
69
|
+
(_this$options$logger = this.options.logger) === null || _this$options$logger === void 0 || _this$options$logger.warn('sse: fatal 상태에서는 connect()가 무시됩니다 — disconnect()로 정리한 뒤 다시 connect() 하세요');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// 연결이 접혔는지(전원 침묵)는 탭의 status로 알 수 없습니다 — 거르지 않고 매번 보냅니다
|
|
73
|
+
if (_classPrivateFieldGet(_port, this) !== null) {
|
|
74
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _requestConnection).call(this);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
var worker = _assertClassBrand(_WorkerSseClient_brand, this, _createWorker).call(this, factory);
|
|
78
|
+
if (worker === null) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
var port = worker.port;
|
|
82
|
+
_classPrivateFieldSet(_port, this, port);
|
|
83
|
+
port.onmessage = function (event) {
|
|
84
|
+
var isSamePort = _classPrivateFieldGet(_port, _this2) === port;
|
|
85
|
+
if (!isSamePort) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
_assertClassBrand(_WorkerSseClient_brand, _this2, _handleWorkerMessage).call(_this2, event.data, port);
|
|
89
|
+
};
|
|
90
|
+
port.start();
|
|
91
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _requestConnection).call(this);
|
|
92
|
+
this.emitter.handlerTypes().forEach(function (type) {
|
|
93
|
+
return _assertClassBrand(_WorkerSseClient_brand, _this2, _sendSubscribe).call(_this2, type);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}, {
|
|
97
|
+
key: "disconnect",
|
|
98
|
+
value: function disconnect() {
|
|
99
|
+
var _classPrivateFieldGet2;
|
|
100
|
+
// 이 탭만 해제합니다 — 마지막 탭 판단과 연결 정리는 워커 몫이라 close를 상태 알림보다 먼저 보냅니다
|
|
101
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
102
|
+
kind: 'close'
|
|
103
|
+
});
|
|
104
|
+
(_classPrivateFieldGet2 = _classPrivateFieldGet(_port, this)) === null || _classPrivateFieldGet2 === void 0 || _classPrivateFieldGet2.close();
|
|
105
|
+
_classPrivateFieldSet(_port, this, null);
|
|
106
|
+
_classPrivateFieldGet(_sentSubscriptions, this).clear();
|
|
107
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _setStatus).call(this, 'closed');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 포트가 없으면 조용히 넘어갑니다 — connect()가 그때까지 모인 구독을 한 번에 보냅니다
|
|
111
|
+
}, {
|
|
112
|
+
key: "registerSubscription",
|
|
113
|
+
value: function registerSubscription(type) {
|
|
114
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _sendSubscribe).call(this, type);
|
|
115
|
+
}
|
|
116
|
+
}]);
|
|
117
|
+
}(BaseSseClient);
|
|
118
|
+
function _requestConnection() {
|
|
119
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
120
|
+
kind: 'init',
|
|
121
|
+
baseUrl: this.options.baseUrl
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// 팩토리는 소비처 코드고 new SharedWorker도 URL 오류·CSP 차단 시 동기로 던집니다 — 직결 클라이언트의 EventSource 생성 실패와 같은 fatal 처리
|
|
125
|
+
function _createWorker(factory) {
|
|
126
|
+
try {
|
|
127
|
+
return factory();
|
|
128
|
+
} catch (error) {
|
|
129
|
+
var _this$options$logger2;
|
|
130
|
+
(_this$options$logger2 = this.options.logger) === null || _this$options$logger2 === void 0 || _this$options$logger2.error('sse: SharedWorker 생성 실패 — 워커 URL·CSP를 확인하세요', error);
|
|
131
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _setStatus).call(this, 'fatal');
|
|
132
|
+
this.emitter.emit(SDK_EVENTS.fatal, {
|
|
133
|
+
cause: 'blocked'
|
|
134
|
+
});
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function _sendSubscribe(type) {
|
|
139
|
+
if (_classPrivateFieldGet(_port, this) === null || _classPrivateFieldGet(_sentSubscriptions, this).has(type)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (!isSubscribableEvent(type)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
_classPrivateFieldGet(_sentSubscriptions, this).add(type);
|
|
146
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
147
|
+
kind: 'subscribe',
|
|
148
|
+
event: type
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function _handleWorkerMessage(data, port) {
|
|
152
|
+
var message = data;
|
|
153
|
+
switch (message === null || message === void 0 ? void 0 : message.kind) {
|
|
154
|
+
case 'status':
|
|
155
|
+
// 포트 너머는 신뢰 경계 — 검증 없이 반영하면 status 게터의 타입 보증이 깨집니다
|
|
156
|
+
if (isSseStatus(message.status)) {
|
|
157
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _setStatus).call(this, message.status);
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
case 'event':
|
|
161
|
+
if (typeof message.event === 'string') {
|
|
162
|
+
this.emitter.emit(message.event, message.payload);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
case 'ping':
|
|
166
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, {
|
|
167
|
+
kind: 'pong'
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
case 'ticket-request':
|
|
171
|
+
// 토큰은 탭에만 있습니다 — 발급까지 여기서 하고 워커에는 결과만 넘깁니다
|
|
172
|
+
void _assertClassBrand(_WorkerSseClient_brand, this, _issueTicketForWorker).call(this, port);
|
|
173
|
+
return;
|
|
174
|
+
default:
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function _issueTicketForWorker(_x) {
|
|
179
|
+
return _issueTicketForWorker2.apply(this, arguments);
|
|
180
|
+
}
|
|
181
|
+
function _issueTicketForWorker2() {
|
|
182
|
+
_issueTicketForWorker2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(port) {
|
|
183
|
+
var result, ticketResponse;
|
|
184
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
185
|
+
while (1) switch (_context.prev = _context.next) {
|
|
186
|
+
case 0:
|
|
187
|
+
_context.next = 2;
|
|
188
|
+
return issueTicketWithTimeout(this.options);
|
|
189
|
+
case 2:
|
|
190
|
+
result = _context.sent;
|
|
191
|
+
if (!(_classPrivateFieldGet(_port, this) !== port)) {
|
|
192
|
+
_context.next = 5;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
return _context.abrupt("return");
|
|
196
|
+
case 5:
|
|
197
|
+
ticketResponse = result.ok ? {
|
|
198
|
+
kind: 'ticket-response',
|
|
199
|
+
ok: true,
|
|
200
|
+
ticket: result.ticket
|
|
201
|
+
} : {
|
|
202
|
+
kind: 'ticket-response',
|
|
203
|
+
ok: false,
|
|
204
|
+
error: result.error
|
|
205
|
+
};
|
|
206
|
+
_assertClassBrand(_WorkerSseClient_brand, this, _post).call(this, ticketResponse);
|
|
207
|
+
case 7:
|
|
208
|
+
case "end":
|
|
209
|
+
return _context.stop();
|
|
210
|
+
}
|
|
211
|
+
}, _callee, this);
|
|
212
|
+
}));
|
|
213
|
+
return _issueTicketForWorker2.apply(this, arguments);
|
|
214
|
+
}
|
|
215
|
+
function _post(message) {
|
|
216
|
+
var _classPrivateFieldGet3;
|
|
217
|
+
(_classPrivateFieldGet3 = _classPrivateFieldGet(_port, this)) === null || _classPrivateFieldGet3 === void 0 || _classPrivateFieldGet3.postMessage(message);
|
|
218
|
+
}
|
|
219
|
+
function _setStatus(next) {
|
|
220
|
+
if (_classPrivateFieldGet(_status, this) === next) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
_classPrivateFieldSet(_status, this, next);
|
|
224
|
+
this.emitter.emitStatus(next);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export { WorkerSseClient };
|
|
228
|
+
//# sourceMappingURL=WorkerSseClient.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkerSseClient.esm.js","sources":["../../../src/worker/WorkerSseClient.ts"],"sourcesContent":["import type {\n Incoming,\n TabToWorkerMessage,\n TicketResponseMessage,\n WorkerToTabMessage,\n} from './protocol';\n\nimport { BaseSseClient } from '../core/BaseSseClient';\nimport { isSubscribableEvent, SDK_EVENTS } from '../core/eventNames';\nimport { issueTicketWithTimeout } from '../core/ticket';\nimport type {\n FatalPayload,\n SseClientOptions,\n SseEventMap,\n SseStatus,\n} from '../core/types';\nimport { isSseStatus } from '../core/types';\n\n/**\n * @description SseClient와 같은 시그니처의 탭 측 프록시 — 연결은 SharedWorker가 소유합니다.\n * 워커와 주고받는 메시지의 방향·배선 현황은 protocol.ts의 지도를 보세요.\n *\n * 프록시는 상태를 소유하지 않고 워커의 status 통지를 반영합니다 — 스스로 status를 정하는 건\n * disconnect의 closed와 워커 생성 실패의 fatal뿐이며, (재)connect 직후에는 워커 스냅샷이\n * 도착할 때까지 직전 상태가 잠시 유지됩니다.\n *\n * 얼어붙은 탭에 복귀 절차는 없습니다 — 밀린 메시지는 깨어날 때 그대로 받습니다.\n * 전원이 침묵해 워커가 접은 연결만 connect()가 다시 엽니다 (배선은 attachAutoResume).\n */\nexport class WorkerSseClient<\n M extends object = SseEventMap,\n> extends BaseSseClient<M> {\n // 워커의 구독 장부(hub)는 탭 구분 없는 전역 union이라, 같은 이벤트는 탭당 한 번만 알리면 됩니다\n readonly #sentSubscriptions = new Set<string>();\n\n #status: SseStatus = 'idle';\n #port: MessagePort | null = null;\n\n constructor(\n protected readonly options: Omit<SseClientOptions, 'eventSource'>\n ) {\n super(options.logger);\n }\n\n get status(): SseStatus {\n return this.#status;\n }\n\n connect(): void {\n // 팩토리 없는 생성은 방어적 no-op — 정상 경로(createSseClient)는 항상 팩토리와 함께 이 클래스를 고릅니다\n const factory = this.options.sharedWorker;\n if (factory === undefined) {\n return;\n }\n // 직결 클라이언트와 같은 회복 계약 — fatal은 disconnect()로 정리한 뒤에만 다시 시도할 수 있습니다\n if (this.#status === 'fatal') {\n this.options.logger?.warn(\n 'sse: fatal 상태에서는 connect()가 무시됩니다 — disconnect()로 정리한 뒤 다시 connect() 하세요'\n );\n return;\n }\n // 연결이 접혔는지(전원 침묵)는 탭의 status로 알 수 없습니다 — 거르지 않고 매번 보냅니다\n if (this.#port !== null) {\n this.#requestConnection();\n return;\n }\n\n const worker = this.#createWorker(factory);\n if (worker === null) {\n return;\n }\n\n const port = worker.port;\n this.#port = port;\n port.onmessage = (event) => {\n const isSamePort = this.#port === port;\n if (!isSamePort) {\n return;\n }\n this.#handleWorkerMessage(event.data, port);\n };\n port.start();\n\n this.#requestConnection();\n this.emitter.handlerTypes().forEach((type) => this.#sendSubscribe(type));\n }\n\n disconnect(): void {\n // 이 탭만 해제합니다 — 마지막 탭 판단과 연결 정리는 워커 몫이라 close를 상태 알림보다 먼저 보냅니다\n this.#post({ kind: 'close' });\n this.#port?.close();\n this.#port = null;\n this.#sentSubscriptions.clear();\n this.#setStatus('closed');\n }\n\n // 포트가 없으면 조용히 넘어갑니다 — connect()가 그때까지 모인 구독을 한 번에 보냅니다\n protected registerSubscription(type: string): void {\n this.#sendSubscribe(type);\n }\n\n // 워커는 baseUrl을 모르므로 탭이 알려줍니다 — 이미 연결이 서 있으면 워커가 무시합니다\n #requestConnection(): void {\n this.#post({ kind: 'init', baseUrl: this.options.baseUrl });\n }\n\n // 팩토리는 소비처 코드고 new SharedWorker도 URL 오류·CSP 차단 시 동기로 던집니다 — 직결 클라이언트의 EventSource 생성 실패와 같은 fatal 처리\n #createWorker(factory: () => SharedWorker): SharedWorker | null {\n try {\n return factory();\n } catch (error) {\n this.options.logger?.error(\n 'sse: SharedWorker 생성 실패 — 워커 URL·CSP를 확인하세요',\n error\n );\n this.#setStatus('fatal');\n this.emitter.emit(SDK_EVENTS.fatal, {\n cause: 'blocked',\n } satisfies FatalPayload);\n return null;\n }\n }\n\n #sendSubscribe(type: string): void {\n if (this.#port === null || this.#sentSubscriptions.has(type)) {\n return;\n }\n if (!isSubscribableEvent(type)) {\n return;\n }\n\n this.#sentSubscriptions.add(type);\n this.#post({ kind: 'subscribe', event: type });\n }\n\n #handleWorkerMessage(data: unknown, port: MessagePort): void {\n const message = data as Incoming<WorkerToTabMessage> | null;\n switch (message?.kind) {\n case 'status':\n // 포트 너머는 신뢰 경계 — 검증 없이 반영하면 status 게터의 타입 보증이 깨집니다\n if (isSseStatus(message.status)) {\n this.#setStatus(message.status);\n }\n return;\n case 'event':\n if (typeof message.event === 'string') {\n this.emitter.emit(message.event, message.payload);\n }\n return;\n case 'ping':\n this.#post({ kind: 'pong' });\n return;\n case 'ticket-request':\n // 토큰은 탭에만 있습니다 — 발급까지 여기서 하고 워커에는 결과만 넘깁니다\n void this.#issueTicketForWorker(port);\n return;\n default:\n // 여기 도달하는 건 미지 kind(신·구 코드가 섞인 배포)뿐이라 무시합니다\n message satisfies null;\n return;\n }\n }\n\n async #issueTicketForWorker(port: MessagePort): Promise<void> {\n const result = await issueTicketWithTimeout(this.options);\n // 발급 중 재연결로 포트가 바뀌었으면, 이 티켓은 이미 사라진 세션의 것입니다\n if (this.#port !== port) {\n return;\n }\n\n const ticketResponse: TicketResponseMessage = result.ok\n ? { kind: 'ticket-response', ok: true, ticket: result.ticket }\n : { kind: 'ticket-response', ok: false, error: result.error };\n\n this.#post(ticketResponse);\n }\n\n #post(message: TabToWorkerMessage): void {\n this.#port?.postMessage(message);\n }\n\n #setStatus(next: SseStatus): void {\n if (this.#status === next) {\n return;\n }\n\n this.#status = next;\n this.emitter.emitStatus(next);\n }\n}\n"],"names":["_sentSubscriptions","WeakMap","_status","_port","_WorkerSseClient_brand","WeakSet","WorkerSseClient","_BaseSseClient","options","_this","_classCallCheck","_callSuper","logger","_classPrivateMethodInitSpec","_classPrivateFieldInitSpec","Set","_inherits","_createClass","key","get","_classPrivateFieldGet","value","connect","_this2","factory","sharedWorker","undefined","_this$options$logger","warn","_assertClassBrand","_requestConnection","call","worker","_createWorker","port","_classPrivateFieldSet","onmessage","event","isSamePort","_handleWorkerMessage","data","start","emitter","handlerTypes","forEach","type","_sendSubscribe","disconnect","_classPrivateFieldGet2","_post","kind","close","clear","_setStatus","registerSubscription","BaseSseClient","baseUrl","error","_this$options$logger2","emit","SDK_EVENTS","fatal","cause","has","isSubscribableEvent","add","message","isSseStatus","status","payload","_issueTicketForWorker","_x","_issueTicketForWorker2","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee","result","ticketResponse","wrap","_callee$","_context","prev","next","issueTicketWithTimeout","sent","abrupt","ok","ticket","stop","_classPrivateFieldGet3","postMessage","emitStatus"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HubPort } from './portRegistry';
|
|
2
|
+
import type { WorkerToTabMessage } from './protocol';
|
|
3
|
+
import type { WorkerConnectionOptions } from './workerConnection';
|
|
4
|
+
export { PING_INTERVAL_MS } from './portRegistry';
|
|
5
|
+
export type { HubPort } from './portRegistry';
|
|
6
|
+
/**
|
|
7
|
+
* @description SharedWorker 쪽 메시지 라우팅 — 탭이 보낸 것을 연결에 전하고, 연결이 낸 것을 탭에 전합니다.
|
|
8
|
+
* 포트 장부(frozen 탭을 빼지 않는 이유 포함)는 portRegistry가, 연결 수립·정리는 workerConnection이 소유합니다.
|
|
9
|
+
* 배선 현황은 protocol.ts의 지도를 보세요.
|
|
10
|
+
*/
|
|
11
|
+
export declare const createWorkerHub: (hubOptions?: WorkerConnectionOptions) => {
|
|
12
|
+
addPort: (port: HubPort) => void;
|
|
13
|
+
handleMessage: (port: HubPort, data: unknown) => void;
|
|
14
|
+
broadcast: (message: WorkerToTabMessage) => void;
|
|
15
|
+
tabCount: () => number;
|
|
16
|
+
subscribedEvents: () => string[];
|
|
17
|
+
};
|
|
18
|
+
export type WorkerHub = ReturnType<typeof createWorkerHub>;
|
|
19
|
+
//# sourceMappingURL=hub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../../src/worker/hub.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,KAAK,EAGV,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAMlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,eAAe,gBAAgB,uBAAuB;oBAkC/C,OAAO,KAAG,IAAI;0BAKR,OAAO,QAAQ,OAAO,KAAG,IAAI;yBAjCzB,kBAAkB,KAAG,IAAI;;;CAwFtD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { WorkerToTabMessage } from './protocol';
|
|
2
|
+
/** @description 장부가 포트에 요구하는 최소 표면 — 실제 MessagePort와 테스트 대역이 함께 만족합니다 */
|
|
3
|
+
export interface HubPort {
|
|
4
|
+
postMessage(message: WorkerToTabMessage): void;
|
|
5
|
+
close(): void;
|
|
6
|
+
}
|
|
7
|
+
export declare const PING_INTERVAL_MS = 15000;
|
|
8
|
+
/**
|
|
9
|
+
* @description 탭 포트의 장부 — 등록·떠남과 ping 주기를 소유합니다.
|
|
10
|
+
*
|
|
11
|
+
* 무응답 포트를 장부에서 빼지 않습니다: frozen 탭의 메시지는 브라우저가 큐잉했다가
|
|
12
|
+
* 깨어날 때 전부 배달하므로, 계속 보내는 것이 곧 공백 없는 복구입니다 —
|
|
13
|
+
* 빼는 순간부터가 그 탭이 놓치는 구간이 됩니다.
|
|
14
|
+
* close 없이 사라진 탭(크래시·창 닫기)의 포트는 장부에 남지만 무해합니다 — 전송은 조용히
|
|
15
|
+
* 버려지고, 상한은 워커 생존 중 닫힌 탭 수이며, 마지막 클라이언트가 사라지면 브라우저가 워커째 거둡니다.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createPortRegistry: () => {
|
|
18
|
+
add: (port: HubPort) => void;
|
|
19
|
+
/** @description 떠난 포트의 늦은 메시지를 거르는 유일한 기준 — frozen 탭은 장부에 남아 있습니다 */
|
|
20
|
+
has: (port: HubPort) => boolean;
|
|
21
|
+
/**
|
|
22
|
+
* @description 탭이 스스로 떠났습니다 — 장부에서 빼고 포트를 닫습니다.
|
|
23
|
+
* 마지막이었는지 함께 알려 호출부가 세션을 접을지 정하게 합니다.
|
|
24
|
+
*/
|
|
25
|
+
close: (port: HubPort) => {
|
|
26
|
+
wasLast: boolean;
|
|
27
|
+
};
|
|
28
|
+
markAlive: (port: HubPort) => void;
|
|
29
|
+
size: () => number;
|
|
30
|
+
forEach: (visit: (port: HubPort) => void) => void;
|
|
31
|
+
/**
|
|
32
|
+
* @description 티켓을 물어볼 탭 하나 — EventSource가 하나라 티켓도 한 장이면 됩니다.
|
|
33
|
+
* 전원에게 뿌리면 탭 수만큼 POST가 나가고, 그 증폭이 429(fatal)를 자초합니다.
|
|
34
|
+
* 이번 주기에 답한 탭을 먼저 고르고, 없으면 아직 판정 전인 탭에게 묻습니다.
|
|
35
|
+
* 전원이 확정 침묵이면 undefined: 답이 올 수 없는 요청을 큐에 쌓으면
|
|
36
|
+
* 깨어난 탭이 그 수만큼 한꺼번에 POST를 쏘게 됩니다.
|
|
37
|
+
*/
|
|
38
|
+
livelyPort: () => HubPort | undefined;
|
|
39
|
+
};
|
|
40
|
+
export type PortRegistry = ReturnType<typeof createPortRegistry>;
|
|
41
|
+
//# sourceMappingURL=portRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portRegistry.d.ts","sourceRoot":"","sources":["../../../src/worker/portRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACtB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC/C,KAAK,IAAI,IAAI,CAAC;CACf;AAGD,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;gBAmBf,OAAO,KAAG,IAAI;IAI1B,qEAAqE;gBACzD,OAAO,KAAG,OAAO;IAE7B;;;OAGG;kBACW,OAAO,KAAG;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE;sBAS1B,OAAO,KAAG,IAAI;gBAKtB,MAAM;qBAEC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAG,IAAI;IAE/C;;;;;;OAMG;sBACa,OAAO,GAAG,SAAS;CAQtC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|