@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.
Files changed (160) hide show
  1. package/README.md +238 -0
  2. package/dist/src/core/BaseSseClient.cjs.js +97 -0
  3. package/dist/src/core/BaseSseClient.cjs.js.map +1 -0
  4. package/dist/src/core/BaseSseClient.d.ts +33 -0
  5. package/dist/src/core/BaseSseClient.d.ts.map +1 -0
  6. package/dist/src/core/BaseSseClient.esm.js +89 -0
  7. package/dist/src/core/BaseSseClient.esm.js.map +1 -0
  8. package/dist/src/core/SseClient.cjs.js +382 -0
  9. package/dist/src/core/SseClient.cjs.js.map +1 -0
  10. package/dist/src/core/SseClient.d.ts +24 -0
  11. package/dist/src/core/SseClient.d.ts.map +1 -0
  12. package/dist/src/core/SseClient.esm.js +370 -0
  13. package/dist/src/core/SseClient.esm.js.map +1 -0
  14. package/dist/src/core/backoff.cjs.js +30 -0
  15. package/dist/src/core/backoff.cjs.js.map +1 -0
  16. package/dist/src/core/backoff.d.ts +12 -0
  17. package/dist/src/core/backoff.d.ts.map +1 -0
  18. package/dist/src/core/backoff.esm.js +28 -0
  19. package/dist/src/core/backoff.esm.js.map +1 -0
  20. package/dist/src/core/emitter.cjs.js +57 -0
  21. package/dist/src/core/emitter.cjs.js.map +1 -0
  22. package/dist/src/core/emitter.d.ts +14 -0
  23. package/dist/src/core/emitter.d.ts.map +1 -0
  24. package/dist/src/core/emitter.esm.js +51 -0
  25. package/dist/src/core/emitter.esm.js.map +1 -0
  26. package/dist/src/core/eventNames.cjs.js +64 -0
  27. package/dist/src/core/eventNames.cjs.js.map +1 -0
  28. package/dist/src/core/eventNames.d.ts +31 -0
  29. package/dist/src/core/eventNames.d.ts.map +1 -0
  30. package/dist/src/core/eventNames.esm.js +54 -0
  31. package/dist/src/core/eventNames.esm.js.map +1 -0
  32. package/dist/src/core/retryWait.cjs.js +20 -0
  33. package/dist/src/core/retryWait.cjs.js.map +1 -0
  34. package/dist/src/core/retryWait.d.ts +6 -0
  35. package/dist/src/core/retryWait.d.ts.map +1 -0
  36. package/dist/src/core/retryWait.esm.js +18 -0
  37. package/dist/src/core/retryWait.esm.js.map +1 -0
  38. package/dist/src/core/statusMachine.cjs.js +56 -0
  39. package/dist/src/core/statusMachine.cjs.js.map +1 -0
  40. package/dist/src/core/statusMachine.d.ts +9 -0
  41. package/dist/src/core/statusMachine.d.ts.map +1 -0
  42. package/dist/src/core/statusMachine.esm.js +54 -0
  43. package/dist/src/core/statusMachine.esm.js.map +1 -0
  44. package/dist/src/core/stream.cjs.js +136 -0
  45. package/dist/src/core/stream.cjs.js.map +1 -0
  46. package/dist/src/core/stream.d.ts +42 -0
  47. package/dist/src/core/stream.d.ts.map +1 -0
  48. package/dist/src/core/stream.esm.js +128 -0
  49. package/dist/src/core/stream.esm.js.map +1 -0
  50. package/dist/src/core/ticket.cjs.js +237 -0
  51. package/dist/src/core/ticket.cjs.js.map +1 -0
  52. package/dist/src/core/ticket.d.ts +31 -0
  53. package/dist/src/core/ticket.d.ts.map +1 -0
  54. package/dist/src/core/ticket.esm.js +220 -0
  55. package/dist/src/core/ticket.esm.js.map +1 -0
  56. package/dist/src/core/types.cjs.js +54 -0
  57. package/dist/src/core/types.cjs.js.map +1 -0
  58. package/dist/src/core/types.d.ts +131 -0
  59. package/dist/src/core/types.d.ts.map +1 -0
  60. package/dist/src/core/types.esm.js +50 -0
  61. package/dist/src/core/types.esm.js.map +1 -0
  62. package/dist/src/core/wire.cjs.js +29 -0
  63. package/dist/src/core/wire.cjs.js.map +1 -0
  64. package/dist/src/core/wire.d.ts +7 -0
  65. package/dist/src/core/wire.d.ts.map +1 -0
  66. package/dist/src/core/wire.esm.js +23 -0
  67. package/dist/src/core/wire.esm.js.map +1 -0
  68. package/dist/src/createSseClient.cjs.js +30 -0
  69. package/dist/src/createSseClient.cjs.js.map +1 -0
  70. package/dist/src/createSseClient.d.ts +8 -0
  71. package/dist/src/createSseClient.d.ts.map +1 -0
  72. package/dist/src/createSseClient.esm.js +28 -0
  73. package/dist/src/createSseClient.esm.js.map +1 -0
  74. package/dist/src/index.cjs.js +10 -0
  75. package/dist/src/index.cjs.js.map +1 -0
  76. package/dist/src/index.d.ts +6 -0
  77. package/dist/src/index.d.ts.map +1 -0
  78. package/dist/src/index.esm.js +3 -0
  79. package/dist/src/index.esm.js.map +1 -0
  80. package/dist/src/react/createSseContext.cjs.js +130 -0
  81. package/dist/src/react/createSseContext.cjs.js.map +1 -0
  82. package/dist/src/react/createSseContext.d.ts +32 -0
  83. package/dist/src/react/createSseContext.d.ts.map +1 -0
  84. package/dist/src/react/createSseContext.esm.js +122 -0
  85. package/dist/src/react/createSseContext.esm.js.map +1 -0
  86. package/dist/src/react/index.cjs.js +8 -0
  87. package/dist/src/react/index.cjs.js.map +1 -0
  88. package/dist/src/react/index.d.ts +3 -0
  89. package/dist/src/react/index.d.ts.map +1 -0
  90. package/dist/src/react/index.esm.js +2 -0
  91. package/dist/src/react/index.esm.js.map +1 -0
  92. package/dist/src/testing/doubles.d.ts +50 -0
  93. package/dist/src/testing/doubles.d.ts.map +1 -0
  94. package/dist/src/testing/harness.d.ts +43 -0
  95. package/dist/src/testing/harness.d.ts.map +1 -0
  96. package/dist/src/testing/index.d.ts +3 -0
  97. package/dist/src/testing/index.d.ts.map +1 -0
  98. package/dist/src/worker/WorkerSseClient.cjs.js +240 -0
  99. package/dist/src/worker/WorkerSseClient.cjs.js.map +1 -0
  100. package/dist/src/worker/WorkerSseClient.d.ts +23 -0
  101. package/dist/src/worker/WorkerSseClient.d.ts.map +1 -0
  102. package/dist/src/worker/WorkerSseClient.esm.js +228 -0
  103. package/dist/src/worker/WorkerSseClient.esm.js.map +1 -0
  104. package/dist/src/worker/hub.d.ts +19 -0
  105. package/dist/src/worker/hub.d.ts.map +1 -0
  106. package/dist/src/worker/portRegistry.d.ts +41 -0
  107. package/dist/src/worker/portRegistry.d.ts.map +1 -0
  108. package/dist/src/worker/protocol.d.ts +63 -0
  109. package/dist/src/worker/protocol.d.ts.map +1 -0
  110. package/dist/src/worker/sse.worker.d.ts +2 -0
  111. package/dist/src/worker/sse.worker.d.ts.map +1 -0
  112. package/dist/src/worker/ticketDelegate.d.ts +16 -0
  113. package/dist/src/worker/ticketDelegate.d.ts.map +1 -0
  114. package/dist/src/worker/workerConnection.d.ts +28 -0
  115. package/dist/src/worker/workerConnection.d.ts.map +1 -0
  116. package/dist/worker/sse.worker.js +1865 -0
  117. package/dist/worker/sse.worker.js.map +1 -0
  118. package/package.json +87 -0
  119. package/src/core/BaseSseClient.test.ts +142 -0
  120. package/src/core/BaseSseClient.ts +103 -0
  121. package/src/core/SseClient.connection.test.ts +863 -0
  122. package/src/core/SseClient.scenarios.test.ts +164 -0
  123. package/src/core/SseClient.test.ts +63 -0
  124. package/src/core/SseClient.ts +310 -0
  125. package/src/core/backoff.test.ts +43 -0
  126. package/src/core/backoff.ts +39 -0
  127. package/src/core/emitter.ts +56 -0
  128. package/src/core/eventNames.ts +53 -0
  129. package/src/core/retryWait.ts +18 -0
  130. package/src/core/statusMachine.ts +56 -0
  131. package/src/core/stream.ts +141 -0
  132. package/src/core/ticket.ts +146 -0
  133. package/src/core/types.ts +182 -0
  134. package/src/core/wire.ts +23 -0
  135. package/src/createSseClient.test.ts +82 -0
  136. package/src/createSseClient.ts +34 -0
  137. package/src/index.ts +21 -0
  138. package/src/react/SseProvider.test.tsx +129 -0
  139. package/src/react/createSseContext.test.ts +47 -0
  140. package/src/react/createSseContext.tsx +140 -0
  141. package/src/react/index.ts +2 -0
  142. package/src/react/useSseEvent.test.tsx +71 -0
  143. package/src/react/useSseStatus.test.tsx +40 -0
  144. package/src/testing/doubles.ts +128 -0
  145. package/src/testing/harness.ts +82 -0
  146. package/src/testing/index.ts +3 -0
  147. package/src/worker/SseClientLike.contract.test.ts +270 -0
  148. package/src/worker/WorkerSseClient.scenarios.test.ts +179 -0
  149. package/src/worker/WorkerSseClient.test.ts +377 -0
  150. package/src/worker/WorkerSseClient.ts +190 -0
  151. package/src/worker/hub.connection.test.ts +1023 -0
  152. package/src/worker/hub.test.ts +213 -0
  153. package/src/worker/hub.ts +120 -0
  154. package/src/worker/portRegistry.test.ts +59 -0
  155. package/src/worker/portRegistry.ts +86 -0
  156. package/src/worker/protocol.ts +80 -0
  157. package/src/worker/sse.worker.ts +19 -0
  158. package/src/worker/ticketDelegate.test.ts +99 -0
  159. package/src/worker/ticketDelegate.ts +80 -0
  160. package/src/worker/workerConnection.ts +134 -0
@@ -0,0 +1,220 @@
1
+ import _typeof from '@babel/runtime/helpers/typeof';
2
+ import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
3
+ import _createClass from '@babel/runtime/helpers/createClass';
4
+ import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
5
+ import _possibleConstructorReturn from '@babel/runtime/helpers/possibleConstructorReturn';
6
+ import _getPrototypeOf from '@babel/runtime/helpers/getPrototypeOf';
7
+ import _inherits from '@babel/runtime/helpers/inherits';
8
+ import _wrapNativeSuper from '@babel/runtime/helpers/wrapNativeSuper';
9
+ import _regeneratorRuntime from '@babel/runtime/regenerator';
10
+
11
+ function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
12
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
13
+
14
+ /**
15
+ * @description 티켓 발급 실패의 분류 — status 부재 = 네트워크성, cause = 네트워크가 아닌 실패의 출처.
16
+ * 워커 프로토콜이 이 객체를 그대로 실어 나르므로 직렬화 가능한 값만 담습니다.
17
+ */
18
+
19
+ var TICKET_TIMEOUT_MS = 10000;
20
+ var TICKET_NETWORK_FAILURE = Object.freeze({
21
+ ok: false,
22
+ error: Object.freeze({})
23
+ });
24
+ var TICKET_AUTH_CALLBACK_FAILURE = Object.freeze({
25
+ ok: false,
26
+ error: Object.freeze({
27
+ cause: 'auth_callback'
28
+ })
29
+ });
30
+ var AuthTokenError = /*#__PURE__*/function (_Error) {
31
+ function AuthTokenError(message, options) {
32
+ var _this;
33
+ _classCallCheck(this, AuthTokenError);
34
+ _this = _callSuper(this, AuthTokenError, [message, options]);
35
+ _this.name = 'AuthTokenError';
36
+ return _this;
37
+ }
38
+ _inherits(AuthTokenError, _Error);
39
+ return _createClass(AuthTokenError);
40
+ }(/*#__PURE__*/_wrapNativeSuper(Error));
41
+ var resolveAuthToken = /*#__PURE__*/function () {
42
+ var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(getAuthToken) {
43
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
44
+ while (1) switch (_context.prev = _context.next) {
45
+ case 0:
46
+ _context.prev = 0;
47
+ _context.next = 3;
48
+ return getAuthToken();
49
+ case 3:
50
+ return _context.abrupt("return", _context.sent);
51
+ case 6:
52
+ _context.prev = 6;
53
+ _context.t0 = _context["catch"](0);
54
+ throw new AuthTokenError('getAuthToken threw', {
55
+ cause: _context.t0
56
+ });
57
+ case 9:
58
+ case "end":
59
+ return _context.stop();
60
+ }
61
+ }, _callee, null, [[0, 6]]);
62
+ }));
63
+ return function resolveAuthToken(_x) {
64
+ return _ref.apply(this, arguments);
65
+ };
66
+ }();
67
+ var authHeaders = function authHeaders(token) {
68
+ return token === null ? undefined : {
69
+ Authorization: "Bearer ".concat(token)
70
+ };
71
+ };
72
+ var hasTicket = function hasTicket(body) {
73
+ if (_typeof(body) !== 'object' || body === null) {
74
+ return false;
75
+ }
76
+ var _ref2 = body,
77
+ data = _ref2.data;
78
+ return _typeof(data) === 'object' && data !== null && typeof data.ticket === 'string';
79
+ };
80
+
81
+ /**
82
+ * @description 본문을 계약({ data: { ticket: string } } — 사내 기본 응답 스키마)으로 좁혀 값으로 돌려줍니다 —
83
+ * 파싱 실패(게이트웨이·프록시가 200으로 주는 HTML 등)와 형태 위반(ticket 부재)을 가르지 않습니다:
84
+ * 어느 쪽이든 서버가 응답한 본문 계약 위반이고, 경로 생존 증거라 status를 실어 네트워크성 연속 판정을 끊습니다.
85
+ * 이 분기는 stalled·fatal 어느 쪽에도 닿지 않는 유일한 침묵 실패라 warn을 남깁니다 — 서버 로그에도 흔적이
86
+ * 없는 실패(서버 입장에선 정상 200)여서 이 로그가 유일한 단서입니다.
87
+ */
88
+ var parseTicketBody = /*#__PURE__*/function () {
89
+ var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(response, logger) {
90
+ var body;
91
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
92
+ while (1) switch (_context2.prev = _context2.next) {
93
+ case 0:
94
+ _context2.next = 2;
95
+ return response.json()["catch"](function () {
96
+ return null;
97
+ });
98
+ case 2:
99
+ body = _context2.sent;
100
+ if (hasTicket(body)) {
101
+ _context2.next = 6;
102
+ break;
103
+ }
104
+ logger === null || logger === void 0 || logger.warn("sse: \uD2F0\uCF13 \uC751\uB2F5(".concat(response.status, ") \uBCF8\uBB38\uC774 \uACC4\uC57D({ data: { ticket } })\uACFC \uB2E4\uB985\uB2C8\uB2E4 \u2014 \uC7AC\uC2DC\uB3C4\uB294 \uACC4\uC18D\uD569\uB2C8\uB2E4"));
105
+ return _context2.abrupt("return", {
106
+ ok: false,
107
+ error: {
108
+ status: response.status
109
+ }
110
+ });
111
+ case 6:
112
+ return _context2.abrupt("return", {
113
+ ok: true,
114
+ ticket: body.data.ticket
115
+ });
116
+ case 7:
117
+ case "end":
118
+ return _context2.stop();
119
+ }
120
+ }, _callee2);
121
+ }));
122
+ return function parseTicketBody(_x2, _x3) {
123
+ return _ref3.apply(this, arguments);
124
+ };
125
+ }();
126
+
127
+ /**
128
+ * @description 연결 티켓을 발급합니다 — TTL 15s이므로 발급 즉시 연결해야 합니다
129
+ */
130
+ var issueTicket = /*#__PURE__*/function () {
131
+ var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3(options) {
132
+ var _options$fetch;
133
+ var fetchImpl, token, response, _options$logger;
134
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
135
+ while (1) switch (_context3.prev = _context3.next) {
136
+ case 0:
137
+ fetchImpl = (_options$fetch = options.fetch) !== null && _options$fetch !== void 0 ? _options$fetch : globalThis.fetch;
138
+ _context3.prev = 1;
139
+ _context3.next = 4;
140
+ return resolveAuthToken(options.getAuthToken);
141
+ case 4:
142
+ token = _context3.sent;
143
+ _context3.next = 7;
144
+ return fetchImpl("".concat(options.baseUrl, "/v1/events/ticket"), {
145
+ method: 'POST',
146
+ headers: authHeaders(token)
147
+ });
148
+ case 7:
149
+ response = _context3.sent;
150
+ if (response.ok) {
151
+ _context3.next = 10;
152
+ break;
153
+ }
154
+ return _context3.abrupt("return", {
155
+ ok: false,
156
+ error: {
157
+ status: response.status
158
+ }
159
+ });
160
+ case 10:
161
+ _context3.next = 12;
162
+ return parseTicketBody(response, options.logger);
163
+ case 12:
164
+ return _context3.abrupt("return", _context3.sent);
165
+ case 15:
166
+ _context3.prev = 15;
167
+ _context3.t0 = _context3["catch"](1);
168
+ if (!(_context3.t0 instanceof AuthTokenError)) {
169
+ _context3.next = 20;
170
+ break;
171
+ }
172
+ // 정지 결정은 SseClient 몫이지만, 로그는 원인 예외가 있는 여기서 한 번만 — 워커 경로에선 탭 스코프라 유일한 기록입니다
173
+ (_options$logger = options.logger) === null || _options$logger === void 0 || _options$logger.error('sse: getAuthToken이 예외를 던져 멈춥니다 — 콜백은 실패 시 null을 반환해야 합니다', _context3.t0.cause);
174
+ return _context3.abrupt("return", TICKET_AUTH_CALLBACK_FAILURE);
175
+ case 20:
176
+ return _context3.abrupt("return", TICKET_NETWORK_FAILURE);
177
+ case 21:
178
+ case "end":
179
+ return _context3.stop();
180
+ }
181
+ }, _callee3, null, [[1, 15]]);
182
+ }));
183
+ return function issueTicket(_x4) {
184
+ return _ref4.apply(this, arguments);
185
+ };
186
+ }();
187
+
188
+ /**
189
+ * @description 티켓 발급에 시간 제한을 겁니다 — 타임아웃 이후 도착한 응답은 race가 이미 완료돼 무시됩니다
190
+ */
191
+ var issueTicketWithTimeout = /*#__PURE__*/function () {
192
+ var _ref5 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4(options) {
193
+ var ticketTimer, ticketTimeout, result;
194
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
195
+ while (1) switch (_context4.prev = _context4.next) {
196
+ case 0:
197
+ ticketTimeout = new Promise(function (resolve) {
198
+ ticketTimer = setTimeout(function () {
199
+ return resolve(TICKET_NETWORK_FAILURE);
200
+ }, TICKET_TIMEOUT_MS);
201
+ });
202
+ _context4.next = 3;
203
+ return Promise.race([issueTicket(options), ticketTimeout]);
204
+ case 3:
205
+ result = _context4.sent;
206
+ clearTimeout(ticketTimer);
207
+ return _context4.abrupt("return", result);
208
+ case 6:
209
+ case "end":
210
+ return _context4.stop();
211
+ }
212
+ }, _callee4);
213
+ }));
214
+ return function issueTicketWithTimeout(_x5) {
215
+ return _ref5.apply(this, arguments);
216
+ };
217
+ }();
218
+
219
+ export { TICKET_NETWORK_FAILURE, TICKET_TIMEOUT_MS, issueTicket, issueTicketWithTimeout };
220
+ //# sourceMappingURL=ticket.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket.esm.js","sources":["../../../src/core/ticket.ts"],"sourcesContent":["import type { FatalCause, SseClientOptions, SseLogger } from './types';\n\n// 티켓 층이 만드는 cause의 단일 원천 — fatal로 그대로 이어지므로 FatalCause의 부분집합이어야 합니다(satisfies가 강제)\nconst TICKET_FAILURE_CAUSES = [\n 'auth_callback',\n] as const satisfies readonly FatalCause[];\n\ntype TicketFailureCause = (typeof TICKET_FAILURE_CAUSES)[number];\n\n/**\n * @description 티켓 발급 실패의 분류 — status 부재 = 네트워크성, cause = 네트워크가 아닌 실패의 출처.\n * 워커 프로토콜이 이 객체를 그대로 실어 나르므로 직렬화 가능한 값만 담습니다.\n */\nexport interface TicketError {\n status?: number;\n // 와이어 너머의 신·구 버전이 미지 cause를 보낼 수 있습니다 — 코어는 아는 값만 동등 비교로 골라 씁니다\n cause?: TicketFailureCause | (string & Record<never, never>);\n}\n\nexport type TicketResult =\n | { ok: true; ticket: string }\n | { ok: false; error: TicketError };\n\ntype ServerContractOptions = Pick<\n SseClientOptions,\n 'baseUrl' | 'getAuthToken' | 'fetch' | 'logger'\n>;\n\nexport const TICKET_TIMEOUT_MS = 10_000;\n\nexport const TICKET_NETWORK_FAILURE: TicketResult = Object.freeze({\n ok: false,\n error: Object.freeze({}),\n});\n\nconst TICKET_AUTH_CALLBACK_FAILURE: TicketResult = Object.freeze({\n ok: false,\n error: Object.freeze({ cause: 'auth_callback' as const }),\n});\n\nclass AuthTokenError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'AuthTokenError';\n }\n}\n\nconst resolveAuthToken = async (\n getAuthToken: ServerContractOptions['getAuthToken']\n): Promise<string | null> => {\n try {\n return await getAuthToken();\n } catch (error) {\n throw new AuthTokenError('getAuthToken threw', { cause: error });\n }\n};\n\nconst authHeaders = (token: string | null) =>\n token === null ? undefined : { Authorization: `Bearer ${token}` };\n\nconst hasTicket = (body: unknown): body is { data: { ticket: string } } => {\n if (typeof body !== 'object' || body === null) {\n return false;\n }\n const { data } = body as { data?: unknown };\n return (\n typeof data === 'object' &&\n data !== null &&\n typeof (data as { ticket?: unknown }).ticket === 'string'\n );\n};\n\n/**\n * @description 본문을 계약({ data: { ticket: string } } — 사내 기본 응답 스키마)으로 좁혀 값으로 돌려줍니다 —\n * 파싱 실패(게이트웨이·프록시가 200으로 주는 HTML 등)와 형태 위반(ticket 부재)을 가르지 않습니다:\n * 어느 쪽이든 서버가 응답한 본문 계약 위반이고, 경로 생존 증거라 status를 실어 네트워크성 연속 판정을 끊습니다.\n * 이 분기는 stalled·fatal 어느 쪽에도 닿지 않는 유일한 침묵 실패라 warn을 남깁니다 — 서버 로그에도 흔적이\n * 없는 실패(서버 입장에선 정상 200)여서 이 로그가 유일한 단서입니다.\n */\nconst parseTicketBody = async (\n response: Response,\n logger?: SseLogger\n): Promise<TicketResult> => {\n const body: unknown = await response.json().catch(() => null);\n if (!hasTicket(body)) {\n logger?.warn(\n `sse: 티켓 응답(${response.status}) 본문이 계약({ data: { ticket } })과 다릅니다 — 재시도는 계속합니다`\n );\n return { ok: false, error: { status: response.status } };\n }\n return { ok: true, ticket: body.data.ticket };\n};\n\n/**\n * @description 연결 티켓을 발급합니다 — TTL 15s이므로 발급 즉시 연결해야 합니다\n */\nexport const issueTicket = async (\n options: ServerContractOptions\n): Promise<TicketResult> => {\n const fetchImpl = options.fetch ?? globalThis.fetch;\n\n try {\n const token = await resolveAuthToken(options.getAuthToken);\n const response = await fetchImpl(`${options.baseUrl}/v1/events/ticket`, {\n method: 'POST',\n headers: authHeaders(token),\n });\n\n if (!response.ok) {\n return { ok: false, error: { status: response.status } };\n }\n\n return await parseTicketBody(response, options.logger);\n } catch (error) {\n if (error instanceof AuthTokenError) {\n // 정지 결정은 SseClient 몫이지만, 로그는 원인 예외가 있는 여기서 한 번만 — 워커 경로에선 탭 스코프라 유일한 기록입니다\n options.logger?.error(\n 'sse: getAuthToken이 예외를 던져 멈춥니다 — 콜백은 실패 시 null을 반환해야 합니다',\n error.cause\n );\n return TICKET_AUTH_CALLBACK_FAILURE;\n }\n return TICKET_NETWORK_FAILURE;\n }\n};\n\n/**\n * @description 티켓 발급에 시간 제한을 겁니다 — 타임아웃 이후 도착한 응답은 race가 이미 완료돼 무시됩니다\n */\nexport const issueTicketWithTimeout = async (\n options: ServerContractOptions\n): Promise<TicketResult> => {\n let ticketTimer: ReturnType<typeof setTimeout> | undefined;\n\n const ticketTimeout = new Promise<TicketResult>((resolve) => {\n ticketTimer = setTimeout(\n () => resolve(TICKET_NETWORK_FAILURE),\n TICKET_TIMEOUT_MS\n );\n });\n\n const result = await Promise.race([issueTicket(options), ticketTimeout]);\n clearTimeout(ticketTimer);\n\n return result;\n};\n"],"names":["TICKET_TIMEOUT_MS","TICKET_NETWORK_FAILURE","Object","freeze","ok","error","TICKET_AUTH_CALLBACK_FAILURE","cause","AuthTokenError","_Error","message","options","_this","_classCallCheck","_callSuper","name","_inherits","_createClass","_wrapNativeSuper","Error","resolveAuthToken","_ref","_asyncToGenerator","_regeneratorRuntime","mark","_callee","getAuthToken","wrap","_callee$","_context","prev","next","abrupt","sent","t0","stop","_x","apply","arguments","authHeaders","token","undefined","Authorization","concat","hasTicket","body","_typeof","_ref2","data","ticket","parseTicketBody","_ref3","_callee2","response","logger","_callee2$","_context2","json","warn","status","_x2","_x3","issueTicket","_ref4","_callee3","_options$fetch","fetchImpl","_options$logger","_callee3$","_context3","fetch","globalThis","baseUrl","method","headers","_x4","issueTicketWithTimeout","_ref5","_callee4","ticketTimer","ticketTimeout","result","_callee4$","_context4","Promise","resolve","setTimeout","race","clearTimeout","_x5"],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ // 상태 이름의 단일 원천 — 타입과 런타임 멤버십 검사가 같은 목록을 봅니다
4
+ var SSE_STATUSES = ['idle', 'connecting', 'open', 'reconnecting', 'closed', 'fatal'];
5
+ /** @description postMessage 신뢰 경계용 — 버전이 다른 상대가 보낸 미지 상태값이 타입 보증을 깨지 않게 거릅니다 */
6
+ var isSseStatus = function isSseStatus(value) {
7
+ return SSE_STATUSES.includes(value);
8
+ };
9
+ // goaway 값의 단일 원천 — 타입과 런타임 검사가 같은 목록을 봅니다
10
+ var GOAWAY_REASONS = ['deploy', 'user_logout', 'maintenance', 'manual'];
11
+ var GOAWAY_ACTIONS = ['retry', 'close'];
12
+ /**
13
+ * @description 포트·스트림 너머의 값을 좁힙니다 — 미지 값은 없는 것으로 봅니다
14
+ */
15
+ var isGoawayReason = function isGoawayReason(value) {
16
+ return GOAWAY_REASONS.includes(value);
17
+ };
18
+ var isGoawayAction = function isGoawayAction(value) {
19
+ return GOAWAY_ACTIONS.includes(value);
20
+ };
21
+
22
+ /**
23
+ * @description 연결이 fatal로 멈춘 사유
24
+ * - auth = 티켓 401/403
25
+ * - auth_callback = getAuthToken이 던짐 (소비처 콜백 문제 — 콜백은 실패 시 throw 대신 null을 반환해야 합니다)
26
+ * - rate_limit = 티켓 429 ·
27
+ * - blocked = 워커 생성 차단(CSP 등)
28
+ * - config = EventSource 생성 실패(재시도해도 되살아나지 않습니다)
29
+ * - unknown = 연결 루프의 예상 못 한 예외.
30
+ *
31
+ * 회복(사유 해소 후 disconnect → connect)은 이 이벤트의 핸들러에서 시작하세요
32
+ * - status 리스너에서 끊으면 워커 경로에선 뒤따라오던 사유가 포트와 함께 버려집니다.
33
+ * 사유는 발화 순간의 구독자에게만 배달되므로 핸들러는 앱 루트에 고정하세요.
34
+ */
35
+
36
+ /**
37
+ * @description 재연결 등 SDK 수명주기 이벤트 — 도메인 이벤트 맵(M)과 병합되어 on()/wire()에 노출됩니다.
38
+ * `resync`·`goaway`·`fatal`·`stalled`는 SDK 예약 이벤트명이므로 서버 이벤트 맵에 같은 이름을 정의하지 않습니다.
39
+ */
40
+
41
+ /**
42
+ * @description 리터럴 유니온에 흡수되지 않는 string 인터섹션으로, 자동완성을 유지한 채 임의 이름을 허용합니다
43
+ */
44
+
45
+ /** @description 맵에 있는 이름이면 그 페이로드, 없으면 unknown — "미지 이벤트는 무시" 런타임과 타입을 일치시킵니다 */
46
+
47
+ /**
48
+ * @description 클라이언트 구현들이 공유하는 공개 표면 — M은 interface·type 어느 쪽 맵도 받도록 object로 제약합니다
49
+ */
50
+
51
+ exports.isGoawayAction = isGoawayAction;
52
+ exports.isGoawayReason = isGoawayReason;
53
+ exports.isSseStatus = isSseStatus;
54
+ //# sourceMappingURL=types.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs.js","sources":["../../../src/core/types.ts"],"sourcesContent":["import type { BackoffOptions } from './backoff';\n\n// 상태 이름의 단일 원천 — 타입과 런타임 멤버십 검사가 같은 목록을 봅니다\nconst SSE_STATUSES = [\n 'idle',\n 'connecting',\n 'open',\n 'reconnecting',\n 'closed',\n 'fatal',\n] as const;\n\nexport type SseStatus = (typeof SSE_STATUSES)[number];\n\n/** @description postMessage 신뢰 경계용 — 버전이 다른 상대가 보낸 미지 상태값이 타입 보증을 깨지 않게 거릅니다 */\nexport const isSseStatus = (value: unknown): value is SseStatus =>\n (SSE_STATUSES as readonly unknown[]).includes(value);\n\nexport type SseEventMap = Record<string, unknown>;\n\n// goaway 값의 단일 원천 — 타입과 런타임 검사가 같은 목록을 봅니다\nconst GOAWAY_REASONS = [\n 'deploy',\n 'user_logout',\n 'maintenance',\n 'manual',\n] as const;\nconst GOAWAY_ACTIONS = ['retry', 'close'] as const;\n\nexport type GoawayReason = (typeof GOAWAY_REASONS)[number];\nexport type GoawayAction = (typeof GOAWAY_ACTIONS)[number];\n\n/**\n * @description 포트·스트림 너머의 값을 좁힙니다 — 미지 값은 없는 것으로 봅니다\n */\nexport const isGoawayReason = (value: unknown): value is GoawayReason =>\n (GOAWAY_REASONS as readonly unknown[]).includes(value);\n\nexport const isGoawayAction = (value: unknown): value is GoawayAction =>\n (GOAWAY_ACTIONS as readonly unknown[]).includes(value);\n\n/**\n * @description 서버가 연결을 닫으며 알려주는 사정 — SDK는 action만 보고, reason 대응은 소비처 몫입니다.\n * 구버전 서버·잘린 프레임에서는 필드가 없을 수 있어 둘 다 선택입니다.\n */\nexport interface GoawayPayload {\n reason?: GoawayReason;\n action?: GoawayAction;\n}\n\n// fatal 값의 단일 원천 — 소비처가 원인별 안내(재로그인·한도·환경 차단)를 가르는 기준입니다\nconst FATAL_CAUSES = [\n 'auth',\n 'auth_callback',\n 'rate_limit',\n 'blocked',\n 'config',\n 'unknown',\n] as const;\n\nexport type FatalCause = (typeof FATAL_CAUSES)[number];\n\n/**\n * @description 연결이 fatal로 멈춘 사유\n * - auth = 티켓 401/403\n * - auth_callback = getAuthToken이 던짐 (소비처 콜백 문제 — 콜백은 실패 시 throw 대신 null을 반환해야 합니다)\n * - rate_limit = 티켓 429 ·\n * - blocked = 워커 생성 차단(CSP 등)\n * - config = EventSource 생성 실패(재시도해도 되살아나지 않습니다)\n * - unknown = 연결 루프의 예상 못 한 예외.\n *\n * 회복(사유 해소 후 disconnect → connect)은 이 이벤트의 핸들러에서 시작하세요\n * - status 리스너에서 끊으면 워커 경로에선 뒤따라오던 사유가 포트와 함께 버려집니다.\n * 사유는 발화 순간의 구독자에게만 배달되므로 핸들러는 앱 루트에 고정하세요.\n */\nexport interface FatalPayload {\n cause: FatalCause;\n /** @description 티켓 발급 거부일 때만 — 서버가 준 HTTP 상태 코드 */\n status?: number;\n}\n\n/**\n * @description 재연결 등 SDK 수명주기 이벤트 — 도메인 이벤트 맵(M)과 병합되어 on()/wire()에 노출됩니다.\n * `resync`·`goaway`·`fatal`·`stalled`는 SDK 예약 이벤트명이므로 서버 이벤트 맵에 같은 이름을 정의하지 않습니다.\n */\nexport type WithLifecycle<M extends object> = M & {\n /**\n * @description 재수립 후 \"놓친 구간을 다시 조회하라\"는 신호 — 끊긴 사이의 정합성은 REST 재조회로 메웁니다.\n * (SDK 합성 — 와이어에 없는 이벤트입니다. 재연결 후 첫 hello에서 resync 이벤트가 발행됩니다)\n */\n resync: void;\n /**\n * @description 서버의 예고된 끊음 — `action: 'retry'`면 SDK가 즉시 재수립하고, `'close'`면 재연결 없이 닫습니다.\n * 배포·로그아웃·점검처럼 에러가 아닐 수 있으니 안내는 close에서만 reason별로 고려하세요.\n * (서버 발신 — 와이어의 `event: goaway`를 그대로 전달합니다)\n */\n goaway: GoawayPayload;\n /**\n * @description SDK의 포기 선언 — 재시도 없이 멈췄습니다. `cause`로 사유를 가르고,\n * 사유 해소 후 disconnect() → connect()로 회복합니다.\n * (SDK 합성 — 와이어에 없는 이벤트입니다. SDK가 실패를 판정해 발화합니다)\n */\n fatal: FatalPayload;\n /**\n * @description 네트워크 에러가 지속되고 있다는 진행형 통지 — fatal과 달리 재시도는 계속됩니다.\n * 응답 status가 없는 실패(오프라인·CORS 차단·접속 거부 — 브라우저는 서로 구분해주지 않습니다)가\n * hello에 닿지 못한 채 연속 5회 이어지는 순간 1회 발화합니다. 서버가 응답한 거부(5xx)는\n * 네트워크 경로가 살아 있다는 증거라 세지 않고 연속을 끊습니다.\n * 회복 시 별도 이벤트는 없습니다 — status가 open으로 바뀌는 것으로 알 수 있고, 판정은 그때 리셋됩니다.\n * (SDK 합성 — 와이어에 없는 이벤트입니다. SDK가 연속 실패를 세어 발화합니다)\n */\n stalled: void;\n};\n\nexport interface SseLogger {\n warn(message: string, meta?: unknown): void;\n error(message: string, meta?: unknown): void;\n}\n\nexport interface SseClientOptions {\n baseUrl: string;\n /**\n * @description 티켓 발급 요청에만 사용합니다 — null이면 인증 헤더 없이 요청합니다.\n * 실패는 throw 대신 null로 알리세요 — 예외가 새어 나오면 재시도 없이 fatal(cause: 'auth_callback')로 멈춥니다.\n */\n getAuthToken: () => string | null | Promise<string | null>;\n fetch?: typeof globalThis.fetch;\n /** @description 워커 경로(sharedWorker 제공 시)에서는 적용되지 않습니다 — 스트림은 워커 스코프에서 만들어집니다 */\n eventSource?: typeof EventSource;\n backoff?: Partial<BackoffOptions>;\n logger?: SseLogger;\n /**\n * @description 탭 간 연결 공유용 SharedWorker 팩토리 — 미제공 시 탭마다 직접 연결합니다.\n * 워커 스크립트 URL은 소비처 번들러가 해석해야 하므로 인스턴스가 아니라 팩토리를 받습니다.\n * @example () => new SharedWorker(new URL('@remember-web/sse/worker', import.meta.url), { name: 'rwp-sse', type: 'module' })\n */\n sharedWorker?: () => SharedWorker;\n}\n\nexport type SseEventHandler<Payload> = (payload: Payload) => void;\n\nexport type Unsubscribe = () => void;\n\n/**\n * @description 리터럴 유니온에 흡수되지 않는 string 인터섹션으로, 자동완성을 유지한 채 임의 이름을 허용합니다\n */\nexport type SseEventName<M extends object> =\n | (keyof WithLifecycle<M> & string)\n | (string & Record<never, never>);\n\n/** @description 맵에 있는 이름이면 그 페이로드, 없으면 unknown — \"미지 이벤트는 무시\" 런타임과 타입을 일치시킵니다 */\nexport type PayloadOf<\n M extends object,\n K extends string,\n> = K extends keyof WithLifecycle<M> ? WithLifecycle<M>[K] : unknown;\n\n/**\n * @description 클라이언트 구현들이 공유하는 공개 표면 — M은 interface·type 어느 쪽 맵도 받도록 object로 제약합니다\n */\nexport interface SseClientLike<M extends object = SseEventMap> {\n /** @description 타입 전용 마커 — M이 조건부 타입 안에만 있으면 wire() 등에서 M 추론이 불가능해집니다 */\n readonly __eventMap?: M;\n readonly status: SseStatus;\n connect(): void;\n disconnect(): void;\n /**\n * @description 이벤트 구독 — 해제 함수를 반환합니다. 구독은 connect()보다 먼저 걸어두세요.\n * @param type 이벤트 이름. 도메인 이벤트 외 예약 4종 —\n * `fatal`(SDK의 포기 선언 — 재시도 없이 멈춤, SDK 합성) ·\n * `stalled`(재시도가 계속되는 진행형 통지, SDK 합성) ·\n * `goaway`(서버의 예고된 끊음 — `action: 'close'`만 안내 대상, 서버 발신) ·\n * `resync`(재수립 후 놓친 구간을 REST로 재조회하라는 신호, SDK 합성). 상세는 `WithLifecycle` 문서 참조.\n * @param handler 이벤트 페이로드를 받는 콜백 — 발화 순간의 구독자에게만 배달됩니다\n */\n on<K extends SseEventName<M>>(\n type: K,\n handler: SseEventHandler<PayloadOf<M, K>>\n ): Unsubscribe;\n onStatus(listener: (status: SseStatus) => void): Unsubscribe;\n /** @description 탭이 돌아올 때 자동으로 connect()를 부릅니다 — 반환값으로 떼세요 */\n attachAutoResume(): Unsubscribe;\n}\n"],"names":["SSE_STATUSES","isSseStatus","value","includes","GOAWAY_REASONS","GOAWAY_ACTIONS","isGoawayReason","isGoawayAction"],"mappings":";;AAEA;AACA,IAAMA,YAAY,GAAG,CACnB,MAAM,EACN,YAAY,EACZ,MAAM,EACN,cAAc,EACd,QAAQ,EACR,OAAO,CACC;AAIV;IACaC,WAAW,GAAG,SAAdA,WAAWA,CAAIC,KAAc,EAAA;AAAA,EAAA,OACvCF,YAAY,CAAwBG,QAAQ,CAACD,KAAK,CAAC;AAAA;AAItD;AACA,IAAME,cAAc,GAAG,CACrB,QAAQ,EACR,aAAa,EACb,aAAa,EACb,QAAQ,CACA;AACV,IAAMC,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,CAAU;AAKlD;AACA;AACA;IACaC,cAAc,GAAG,SAAjBA,cAAcA,CAAIJ,KAAc,EAAA;AAAA,EAAA,OAC1CE,cAAc,CAAwBD,QAAQ,CAACD,KAAK,CAAC;AAAA;IAE3CK,cAAc,GAAG,SAAjBA,cAAcA,CAAIL,KAAc,EAAA;AAAA,EAAA,OAC1CG,cAAc,CAAwBF,QAAQ,CAACD,KAAK,CAAC;AAAA;;AAuBxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;AACA;AACA;AACA;;AA2DA;AACA;AACA;;AAKA;;AAMA;AACA;AACA;;;;;;"}
@@ -0,0 +1,131 @@
1
+ import type { BackoffOptions } from './backoff';
2
+ declare const SSE_STATUSES: readonly ["idle", "connecting", "open", "reconnecting", "closed", "fatal"];
3
+ export type SseStatus = (typeof SSE_STATUSES)[number];
4
+ /** @description postMessage 신뢰 경계용 — 버전이 다른 상대가 보낸 미지 상태값이 타입 보증을 깨지 않게 거릅니다 */
5
+ export declare const isSseStatus: (value: unknown) => value is "idle" | "connecting" | "open" | "reconnecting" | "closed" | "fatal";
6
+ export type SseEventMap = Record<string, unknown>;
7
+ declare const GOAWAY_REASONS: readonly ["deploy", "user_logout", "maintenance", "manual"];
8
+ declare const GOAWAY_ACTIONS: readonly ["retry", "close"];
9
+ export type GoawayReason = (typeof GOAWAY_REASONS)[number];
10
+ export type GoawayAction = (typeof GOAWAY_ACTIONS)[number];
11
+ /**
12
+ * @description 포트·스트림 너머의 값을 좁힙니다 — 미지 값은 없는 것으로 봅니다
13
+ */
14
+ export declare const isGoawayReason: (value: unknown) => value is "deploy" | "user_logout" | "maintenance" | "manual";
15
+ export declare const isGoawayAction: (value: unknown) => value is "retry" | "close";
16
+ /**
17
+ * @description 서버가 연결을 닫으며 알려주는 사정 — SDK는 action만 보고, reason 대응은 소비처 몫입니다.
18
+ * 구버전 서버·잘린 프레임에서는 필드가 없을 수 있어 둘 다 선택입니다.
19
+ */
20
+ export interface GoawayPayload {
21
+ reason?: GoawayReason;
22
+ action?: GoawayAction;
23
+ }
24
+ declare const FATAL_CAUSES: readonly ["auth", "auth_callback", "rate_limit", "blocked", "config", "unknown"];
25
+ export type FatalCause = (typeof FATAL_CAUSES)[number];
26
+ /**
27
+ * @description 연결이 fatal로 멈춘 사유
28
+ * - auth = 티켓 401/403
29
+ * - auth_callback = getAuthToken이 던짐 (소비처 콜백 문제 — 콜백은 실패 시 throw 대신 null을 반환해야 합니다)
30
+ * - rate_limit = 티켓 429 ·
31
+ * - blocked = 워커 생성 차단(CSP 등)
32
+ * - config = EventSource 생성 실패(재시도해도 되살아나지 않습니다)
33
+ * - unknown = 연결 루프의 예상 못 한 예외.
34
+ *
35
+ * 회복(사유 해소 후 disconnect → connect)은 이 이벤트의 핸들러에서 시작하세요
36
+ * - status 리스너에서 끊으면 워커 경로에선 뒤따라오던 사유가 포트와 함께 버려집니다.
37
+ * 사유는 발화 순간의 구독자에게만 배달되므로 핸들러는 앱 루트에 고정하세요.
38
+ */
39
+ export interface FatalPayload {
40
+ cause: FatalCause;
41
+ /** @description 티켓 발급 거부일 때만 — 서버가 준 HTTP 상태 코드 */
42
+ status?: number;
43
+ }
44
+ /**
45
+ * @description 재연결 등 SDK 수명주기 이벤트 — 도메인 이벤트 맵(M)과 병합되어 on()/wire()에 노출됩니다.
46
+ * `resync`·`goaway`·`fatal`·`stalled`는 SDK 예약 이벤트명이므로 서버 이벤트 맵에 같은 이름을 정의하지 않습니다.
47
+ */
48
+ export type WithLifecycle<M extends object> = M & {
49
+ /**
50
+ * @description 재수립 후 "놓친 구간을 다시 조회하라"는 신호 — 끊긴 사이의 정합성은 REST 재조회로 메웁니다.
51
+ * (SDK 합성 — 와이어에 없는 이벤트입니다. 재연결 후 첫 hello에서 resync 이벤트가 발행됩니다)
52
+ */
53
+ resync: void;
54
+ /**
55
+ * @description 서버의 예고된 끊음 — `action: 'retry'`면 SDK가 즉시 재수립하고, `'close'`면 재연결 없이 닫습니다.
56
+ * 배포·로그아웃·점검처럼 에러가 아닐 수 있으니 안내는 close에서만 reason별로 고려하세요.
57
+ * (서버 발신 — 와이어의 `event: goaway`를 그대로 전달합니다)
58
+ */
59
+ goaway: GoawayPayload;
60
+ /**
61
+ * @description SDK의 포기 선언 — 재시도 없이 멈췄습니다. `cause`로 사유를 가르고,
62
+ * 사유 해소 후 disconnect() → connect()로 회복합니다.
63
+ * (SDK 합성 — 와이어에 없는 이벤트입니다. SDK가 실패를 판정해 발화합니다)
64
+ */
65
+ fatal: FatalPayload;
66
+ /**
67
+ * @description 네트워크 에러가 지속되고 있다는 진행형 통지 — fatal과 달리 재시도는 계속됩니다.
68
+ * 응답 status가 없는 실패(오프라인·CORS 차단·접속 거부 — 브라우저는 서로 구분해주지 않습니다)가
69
+ * hello에 닿지 못한 채 연속 5회 이어지는 순간 1회 발화합니다. 서버가 응답한 거부(5xx)는
70
+ * 네트워크 경로가 살아 있다는 증거라 세지 않고 연속을 끊습니다.
71
+ * 회복 시 별도 이벤트는 없습니다 — status가 open으로 바뀌는 것으로 알 수 있고, 판정은 그때 리셋됩니다.
72
+ * (SDK 합성 — 와이어에 없는 이벤트입니다. SDK가 연속 실패를 세어 발화합니다)
73
+ */
74
+ stalled: void;
75
+ };
76
+ export interface SseLogger {
77
+ warn(message: string, meta?: unknown): void;
78
+ error(message: string, meta?: unknown): void;
79
+ }
80
+ export interface SseClientOptions {
81
+ baseUrl: string;
82
+ /**
83
+ * @description 티켓 발급 요청에만 사용합니다 — null이면 인증 헤더 없이 요청합니다.
84
+ * 실패는 throw 대신 null로 알리세요 — 예외가 새어 나오면 재시도 없이 fatal(cause: 'auth_callback')로 멈춥니다.
85
+ */
86
+ getAuthToken: () => string | null | Promise<string | null>;
87
+ fetch?: typeof globalThis.fetch;
88
+ /** @description 워커 경로(sharedWorker 제공 시)에서는 적용되지 않습니다 — 스트림은 워커 스코프에서 만들어집니다 */
89
+ eventSource?: typeof EventSource;
90
+ backoff?: Partial<BackoffOptions>;
91
+ logger?: SseLogger;
92
+ /**
93
+ * @description 탭 간 연결 공유용 SharedWorker 팩토리 — 미제공 시 탭마다 직접 연결합니다.
94
+ * 워커 스크립트 URL은 소비처 번들러가 해석해야 하므로 인스턴스가 아니라 팩토리를 받습니다.
95
+ * @example () => new SharedWorker(new URL('@remember-web/sse/worker', import.meta.url), { name: 'rwp-sse', type: 'module' })
96
+ */
97
+ sharedWorker?: () => SharedWorker;
98
+ }
99
+ export type SseEventHandler<Payload> = (payload: Payload) => void;
100
+ export type Unsubscribe = () => void;
101
+ /**
102
+ * @description 리터럴 유니온에 흡수되지 않는 string 인터섹션으로, 자동완성을 유지한 채 임의 이름을 허용합니다
103
+ */
104
+ export type SseEventName<M extends object> = (keyof WithLifecycle<M> & string) | (string & Record<never, never>);
105
+ /** @description 맵에 있는 이름이면 그 페이로드, 없으면 unknown — "미지 이벤트는 무시" 런타임과 타입을 일치시킵니다 */
106
+ export type PayloadOf<M extends object, K extends string> = K extends keyof WithLifecycle<M> ? WithLifecycle<M>[K] : unknown;
107
+ /**
108
+ * @description 클라이언트 구현들이 공유하는 공개 표면 — M은 interface·type 어느 쪽 맵도 받도록 object로 제약합니다
109
+ */
110
+ export interface SseClientLike<M extends object = SseEventMap> {
111
+ /** @description 타입 전용 마커 — M이 조건부 타입 안에만 있으면 wire() 등에서 M 추론이 불가능해집니다 */
112
+ readonly __eventMap?: M;
113
+ readonly status: SseStatus;
114
+ connect(): void;
115
+ disconnect(): void;
116
+ /**
117
+ * @description 이벤트 구독 — 해제 함수를 반환합니다. 구독은 connect()보다 먼저 걸어두세요.
118
+ * @param type 이벤트 이름. 도메인 이벤트 외 예약 4종 —
119
+ * `fatal`(SDK의 포기 선언 — 재시도 없이 멈춤, SDK 합성) ·
120
+ * `stalled`(재시도가 계속되는 진행형 통지, SDK 합성) ·
121
+ * `goaway`(서버의 예고된 끊음 — `action: 'close'`만 안내 대상, 서버 발신) ·
122
+ * `resync`(재수립 후 놓친 구간을 REST로 재조회하라는 신호, SDK 합성). 상세는 `WithLifecycle` 문서 참조.
123
+ * @param handler 이벤트 페이로드를 받는 콜백 — 발화 순간의 구독자에게만 배달됩니다
124
+ */
125
+ on<K extends SseEventName<M>>(type: K, handler: SseEventHandler<PayloadOf<M, K>>): Unsubscribe;
126
+ onStatus(listener: (status: SseStatus) => void): Unsubscribe;
127
+ /** @description 탭이 돌아올 때 자동으로 connect()를 부릅니다 — 반환값으로 떼세요 */
128
+ attachAutoResume(): Unsubscribe;
129
+ }
130
+ export {};
131
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ // 상태 이름의 단일 원천 — 타입과 런타임 멤버십 검사가 같은 목록을 봅니다
2
+ var SSE_STATUSES = ['idle', 'connecting', 'open', 'reconnecting', 'closed', 'fatal'];
3
+ /** @description postMessage 신뢰 경계용 — 버전이 다른 상대가 보낸 미지 상태값이 타입 보증을 깨지 않게 거릅니다 */
4
+ var isSseStatus = function isSseStatus(value) {
5
+ return SSE_STATUSES.includes(value);
6
+ };
7
+ // goaway 값의 단일 원천 — 타입과 런타임 검사가 같은 목록을 봅니다
8
+ var GOAWAY_REASONS = ['deploy', 'user_logout', 'maintenance', 'manual'];
9
+ var GOAWAY_ACTIONS = ['retry', 'close'];
10
+ /**
11
+ * @description 포트·스트림 너머의 값을 좁힙니다 — 미지 값은 없는 것으로 봅니다
12
+ */
13
+ var isGoawayReason = function isGoawayReason(value) {
14
+ return GOAWAY_REASONS.includes(value);
15
+ };
16
+ var isGoawayAction = function isGoawayAction(value) {
17
+ return GOAWAY_ACTIONS.includes(value);
18
+ };
19
+
20
+ /**
21
+ * @description 연결이 fatal로 멈춘 사유
22
+ * - auth = 티켓 401/403
23
+ * - auth_callback = getAuthToken이 던짐 (소비처 콜백 문제 — 콜백은 실패 시 throw 대신 null을 반환해야 합니다)
24
+ * - rate_limit = 티켓 429 ·
25
+ * - blocked = 워커 생성 차단(CSP 등)
26
+ * - config = EventSource 생성 실패(재시도해도 되살아나지 않습니다)
27
+ * - unknown = 연결 루프의 예상 못 한 예외.
28
+ *
29
+ * 회복(사유 해소 후 disconnect → connect)은 이 이벤트의 핸들러에서 시작하세요
30
+ * - status 리스너에서 끊으면 워커 경로에선 뒤따라오던 사유가 포트와 함께 버려집니다.
31
+ * 사유는 발화 순간의 구독자에게만 배달되므로 핸들러는 앱 루트에 고정하세요.
32
+ */
33
+
34
+ /**
35
+ * @description 재연결 등 SDK 수명주기 이벤트 — 도메인 이벤트 맵(M)과 병합되어 on()/wire()에 노출됩니다.
36
+ * `resync`·`goaway`·`fatal`·`stalled`는 SDK 예약 이벤트명이므로 서버 이벤트 맵에 같은 이름을 정의하지 않습니다.
37
+ */
38
+
39
+ /**
40
+ * @description 리터럴 유니온에 흡수되지 않는 string 인터섹션으로, 자동완성을 유지한 채 임의 이름을 허용합니다
41
+ */
42
+
43
+ /** @description 맵에 있는 이름이면 그 페이로드, 없으면 unknown — "미지 이벤트는 무시" 런타임과 타입을 일치시킵니다 */
44
+
45
+ /**
46
+ * @description 클라이언트 구현들이 공유하는 공개 표면 — M은 interface·type 어느 쪽 맵도 받도록 object로 제약합니다
47
+ */
48
+
49
+ export { isGoawayAction, isGoawayReason, isSseStatus };
50
+ //# sourceMappingURL=types.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.esm.js","sources":["../../../src/core/types.ts"],"sourcesContent":["import type { BackoffOptions } from './backoff';\n\n// 상태 이름의 단일 원천 — 타입과 런타임 멤버십 검사가 같은 목록을 봅니다\nconst SSE_STATUSES = [\n 'idle',\n 'connecting',\n 'open',\n 'reconnecting',\n 'closed',\n 'fatal',\n] as const;\n\nexport type SseStatus = (typeof SSE_STATUSES)[number];\n\n/** @description postMessage 신뢰 경계용 — 버전이 다른 상대가 보낸 미지 상태값이 타입 보증을 깨지 않게 거릅니다 */\nexport const isSseStatus = (value: unknown): value is SseStatus =>\n (SSE_STATUSES as readonly unknown[]).includes(value);\n\nexport type SseEventMap = Record<string, unknown>;\n\n// goaway 값의 단일 원천 — 타입과 런타임 검사가 같은 목록을 봅니다\nconst GOAWAY_REASONS = [\n 'deploy',\n 'user_logout',\n 'maintenance',\n 'manual',\n] as const;\nconst GOAWAY_ACTIONS = ['retry', 'close'] as const;\n\nexport type GoawayReason = (typeof GOAWAY_REASONS)[number];\nexport type GoawayAction = (typeof GOAWAY_ACTIONS)[number];\n\n/**\n * @description 포트·스트림 너머의 값을 좁힙니다 — 미지 값은 없는 것으로 봅니다\n */\nexport const isGoawayReason = (value: unknown): value is GoawayReason =>\n (GOAWAY_REASONS as readonly unknown[]).includes(value);\n\nexport const isGoawayAction = (value: unknown): value is GoawayAction =>\n (GOAWAY_ACTIONS as readonly unknown[]).includes(value);\n\n/**\n * @description 서버가 연결을 닫으며 알려주는 사정 — SDK는 action만 보고, reason 대응은 소비처 몫입니다.\n * 구버전 서버·잘린 프레임에서는 필드가 없을 수 있어 둘 다 선택입니다.\n */\nexport interface GoawayPayload {\n reason?: GoawayReason;\n action?: GoawayAction;\n}\n\n// fatal 값의 단일 원천 — 소비처가 원인별 안내(재로그인·한도·환경 차단)를 가르는 기준입니다\nconst FATAL_CAUSES = [\n 'auth',\n 'auth_callback',\n 'rate_limit',\n 'blocked',\n 'config',\n 'unknown',\n] as const;\n\nexport type FatalCause = (typeof FATAL_CAUSES)[number];\n\n/**\n * @description 연결이 fatal로 멈춘 사유\n * - auth = 티켓 401/403\n * - auth_callback = getAuthToken이 던짐 (소비처 콜백 문제 — 콜백은 실패 시 throw 대신 null을 반환해야 합니다)\n * - rate_limit = 티켓 429 ·\n * - blocked = 워커 생성 차단(CSP 등)\n * - config = EventSource 생성 실패(재시도해도 되살아나지 않습니다)\n * - unknown = 연결 루프의 예상 못 한 예외.\n *\n * 회복(사유 해소 후 disconnect → connect)은 이 이벤트의 핸들러에서 시작하세요\n * - status 리스너에서 끊으면 워커 경로에선 뒤따라오던 사유가 포트와 함께 버려집니다.\n * 사유는 발화 순간의 구독자에게만 배달되므로 핸들러는 앱 루트에 고정하세요.\n */\nexport interface FatalPayload {\n cause: FatalCause;\n /** @description 티켓 발급 거부일 때만 — 서버가 준 HTTP 상태 코드 */\n status?: number;\n}\n\n/**\n * @description 재연결 등 SDK 수명주기 이벤트 — 도메인 이벤트 맵(M)과 병합되어 on()/wire()에 노출됩니다.\n * `resync`·`goaway`·`fatal`·`stalled`는 SDK 예약 이벤트명이므로 서버 이벤트 맵에 같은 이름을 정의하지 않습니다.\n */\nexport type WithLifecycle<M extends object> = M & {\n /**\n * @description 재수립 후 \"놓친 구간을 다시 조회하라\"는 신호 — 끊긴 사이의 정합성은 REST 재조회로 메웁니다.\n * (SDK 합성 — 와이어에 없는 이벤트입니다. 재연결 후 첫 hello에서 resync 이벤트가 발행됩니다)\n */\n resync: void;\n /**\n * @description 서버의 예고된 끊음 — `action: 'retry'`면 SDK가 즉시 재수립하고, `'close'`면 재연결 없이 닫습니다.\n * 배포·로그아웃·점검처럼 에러가 아닐 수 있으니 안내는 close에서만 reason별로 고려하세요.\n * (서버 발신 — 와이어의 `event: goaway`를 그대로 전달합니다)\n */\n goaway: GoawayPayload;\n /**\n * @description SDK의 포기 선언 — 재시도 없이 멈췄습니다. `cause`로 사유를 가르고,\n * 사유 해소 후 disconnect() → connect()로 회복합니다.\n * (SDK 합성 — 와이어에 없는 이벤트입니다. SDK가 실패를 판정해 발화합니다)\n */\n fatal: FatalPayload;\n /**\n * @description 네트워크 에러가 지속되고 있다는 진행형 통지 — fatal과 달리 재시도는 계속됩니다.\n * 응답 status가 없는 실패(오프라인·CORS 차단·접속 거부 — 브라우저는 서로 구분해주지 않습니다)가\n * hello에 닿지 못한 채 연속 5회 이어지는 순간 1회 발화합니다. 서버가 응답한 거부(5xx)는\n * 네트워크 경로가 살아 있다는 증거라 세지 않고 연속을 끊습니다.\n * 회복 시 별도 이벤트는 없습니다 — status가 open으로 바뀌는 것으로 알 수 있고, 판정은 그때 리셋됩니다.\n * (SDK 합성 — 와이어에 없는 이벤트입니다. SDK가 연속 실패를 세어 발화합니다)\n */\n stalled: void;\n};\n\nexport interface SseLogger {\n warn(message: string, meta?: unknown): void;\n error(message: string, meta?: unknown): void;\n}\n\nexport interface SseClientOptions {\n baseUrl: string;\n /**\n * @description 티켓 발급 요청에만 사용합니다 — null이면 인증 헤더 없이 요청합니다.\n * 실패는 throw 대신 null로 알리세요 — 예외가 새어 나오면 재시도 없이 fatal(cause: 'auth_callback')로 멈춥니다.\n */\n getAuthToken: () => string | null | Promise<string | null>;\n fetch?: typeof globalThis.fetch;\n /** @description 워커 경로(sharedWorker 제공 시)에서는 적용되지 않습니다 — 스트림은 워커 스코프에서 만들어집니다 */\n eventSource?: typeof EventSource;\n backoff?: Partial<BackoffOptions>;\n logger?: SseLogger;\n /**\n * @description 탭 간 연결 공유용 SharedWorker 팩토리 — 미제공 시 탭마다 직접 연결합니다.\n * 워커 스크립트 URL은 소비처 번들러가 해석해야 하므로 인스턴스가 아니라 팩토리를 받습니다.\n * @example () => new SharedWorker(new URL('@remember-web/sse/worker', import.meta.url), { name: 'rwp-sse', type: 'module' })\n */\n sharedWorker?: () => SharedWorker;\n}\n\nexport type SseEventHandler<Payload> = (payload: Payload) => void;\n\nexport type Unsubscribe = () => void;\n\n/**\n * @description 리터럴 유니온에 흡수되지 않는 string 인터섹션으로, 자동완성을 유지한 채 임의 이름을 허용합니다\n */\nexport type SseEventName<M extends object> =\n | (keyof WithLifecycle<M> & string)\n | (string & Record<never, never>);\n\n/** @description 맵에 있는 이름이면 그 페이로드, 없으면 unknown — \"미지 이벤트는 무시\" 런타임과 타입을 일치시킵니다 */\nexport type PayloadOf<\n M extends object,\n K extends string,\n> = K extends keyof WithLifecycle<M> ? WithLifecycle<M>[K] : unknown;\n\n/**\n * @description 클라이언트 구현들이 공유하는 공개 표면 — M은 interface·type 어느 쪽 맵도 받도록 object로 제약합니다\n */\nexport interface SseClientLike<M extends object = SseEventMap> {\n /** @description 타입 전용 마커 — M이 조건부 타입 안에만 있으면 wire() 등에서 M 추론이 불가능해집니다 */\n readonly __eventMap?: M;\n readonly status: SseStatus;\n connect(): void;\n disconnect(): void;\n /**\n * @description 이벤트 구독 — 해제 함수를 반환합니다. 구독은 connect()보다 먼저 걸어두세요.\n * @param type 이벤트 이름. 도메인 이벤트 외 예약 4종 —\n * `fatal`(SDK의 포기 선언 — 재시도 없이 멈춤, SDK 합성) ·\n * `stalled`(재시도가 계속되는 진행형 통지, SDK 합성) ·\n * `goaway`(서버의 예고된 끊음 — `action: 'close'`만 안내 대상, 서버 발신) ·\n * `resync`(재수립 후 놓친 구간을 REST로 재조회하라는 신호, SDK 합성). 상세는 `WithLifecycle` 문서 참조.\n * @param handler 이벤트 페이로드를 받는 콜백 — 발화 순간의 구독자에게만 배달됩니다\n */\n on<K extends SseEventName<M>>(\n type: K,\n handler: SseEventHandler<PayloadOf<M, K>>\n ): Unsubscribe;\n onStatus(listener: (status: SseStatus) => void): Unsubscribe;\n /** @description 탭이 돌아올 때 자동으로 connect()를 부릅니다 — 반환값으로 떼세요 */\n attachAutoResume(): Unsubscribe;\n}\n"],"names":["SSE_STATUSES","isSseStatus","value","includes","GOAWAY_REASONS","GOAWAY_ACTIONS","isGoawayReason","isGoawayAction"],"mappings":"AAEA;AACA,IAAMA,YAAY,GAAG,CACnB,MAAM,EACN,YAAY,EACZ,MAAM,EACN,cAAc,EACd,QAAQ,EACR,OAAO,CACC;AAIV;IACaC,WAAW,GAAG,SAAdA,WAAWA,CAAIC,KAAc,EAAA;AAAA,EAAA,OACvCF,YAAY,CAAwBG,QAAQ,CAACD,KAAK,CAAC;AAAA;AAItD;AACA,IAAME,cAAc,GAAG,CACrB,QAAQ,EACR,aAAa,EACb,aAAa,EACb,QAAQ,CACA;AACV,IAAMC,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,CAAU;AAKlD;AACA;AACA;IACaC,cAAc,GAAG,SAAjBA,cAAcA,CAAIJ,KAAc,EAAA;AAAA,EAAA,OAC1CE,cAAc,CAAwBD,QAAQ,CAACD,KAAK,CAAC;AAAA;IAE3CK,cAAc,GAAG,SAAjBA,cAAcA,CAAIL,KAAc,EAAA;AAAA,EAAA,OAC1CG,cAAc,CAAwBF,QAAQ,CAACD,KAAK,CAAC;AAAA;;AAuBxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;AACA;AACA;AACA;;AA2DA;AACA;AACA;;AAKA;;AAMA;AACA;AACA;;;;"}
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var _slicedToArray__default = /*#__PURE__*/_interopDefault(_slicedToArray);
8
+
9
+ /** @description 이벤트 핸들러 맵을 일괄 등록하고 전체 해제 함수를 반환합니다 — 호출 가능한 값만 등록합니다 */
10
+ function wire(client, handlers) {
11
+ var unsubscribers = Object.entries(handlers).filter(function (_ref) {
12
+ var _ref2 = _slicedToArray__default.default(_ref, 2),
13
+ handler = _ref2[1];
14
+ return typeof handler === 'function';
15
+ }).map(function (_ref3) {
16
+ var _ref4 = _slicedToArray__default.default(_ref3, 2),
17
+ type = _ref4[0],
18
+ handler = _ref4[1];
19
+ return client.on(type, handler);
20
+ });
21
+ return function () {
22
+ return unsubscribers.forEach(function (unsubscribe) {
23
+ return unsubscribe();
24
+ });
25
+ };
26
+ }
27
+
28
+ exports.wire = wire;
29
+ //# sourceMappingURL=wire.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.cjs.js","sources":["../../../src/core/wire.ts"],"sourcesContent":["import type {\n SseClientLike,\n SseEventHandler,\n Unsubscribe,\n WithLifecycle,\n} from './types';\n\nexport type WireHandlers<M extends object> = {\n [K in keyof WithLifecycle<M>]?: SseEventHandler<WithLifecycle<M>[K]>;\n};\n\n/** @description 이벤트 핸들러 맵을 일괄 등록하고 전체 해제 함수를 반환합니다 — 호출 가능한 값만 등록합니다 */\nexport function wire<M extends object>(\n client: SseClientLike<M>,\n handlers: WireHandlers<NoInfer<M>>\n): Unsubscribe {\n const unsubscribers = Object.entries(handlers)\n .filter(([, handler]) => typeof handler === 'function')\n .map(([type, handler]) =>\n client.on(type, handler as SseEventHandler<unknown>)\n );\n return () => unsubscribers.forEach((unsubscribe) => unsubscribe());\n}\n"],"names":["wire","client","handlers","unsubscribers","Object","entries","filter","_ref","_ref2","_slicedToArray","handler","map","_ref3","_ref4","type","on","forEach","unsubscribe"],"mappings":";;;;;;;;AAWA;AACO,SAASA,IAAIA,CAClBC,MAAwB,EACxBC,QAAkC,EACrB;AACb,EAAA,IAAMC,aAAa,GAAGC,MAAM,CAACC,OAAO,CAACH,QAAQ,CAAC,CAC3CI,MAAM,CAAC,UAAAC,IAAA,EAAA;AAAA,IAAA,IAAAC,KAAA,GAAAC,+BAAA,CAAAF,IAAA,EAAA,CAAA,CAAA;AAAIG,MAAAA,OAAO,GAAAF,KAAA,CAAA,CAAA,CAAA;IAAA,OAAM,OAAOE,OAAO,KAAK,UAAU;AAAA,GAAA,CAAC,CACtDC,GAAG,CAAC,UAAAC,KAAA,EAAA;AAAA,IAAA,IAAAC,KAAA,GAAAJ,+BAAA,CAAAG,KAAA,EAAA,CAAA,CAAA;AAAEE,MAAAA,IAAI,GAAAD,KAAA,CAAA,CAAA,CAAA;AAAEH,MAAAA,OAAO,GAAAG,KAAA,CAAA,CAAA,CAAA;AAAA,IAAA,OAClBZ,MAAM,CAACc,EAAE,CAACD,IAAI,EAAEJ,OAAmC,CAAC;AAAA,GACtD,CAAC;EACH,OAAO,YAAA;AAAA,IAAA,OAAMP,aAAa,CAACa,OAAO,CAAC,UAACC,WAAW,EAAA;MAAA,OAAKA,WAAW,EAAE;KAAA,CAAC;AAAA,GAAA;AACpE;;;;"}
@@ -0,0 +1,7 @@
1
+ import type { SseClientLike, SseEventHandler, Unsubscribe, WithLifecycle } from './types';
2
+ export type WireHandlers<M extends object> = {
3
+ [K in keyof WithLifecycle<M>]?: SseEventHandler<WithLifecycle<M>[K]>;
4
+ };
5
+ /** @description 이벤트 핸들러 맵을 일괄 등록하고 전체 해제 함수를 반환합니다 — 호출 가능한 값만 등록합니다 */
6
+ export declare function wire<M extends object>(client: SseClientLike<M>, handlers: WireHandlers<NoInfer<M>>): Unsubscribe;
7
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/core/wire.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI;KAC1C,CAAC,IAAI,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACrE,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EACnC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GACjC,WAAW,CAOb"}
@@ -0,0 +1,23 @@
1
+ import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
2
+
3
+ /** @description 이벤트 핸들러 맵을 일괄 등록하고 전체 해제 함수를 반환합니다 — 호출 가능한 값만 등록합니다 */
4
+ function wire(client, handlers) {
5
+ var unsubscribers = Object.entries(handlers).filter(function (_ref) {
6
+ var _ref2 = _slicedToArray(_ref, 2),
7
+ handler = _ref2[1];
8
+ return typeof handler === 'function';
9
+ }).map(function (_ref3) {
10
+ var _ref4 = _slicedToArray(_ref3, 2),
11
+ type = _ref4[0],
12
+ handler = _ref4[1];
13
+ return client.on(type, handler);
14
+ });
15
+ return function () {
16
+ return unsubscribers.forEach(function (unsubscribe) {
17
+ return unsubscribe();
18
+ });
19
+ };
20
+ }
21
+
22
+ export { wire };
23
+ //# sourceMappingURL=wire.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.esm.js","sources":["../../../src/core/wire.ts"],"sourcesContent":["import type {\n SseClientLike,\n SseEventHandler,\n Unsubscribe,\n WithLifecycle,\n} from './types';\n\nexport type WireHandlers<M extends object> = {\n [K in keyof WithLifecycle<M>]?: SseEventHandler<WithLifecycle<M>[K]>;\n};\n\n/** @description 이벤트 핸들러 맵을 일괄 등록하고 전체 해제 함수를 반환합니다 — 호출 가능한 값만 등록합니다 */\nexport function wire<M extends object>(\n client: SseClientLike<M>,\n handlers: WireHandlers<NoInfer<M>>\n): Unsubscribe {\n const unsubscribers = Object.entries(handlers)\n .filter(([, handler]) => typeof handler === 'function')\n .map(([type, handler]) =>\n client.on(type, handler as SseEventHandler<unknown>)\n );\n return () => unsubscribers.forEach((unsubscribe) => unsubscribe());\n}\n"],"names":["wire","client","handlers","unsubscribers","Object","entries","filter","_ref","_ref2","_slicedToArray","handler","map","_ref3","_ref4","type","on","forEach","unsubscribe"],"mappings":";;AAWA;AACO,SAASA,IAAIA,CAClBC,MAAwB,EACxBC,QAAkC,EACrB;AACb,EAAA,IAAMC,aAAa,GAAGC,MAAM,CAACC,OAAO,CAACH,QAAQ,CAAC,CAC3CI,MAAM,CAAC,UAAAC,IAAA,EAAA;AAAA,IAAA,IAAAC,KAAA,GAAAC,cAAA,CAAAF,IAAA,EAAA,CAAA,CAAA;AAAIG,MAAAA,OAAO,GAAAF,KAAA,CAAA,CAAA,CAAA;IAAA,OAAM,OAAOE,OAAO,KAAK,UAAU;AAAA,GAAA,CAAC,CACtDC,GAAG,CAAC,UAAAC,KAAA,EAAA;AAAA,IAAA,IAAAC,KAAA,GAAAJ,cAAA,CAAAG,KAAA,EAAA,CAAA,CAAA;AAAEE,MAAAA,IAAI,GAAAD,KAAA,CAAA,CAAA,CAAA;AAAEH,MAAAA,OAAO,GAAAG,KAAA,CAAA,CAAA,CAAA;AAAA,IAAA,OAClBZ,MAAM,CAACc,EAAE,CAACD,IAAI,EAAEJ,OAAmC,CAAC;AAAA,GACtD,CAAC;EACH,OAAO,YAAA;AAAA,IAAA,OAAMP,aAAa,CAACa,OAAO,CAAC,UAACC,WAAW,EAAA;MAAA,OAAKA,WAAW,EAAE;KAAA,CAAC;AAAA,GAAA;AACpE;;;;"}