@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,1865 @@
|
|
|
1
|
+
function _assertClassBrand(e, t, n) {
|
|
2
|
+
if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
|
|
3
|
+
throw new TypeError("Private element is not present on this object");
|
|
4
|
+
}
|
|
5
|
+
function _callSuper(t, o, e) {
|
|
6
|
+
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
|
|
7
|
+
}
|
|
8
|
+
function _classPrivateFieldGet2(s, a) {
|
|
9
|
+
return s.get(_assertClassBrand(s, a));
|
|
10
|
+
}
|
|
11
|
+
function _classPrivateFieldSet2(s, a, r) {
|
|
12
|
+
return s.set(_assertClassBrand(s, a), r), r;
|
|
13
|
+
}
|
|
14
|
+
function _construct(t, e, r) {
|
|
15
|
+
if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments);
|
|
16
|
+
var o = [null];
|
|
17
|
+
o.push.apply(o, e);
|
|
18
|
+
var p = new (t.bind.apply(t, o))();
|
|
19
|
+
return r && _setPrototypeOf(p, r.prototype), p;
|
|
20
|
+
}
|
|
21
|
+
function _isNativeReflectConstruct() {
|
|
22
|
+
try {
|
|
23
|
+
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
|
24
|
+
} catch (t) {}
|
|
25
|
+
return (_isNativeReflectConstruct = function () {
|
|
26
|
+
return !!t;
|
|
27
|
+
})();
|
|
28
|
+
}
|
|
29
|
+
function ownKeys(e, r) {
|
|
30
|
+
var t = Object.keys(e);
|
|
31
|
+
if (Object.getOwnPropertySymbols) {
|
|
32
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
33
|
+
r && (o = o.filter(function (r) {
|
|
34
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
35
|
+
})), t.push.apply(t, o);
|
|
36
|
+
}
|
|
37
|
+
return t;
|
|
38
|
+
}
|
|
39
|
+
function _objectSpread2(e) {
|
|
40
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
41
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
42
|
+
r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
|
|
43
|
+
_defineProperty(e, r, t[r]);
|
|
44
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
|
|
45
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return e;
|
|
49
|
+
}
|
|
50
|
+
function _regeneratorRuntime() {
|
|
51
|
+
_regeneratorRuntime = function () {
|
|
52
|
+
return e;
|
|
53
|
+
};
|
|
54
|
+
var t,
|
|
55
|
+
e = {},
|
|
56
|
+
r = Object.prototype,
|
|
57
|
+
n = r.hasOwnProperty,
|
|
58
|
+
o = Object.defineProperty || function (t, e, r) {
|
|
59
|
+
t[e] = r.value;
|
|
60
|
+
},
|
|
61
|
+
i = "function" == typeof Symbol ? Symbol : {},
|
|
62
|
+
a = i.iterator || "@@iterator",
|
|
63
|
+
c = i.asyncIterator || "@@asyncIterator",
|
|
64
|
+
u = i.toStringTag || "@@toStringTag";
|
|
65
|
+
function define(t, e, r) {
|
|
66
|
+
return Object.defineProperty(t, e, {
|
|
67
|
+
value: r,
|
|
68
|
+
enumerable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
writable: true
|
|
71
|
+
}), t[e];
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
define({}, "");
|
|
75
|
+
} catch (t) {
|
|
76
|
+
define = function (t, e, r) {
|
|
77
|
+
return t[e] = r;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function wrap(t, e, r, n) {
|
|
81
|
+
var i = e && e.prototype instanceof Generator ? e : Generator,
|
|
82
|
+
a = Object.create(i.prototype),
|
|
83
|
+
c = new Context(n || []);
|
|
84
|
+
return o(a, "_invoke", {
|
|
85
|
+
value: makeInvokeMethod(t, r, c)
|
|
86
|
+
}), a;
|
|
87
|
+
}
|
|
88
|
+
function tryCatch(t, e, r) {
|
|
89
|
+
try {
|
|
90
|
+
return {
|
|
91
|
+
type: "normal",
|
|
92
|
+
arg: t.call(e, r)
|
|
93
|
+
};
|
|
94
|
+
} catch (t) {
|
|
95
|
+
return {
|
|
96
|
+
type: "throw",
|
|
97
|
+
arg: t
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
e.wrap = wrap;
|
|
102
|
+
var h = "suspendedStart",
|
|
103
|
+
l = "suspendedYield",
|
|
104
|
+
f = "executing",
|
|
105
|
+
s = "completed",
|
|
106
|
+
y = {};
|
|
107
|
+
function Generator() {}
|
|
108
|
+
function GeneratorFunction() {}
|
|
109
|
+
function GeneratorFunctionPrototype() {}
|
|
110
|
+
var p = {};
|
|
111
|
+
define(p, a, function () {
|
|
112
|
+
return this;
|
|
113
|
+
});
|
|
114
|
+
var d = Object.getPrototypeOf,
|
|
115
|
+
v = d && d(d(values([])));
|
|
116
|
+
v && v !== r && n.call(v, a) && (p = v);
|
|
117
|
+
var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p);
|
|
118
|
+
function defineIteratorMethods(t) {
|
|
119
|
+
["next", "throw", "return"].forEach(function (e) {
|
|
120
|
+
define(t, e, function (t) {
|
|
121
|
+
return this._invoke(e, t);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function AsyncIterator(t, e) {
|
|
126
|
+
function invoke(r, o, i, a) {
|
|
127
|
+
var c = tryCatch(t[r], t, o);
|
|
128
|
+
if ("throw" !== c.type) {
|
|
129
|
+
var u = c.arg,
|
|
130
|
+
h = u.value;
|
|
131
|
+
return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) {
|
|
132
|
+
invoke("next", t, i, a);
|
|
133
|
+
}, function (t) {
|
|
134
|
+
invoke("throw", t, i, a);
|
|
135
|
+
}) : e.resolve(h).then(function (t) {
|
|
136
|
+
u.value = t, i(u);
|
|
137
|
+
}, function (t) {
|
|
138
|
+
return invoke("throw", t, i, a);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
a(c.arg);
|
|
142
|
+
}
|
|
143
|
+
var r;
|
|
144
|
+
o(this, "_invoke", {
|
|
145
|
+
value: function (t, n) {
|
|
146
|
+
function callInvokeWithMethodAndArg() {
|
|
147
|
+
return new e(function (e, r) {
|
|
148
|
+
invoke(t, n, e, r);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function makeInvokeMethod(e, r, n) {
|
|
156
|
+
var o = h;
|
|
157
|
+
return function (i, a) {
|
|
158
|
+
if (o === f) throw Error("Generator is already running");
|
|
159
|
+
if (o === s) {
|
|
160
|
+
if ("throw" === i) throw a;
|
|
161
|
+
return {
|
|
162
|
+
value: t,
|
|
163
|
+
done: true
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
for (n.method = i, n.arg = a;;) {
|
|
167
|
+
var c = n.delegate;
|
|
168
|
+
if (c) {
|
|
169
|
+
var u = maybeInvokeDelegate(c, n);
|
|
170
|
+
if (u) {
|
|
171
|
+
if (u === y) continue;
|
|
172
|
+
return u;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) {
|
|
176
|
+
if (o === h) throw o = s, n.arg;
|
|
177
|
+
n.dispatchException(n.arg);
|
|
178
|
+
} else "return" === n.method && n.abrupt("return", n.arg);
|
|
179
|
+
o = f;
|
|
180
|
+
var p = tryCatch(e, r, n);
|
|
181
|
+
if ("normal" === p.type) {
|
|
182
|
+
if (o = n.done ? s : l, p.arg === y) continue;
|
|
183
|
+
return {
|
|
184
|
+
value: p.arg,
|
|
185
|
+
done: n.done
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
"throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function maybeInvokeDelegate(e, r) {
|
|
193
|
+
var n = r.method,
|
|
194
|
+
o = e.iterator[n];
|
|
195
|
+
if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y;
|
|
196
|
+
var i = tryCatch(o, e.iterator, r.arg);
|
|
197
|
+
if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y;
|
|
198
|
+
var a = i.arg;
|
|
199
|
+
return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y);
|
|
200
|
+
}
|
|
201
|
+
function pushTryEntry(t) {
|
|
202
|
+
var e = {
|
|
203
|
+
tryLoc: t[0]
|
|
204
|
+
};
|
|
205
|
+
1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e);
|
|
206
|
+
}
|
|
207
|
+
function resetTryEntry(t) {
|
|
208
|
+
var e = t.completion || {};
|
|
209
|
+
e.type = "normal", delete e.arg, t.completion = e;
|
|
210
|
+
}
|
|
211
|
+
function Context(t) {
|
|
212
|
+
this.tryEntries = [{
|
|
213
|
+
tryLoc: "root"
|
|
214
|
+
}], t.forEach(pushTryEntry, this), this.reset(true);
|
|
215
|
+
}
|
|
216
|
+
function values(e) {
|
|
217
|
+
if (e || "" === e) {
|
|
218
|
+
var r = e[a];
|
|
219
|
+
if (r) return r.call(e);
|
|
220
|
+
if ("function" == typeof e.next) return e;
|
|
221
|
+
if (!isNaN(e.length)) {
|
|
222
|
+
var o = -1,
|
|
223
|
+
i = function next() {
|
|
224
|
+
for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = false, next;
|
|
225
|
+
return next.value = t, next.done = true, next;
|
|
226
|
+
};
|
|
227
|
+
return i.next = i;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
throw new TypeError(typeof e + " is not iterable");
|
|
231
|
+
}
|
|
232
|
+
return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", {
|
|
233
|
+
value: GeneratorFunctionPrototype,
|
|
234
|
+
configurable: true
|
|
235
|
+
}), o(GeneratorFunctionPrototype, "constructor", {
|
|
236
|
+
value: GeneratorFunction,
|
|
237
|
+
configurable: true
|
|
238
|
+
}), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) {
|
|
239
|
+
var e = "function" == typeof t && t.constructor;
|
|
240
|
+
return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name));
|
|
241
|
+
}, e.mark = function (t) {
|
|
242
|
+
return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t;
|
|
243
|
+
}, e.awrap = function (t) {
|
|
244
|
+
return {
|
|
245
|
+
__await: t
|
|
246
|
+
};
|
|
247
|
+
}, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () {
|
|
248
|
+
return this;
|
|
249
|
+
}), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) {
|
|
250
|
+
void 0 === i && (i = Promise);
|
|
251
|
+
var a = new AsyncIterator(wrap(t, r, n, o), i);
|
|
252
|
+
return e.isGeneratorFunction(r) ? a : a.next().then(function (t) {
|
|
253
|
+
return t.done ? t.value : a.next();
|
|
254
|
+
});
|
|
255
|
+
}, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () {
|
|
256
|
+
return this;
|
|
257
|
+
}), define(g, "toString", function () {
|
|
258
|
+
return "[object Generator]";
|
|
259
|
+
}), e.keys = function (t) {
|
|
260
|
+
var e = Object(t),
|
|
261
|
+
r = [];
|
|
262
|
+
for (var n in e) r.push(n);
|
|
263
|
+
return r.reverse(), function next() {
|
|
264
|
+
for (; r.length;) {
|
|
265
|
+
var t = r.pop();
|
|
266
|
+
if (t in e) return next.value = t, next.done = false, next;
|
|
267
|
+
}
|
|
268
|
+
return next.done = true, next;
|
|
269
|
+
};
|
|
270
|
+
}, e.values = values, Context.prototype = {
|
|
271
|
+
constructor: Context,
|
|
272
|
+
reset: function (e) {
|
|
273
|
+
if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = false, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t);
|
|
274
|
+
},
|
|
275
|
+
stop: function () {
|
|
276
|
+
this.done = true;
|
|
277
|
+
var t = this.tryEntries[0].completion;
|
|
278
|
+
if ("throw" === t.type) throw t.arg;
|
|
279
|
+
return this.rval;
|
|
280
|
+
},
|
|
281
|
+
dispatchException: function (e) {
|
|
282
|
+
if (this.done) throw e;
|
|
283
|
+
var r = this;
|
|
284
|
+
function handle(n, o) {
|
|
285
|
+
return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o;
|
|
286
|
+
}
|
|
287
|
+
for (var o = this.tryEntries.length - 1; o >= 0; --o) {
|
|
288
|
+
var i = this.tryEntries[o],
|
|
289
|
+
a = i.completion;
|
|
290
|
+
if ("root" === i.tryLoc) return handle("end");
|
|
291
|
+
if (i.tryLoc <= this.prev) {
|
|
292
|
+
var c = n.call(i, "catchLoc"),
|
|
293
|
+
u = n.call(i, "finallyLoc");
|
|
294
|
+
if (c && u) {
|
|
295
|
+
if (this.prev < i.catchLoc) return handle(i.catchLoc, true);
|
|
296
|
+
if (this.prev < i.finallyLoc) return handle(i.finallyLoc);
|
|
297
|
+
} else if (c) {
|
|
298
|
+
if (this.prev < i.catchLoc) return handle(i.catchLoc, true);
|
|
299
|
+
} else {
|
|
300
|
+
if (!u) throw Error("try statement without catch or finally");
|
|
301
|
+
if (this.prev < i.finallyLoc) return handle(i.finallyLoc);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
abrupt: function (t, e) {
|
|
307
|
+
for (var r = this.tryEntries.length - 1; r >= 0; --r) {
|
|
308
|
+
var o = this.tryEntries[r];
|
|
309
|
+
if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) {
|
|
310
|
+
var i = o;
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null);
|
|
315
|
+
var a = i ? i.completion : {};
|
|
316
|
+
return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a);
|
|
317
|
+
},
|
|
318
|
+
complete: function (t, e) {
|
|
319
|
+
if ("throw" === t.type) throw t.arg;
|
|
320
|
+
return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y;
|
|
321
|
+
},
|
|
322
|
+
finish: function (t) {
|
|
323
|
+
for (var e = this.tryEntries.length - 1; e >= 0; --e) {
|
|
324
|
+
var r = this.tryEntries[e];
|
|
325
|
+
if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y;
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
catch: function (t) {
|
|
329
|
+
for (var e = this.tryEntries.length - 1; e >= 0; --e) {
|
|
330
|
+
var r = this.tryEntries[e];
|
|
331
|
+
if (r.tryLoc === t) {
|
|
332
|
+
var n = r.completion;
|
|
333
|
+
if ("throw" === n.type) {
|
|
334
|
+
var o = n.arg;
|
|
335
|
+
resetTryEntry(r);
|
|
336
|
+
}
|
|
337
|
+
return o;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
throw Error("illegal catch attempt");
|
|
341
|
+
},
|
|
342
|
+
delegateYield: function (e, r, n) {
|
|
343
|
+
return this.delegate = {
|
|
344
|
+
iterator: values(e),
|
|
345
|
+
resultName: r,
|
|
346
|
+
nextLoc: n
|
|
347
|
+
}, "next" === this.method && (this.arg = t), y;
|
|
348
|
+
}
|
|
349
|
+
}, e;
|
|
350
|
+
}
|
|
351
|
+
function _toPrimitive(t, r) {
|
|
352
|
+
if ("object" != typeof t || !t) return t;
|
|
353
|
+
var e = t[Symbol.toPrimitive];
|
|
354
|
+
if (void 0 !== e) {
|
|
355
|
+
var i = e.call(t, r);
|
|
356
|
+
if ("object" != typeof i) return i;
|
|
357
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
358
|
+
}
|
|
359
|
+
return ("string" === r ? String : Number)(t);
|
|
360
|
+
}
|
|
361
|
+
function _toPropertyKey(t) {
|
|
362
|
+
var i = _toPrimitive(t, "string");
|
|
363
|
+
return "symbol" == typeof i ? i : i + "";
|
|
364
|
+
}
|
|
365
|
+
function _typeof(o) {
|
|
366
|
+
"@babel/helpers - typeof";
|
|
367
|
+
|
|
368
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
369
|
+
return typeof o;
|
|
370
|
+
} : function (o) {
|
|
371
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
372
|
+
}, _typeof(o);
|
|
373
|
+
}
|
|
374
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
375
|
+
try {
|
|
376
|
+
var info = gen[key](arg);
|
|
377
|
+
var value = info.value;
|
|
378
|
+
} catch (error) {
|
|
379
|
+
reject(error);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (info.done) {
|
|
383
|
+
resolve(value);
|
|
384
|
+
} else {
|
|
385
|
+
Promise.resolve(value).then(_next, _throw);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function _asyncToGenerator(fn) {
|
|
389
|
+
return function () {
|
|
390
|
+
var self = this,
|
|
391
|
+
args = arguments;
|
|
392
|
+
return new Promise(function (resolve, reject) {
|
|
393
|
+
var gen = fn.apply(self, args);
|
|
394
|
+
function _next(value) {
|
|
395
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
396
|
+
}
|
|
397
|
+
function _throw(err) {
|
|
398
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
399
|
+
}
|
|
400
|
+
_next(undefined);
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function _classCallCheck(instance, Constructor) {
|
|
405
|
+
if (!(instance instanceof Constructor)) {
|
|
406
|
+
throw new TypeError("Cannot call a class as a function");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function _defineProperties(target, props) {
|
|
410
|
+
for (var i = 0; i < props.length; i++) {
|
|
411
|
+
var descriptor = props[i];
|
|
412
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
413
|
+
descriptor.configurable = true;
|
|
414
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
415
|
+
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function _createClass(Constructor, protoProps, staticProps) {
|
|
419
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
420
|
+
Object.defineProperty(Constructor, "prototype", {
|
|
421
|
+
writable: false
|
|
422
|
+
});
|
|
423
|
+
return Constructor;
|
|
424
|
+
}
|
|
425
|
+
function _defineProperty(obj, key, value) {
|
|
426
|
+
key = _toPropertyKey(key);
|
|
427
|
+
if (key in obj) {
|
|
428
|
+
Object.defineProperty(obj, key, {
|
|
429
|
+
value: value,
|
|
430
|
+
enumerable: true,
|
|
431
|
+
configurable: true,
|
|
432
|
+
writable: true
|
|
433
|
+
});
|
|
434
|
+
} else {
|
|
435
|
+
obj[key] = value;
|
|
436
|
+
}
|
|
437
|
+
return obj;
|
|
438
|
+
}
|
|
439
|
+
function _inherits(subClass, superClass) {
|
|
440
|
+
if (typeof superClass !== "function" && superClass !== null) {
|
|
441
|
+
throw new TypeError("Super expression must either be null or a function");
|
|
442
|
+
}
|
|
443
|
+
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
444
|
+
constructor: {
|
|
445
|
+
value: subClass,
|
|
446
|
+
writable: true,
|
|
447
|
+
configurable: true
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
Object.defineProperty(subClass, "prototype", {
|
|
451
|
+
writable: false
|
|
452
|
+
});
|
|
453
|
+
if (superClass) _setPrototypeOf(subClass, superClass);
|
|
454
|
+
}
|
|
455
|
+
function _getPrototypeOf(o) {
|
|
456
|
+
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {
|
|
457
|
+
return o.__proto__ || Object.getPrototypeOf(o);
|
|
458
|
+
};
|
|
459
|
+
return _getPrototypeOf(o);
|
|
460
|
+
}
|
|
461
|
+
function _setPrototypeOf(o, p) {
|
|
462
|
+
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
|
|
463
|
+
o.__proto__ = p;
|
|
464
|
+
return o;
|
|
465
|
+
};
|
|
466
|
+
return _setPrototypeOf(o, p);
|
|
467
|
+
}
|
|
468
|
+
function _isNativeFunction(fn) {
|
|
469
|
+
try {
|
|
470
|
+
return Function.toString.call(fn).indexOf("[native code]") !== -1;
|
|
471
|
+
} catch (e) {
|
|
472
|
+
return typeof fn === "function";
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function _wrapNativeSuper(Class) {
|
|
476
|
+
var _cache = typeof Map === "function" ? new Map() : undefined;
|
|
477
|
+
_wrapNativeSuper = function _wrapNativeSuper(Class) {
|
|
478
|
+
if (Class === null || !_isNativeFunction(Class)) return Class;
|
|
479
|
+
if (typeof Class !== "function") {
|
|
480
|
+
throw new TypeError("Super expression must either be null or a function");
|
|
481
|
+
}
|
|
482
|
+
if (typeof _cache !== "undefined") {
|
|
483
|
+
if (_cache.has(Class)) return _cache.get(Class);
|
|
484
|
+
_cache.set(Class, Wrapper);
|
|
485
|
+
}
|
|
486
|
+
function Wrapper() {
|
|
487
|
+
return _construct(Class, arguments, _getPrototypeOf(this).constructor);
|
|
488
|
+
}
|
|
489
|
+
Wrapper.prototype = Object.create(Class.prototype, {
|
|
490
|
+
constructor: {
|
|
491
|
+
value: Wrapper,
|
|
492
|
+
enumerable: false,
|
|
493
|
+
writable: true,
|
|
494
|
+
configurable: true
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
return _setPrototypeOf(Wrapper, Class);
|
|
498
|
+
};
|
|
499
|
+
return _wrapNativeSuper(Class);
|
|
500
|
+
}
|
|
501
|
+
function _assertThisInitialized(self) {
|
|
502
|
+
if (self === void 0) {
|
|
503
|
+
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
504
|
+
}
|
|
505
|
+
return self;
|
|
506
|
+
}
|
|
507
|
+
function _possibleConstructorReturn(self, call) {
|
|
508
|
+
if (call && (typeof call === "object" || typeof call === "function")) {
|
|
509
|
+
return call;
|
|
510
|
+
} else if (call !== void 0) {
|
|
511
|
+
throw new TypeError("Derived constructors may only return object or undefined");
|
|
512
|
+
}
|
|
513
|
+
return _assertThisInitialized(self);
|
|
514
|
+
}
|
|
515
|
+
function _toConsumableArray(arr) {
|
|
516
|
+
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
517
|
+
}
|
|
518
|
+
function _arrayWithoutHoles(arr) {
|
|
519
|
+
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
520
|
+
}
|
|
521
|
+
function _iterableToArray(iter) {
|
|
522
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
523
|
+
}
|
|
524
|
+
function _unsupportedIterableToArray(o, minLen) {
|
|
525
|
+
if (!o) return;
|
|
526
|
+
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
527
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
528
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
529
|
+
if (n === "Map" || n === "Set") return Array.from(o);
|
|
530
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
531
|
+
}
|
|
532
|
+
function _arrayLikeToArray(arr, len) {
|
|
533
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
534
|
+
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
535
|
+
return arr2;
|
|
536
|
+
}
|
|
537
|
+
function _nonIterableSpread() {
|
|
538
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
539
|
+
}
|
|
540
|
+
function _checkPrivateRedeclaration(obj, privateCollection) {
|
|
541
|
+
if (privateCollection.has(obj)) {
|
|
542
|
+
throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) {
|
|
546
|
+
_checkPrivateRedeclaration(obj, privateMap);
|
|
547
|
+
privateMap.set(obj, value);
|
|
548
|
+
}
|
|
549
|
+
function _classPrivateMethodInitSpec(obj, privateSet) {
|
|
550
|
+
_checkPrivateRedeclaration(obj, privateSet);
|
|
551
|
+
privateSet.add(obj);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 15초는 소비자 요구가 아직 없는 임의 기본값입니다 — 티켓 대상 판별의 지연 허용치가 정해지면 조정합니다
|
|
555
|
+
var PING_INTERVAL_MS = 15000;
|
|
556
|
+
/**
|
|
557
|
+
* @description 탭 포트의 장부 — 등록·떠남과 ping 주기를 소유합니다.
|
|
558
|
+
*
|
|
559
|
+
* 무응답 포트를 장부에서 빼지 않습니다: frozen 탭의 메시지는 브라우저가 큐잉했다가
|
|
560
|
+
* 깨어날 때 전부 배달하므로, 계속 보내는 것이 곧 공백 없는 복구입니다 —
|
|
561
|
+
* 빼는 순간부터가 그 탭이 놓치는 구간이 됩니다.
|
|
562
|
+
* close 없이 사라진 탭(크래시·창 닫기)의 포트는 장부에 남지만 무해합니다 — 전송은 조용히
|
|
563
|
+
* 버려지고, 상한은 워커 생존 중 닫힌 탭 수이며, 마지막 클라이언트가 사라지면 브라우저가 워커째 거둡니다.
|
|
564
|
+
*/
|
|
565
|
+
var createPortRegistry = function createPortRegistry() {
|
|
566
|
+
var ports = new Set();
|
|
567
|
+
// 이번 주기 pong을 아직 안 낸 포트 — ping 직후에는 전원이 여기 있으므로 무응답 판정에 쓰면 안 됩니다
|
|
568
|
+
var awaitingPong = new Set();
|
|
569
|
+
// 한 주기를 통째로 놓친 포트 — frozen이거나 죽은 것. pong이 오면 되돌아옵니다
|
|
570
|
+
var silent = new Set();
|
|
571
|
+
var sendPings = function sendPings() {
|
|
572
|
+
awaitingPong.forEach(function (port) {
|
|
573
|
+
return silent.add(port);
|
|
574
|
+
});
|
|
575
|
+
ports.forEach(function (port) {
|
|
576
|
+
return awaitingPong.add(port);
|
|
577
|
+
});
|
|
578
|
+
ports.forEach(function (port) {
|
|
579
|
+
return port.postMessage({
|
|
580
|
+
kind: 'ping'
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
};
|
|
584
|
+
// 해제 수단을 두지 않습니다 — 이 타이머의 수명은 워커 수명이고, 워커는 브라우저가 통째로 거둡니다.
|
|
585
|
+
// 테스트의 가짜 타이머는 vitest.setup의 afterEach(useRealTimers)가 시계째 버려 교차 오염이 없습니다
|
|
586
|
+
setInterval(sendPings, PING_INTERVAL_MS);
|
|
587
|
+
return {
|
|
588
|
+
add: function add(port) {
|
|
589
|
+
ports.add(port);
|
|
590
|
+
},
|
|
591
|
+
/** @description 떠난 포트의 늦은 메시지를 거르는 유일한 기준 — frozen 탭은 장부에 남아 있습니다 */
|
|
592
|
+
has: function has(port) {
|
|
593
|
+
return ports.has(port);
|
|
594
|
+
},
|
|
595
|
+
/**
|
|
596
|
+
* @description 탭이 스스로 떠났습니다 — 장부에서 빼고 포트를 닫습니다.
|
|
597
|
+
* 마지막이었는지 함께 알려 호출부가 세션을 접을지 정하게 합니다.
|
|
598
|
+
*/
|
|
599
|
+
close: function close(port) {
|
|
600
|
+
ports["delete"](port);
|
|
601
|
+
awaitingPong["delete"](port);
|
|
602
|
+
silent["delete"](port);
|
|
603
|
+
port.close();
|
|
604
|
+
return {
|
|
605
|
+
wasLast: ports.size === 0
|
|
606
|
+
};
|
|
607
|
+
},
|
|
608
|
+
markAlive: function markAlive(port) {
|
|
609
|
+
awaitingPong["delete"](port);
|
|
610
|
+
silent["delete"](port);
|
|
611
|
+
},
|
|
612
|
+
size: function size() {
|
|
613
|
+
return ports.size;
|
|
614
|
+
},
|
|
615
|
+
forEach: function forEach(visit) {
|
|
616
|
+
return ports.forEach(visit);
|
|
617
|
+
},
|
|
618
|
+
/**
|
|
619
|
+
* @description 티켓을 물어볼 탭 하나 — EventSource가 하나라 티켓도 한 장이면 됩니다.
|
|
620
|
+
* 전원에게 뿌리면 탭 수만큼 POST가 나가고, 그 증폭이 429(fatal)를 자초합니다.
|
|
621
|
+
* 이번 주기에 답한 탭을 먼저 고르고, 없으면 아직 판정 전인 탭에게 묻습니다.
|
|
622
|
+
* 전원이 확정 침묵이면 undefined: 답이 올 수 없는 요청을 큐에 쌓으면
|
|
623
|
+
* 깨어난 탭이 그 수만큼 한꺼번에 POST를 쏘게 됩니다.
|
|
624
|
+
*/
|
|
625
|
+
livelyPort: function livelyPort() {
|
|
626
|
+
var _candidates$find;
|
|
627
|
+
var candidates = _toConsumableArray(ports);
|
|
628
|
+
return (_candidates$find = candidates.find(function (port) {
|
|
629
|
+
return !awaitingPong.has(port);
|
|
630
|
+
})) !== null && _candidates$find !== void 0 ? _candidates$find : candidates.find(function (port) {
|
|
631
|
+
return !silent.has(port);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
/** @description 판정을 메시지 정의 옆에 둡니다 — 필드가 바뀌면 여기도 같이 눈에 띄도록 */
|
|
638
|
+
var isTicketResponse = function isTicketResponse(message) {
|
|
639
|
+
if (message.kind !== 'ticket-response') {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
var ok = message.ok,
|
|
643
|
+
ticket = message.ticket,
|
|
644
|
+
error = message.error;
|
|
645
|
+
if (ok === true) {
|
|
646
|
+
return typeof ticket === 'string';
|
|
647
|
+
}
|
|
648
|
+
if (ok !== false || _typeof(error) !== 'object' || error === null || Array.isArray(error)) {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
// status는 fatal 판정에 쓰이므로 숫자가 아니면 재시도 대상으로 흘려보내지 않습니다
|
|
652
|
+
var status = error.status,
|
|
653
|
+
cause = error.cause;
|
|
654
|
+
if (status !== undefined && typeof status !== 'number') {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
// 미지 cause 문자열은 버리지 않고 통과시킵니다 — 코어가 모르는 값은 status 규칙으로 처리하므로 배포 skew에 안전합니다
|
|
658
|
+
return cause === undefined || typeof cause === 'string';
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
var TICKET_TIMEOUT_MS = 10000;
|
|
662
|
+
var TICKET_NETWORK_FAILURE = Object.freeze({
|
|
663
|
+
ok: false,
|
|
664
|
+
error: Object.freeze({})
|
|
665
|
+
});
|
|
666
|
+
var TICKET_AUTH_CALLBACK_FAILURE = Object.freeze({
|
|
667
|
+
ok: false,
|
|
668
|
+
error: Object.freeze({
|
|
669
|
+
cause: 'auth_callback'
|
|
670
|
+
})
|
|
671
|
+
});
|
|
672
|
+
var AuthTokenError = /*#__PURE__*/function (_Error) {
|
|
673
|
+
function AuthTokenError(message, options) {
|
|
674
|
+
var _this;
|
|
675
|
+
_classCallCheck(this, AuthTokenError);
|
|
676
|
+
_this = _callSuper(this, AuthTokenError, [message, options]);
|
|
677
|
+
_this.name = 'AuthTokenError';
|
|
678
|
+
return _this;
|
|
679
|
+
}
|
|
680
|
+
_inherits(AuthTokenError, _Error);
|
|
681
|
+
return _createClass(AuthTokenError);
|
|
682
|
+
}(/*#__PURE__*/_wrapNativeSuper(Error));
|
|
683
|
+
var resolveAuthToken = /*#__PURE__*/function () {
|
|
684
|
+
var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(getAuthToken) {
|
|
685
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
686
|
+
while (1) switch (_context.prev = _context.next) {
|
|
687
|
+
case 0:
|
|
688
|
+
_context.prev = 0;
|
|
689
|
+
_context.next = 3;
|
|
690
|
+
return getAuthToken();
|
|
691
|
+
case 3:
|
|
692
|
+
return _context.abrupt("return", _context.sent);
|
|
693
|
+
case 6:
|
|
694
|
+
_context.prev = 6;
|
|
695
|
+
_context.t0 = _context["catch"](0);
|
|
696
|
+
throw new AuthTokenError('getAuthToken threw', {
|
|
697
|
+
cause: _context.t0
|
|
698
|
+
});
|
|
699
|
+
case 9:
|
|
700
|
+
case "end":
|
|
701
|
+
return _context.stop();
|
|
702
|
+
}
|
|
703
|
+
}, _callee, null, [[0, 6]]);
|
|
704
|
+
}));
|
|
705
|
+
return function resolveAuthToken(_x) {
|
|
706
|
+
return _ref.apply(this, arguments);
|
|
707
|
+
};
|
|
708
|
+
}();
|
|
709
|
+
var authHeaders = function authHeaders(token) {
|
|
710
|
+
return token === null ? undefined : {
|
|
711
|
+
Authorization: "Bearer ".concat(token)
|
|
712
|
+
};
|
|
713
|
+
};
|
|
714
|
+
var hasTicket = function hasTicket(body) {
|
|
715
|
+
if (_typeof(body) !== 'object' || body === null) {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
var data = body.data;
|
|
719
|
+
return _typeof(data) === 'object' && data !== null && typeof data.ticket === 'string';
|
|
720
|
+
};
|
|
721
|
+
/**
|
|
722
|
+
* @description 본문을 계약({ data: { ticket: string } } — 사내 기본 응답 스키마)으로 좁혀 값으로 돌려줍니다 —
|
|
723
|
+
* 파싱 실패(게이트웨이·프록시가 200으로 주는 HTML 등)와 형태 위반(ticket 부재)을 가르지 않습니다:
|
|
724
|
+
* 어느 쪽이든 서버가 응답한 본문 계약 위반이고, 경로 생존 증거라 status를 실어 네트워크성 연속 판정을 끊습니다.
|
|
725
|
+
* 이 분기는 stalled·fatal 어느 쪽에도 닿지 않는 유일한 침묵 실패라 warn을 남깁니다 — 서버 로그에도 흔적이
|
|
726
|
+
* 없는 실패(서버 입장에선 정상 200)여서 이 로그가 유일한 단서입니다.
|
|
727
|
+
*/
|
|
728
|
+
var parseTicketBody = /*#__PURE__*/function () {
|
|
729
|
+
var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(response, logger) {
|
|
730
|
+
var body;
|
|
731
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
732
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
733
|
+
case 0:
|
|
734
|
+
_context2.next = 2;
|
|
735
|
+
return response.json()["catch"](function () {
|
|
736
|
+
return null;
|
|
737
|
+
});
|
|
738
|
+
case 2:
|
|
739
|
+
body = _context2.sent;
|
|
740
|
+
if (hasTicket(body)) {
|
|
741
|
+
_context2.next = 6;
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
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"));
|
|
745
|
+
return _context2.abrupt("return", {
|
|
746
|
+
ok: false,
|
|
747
|
+
error: {
|
|
748
|
+
status: response.status
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
case 6:
|
|
752
|
+
return _context2.abrupt("return", {
|
|
753
|
+
ok: true,
|
|
754
|
+
ticket: body.data.ticket
|
|
755
|
+
});
|
|
756
|
+
case 7:
|
|
757
|
+
case "end":
|
|
758
|
+
return _context2.stop();
|
|
759
|
+
}
|
|
760
|
+
}, _callee2);
|
|
761
|
+
}));
|
|
762
|
+
return function parseTicketBody(_x2, _x3) {
|
|
763
|
+
return _ref2.apply(this, arguments);
|
|
764
|
+
};
|
|
765
|
+
}();
|
|
766
|
+
/**
|
|
767
|
+
* @description 연결 티켓을 발급합니다 — TTL 15s이므로 발급 즉시 연결해야 합니다
|
|
768
|
+
*/
|
|
769
|
+
var issueTicket = /*#__PURE__*/function () {
|
|
770
|
+
var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3(options) {
|
|
771
|
+
var _options$fetch;
|
|
772
|
+
var fetchImpl, token, response, _options$logger;
|
|
773
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
774
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
775
|
+
case 0:
|
|
776
|
+
fetchImpl = (_options$fetch = options.fetch) !== null && _options$fetch !== void 0 ? _options$fetch : globalThis.fetch;
|
|
777
|
+
_context3.prev = 1;
|
|
778
|
+
_context3.next = 4;
|
|
779
|
+
return resolveAuthToken(options.getAuthToken);
|
|
780
|
+
case 4:
|
|
781
|
+
token = _context3.sent;
|
|
782
|
+
_context3.next = 7;
|
|
783
|
+
return fetchImpl("".concat(options.baseUrl, "/v1/events/ticket"), {
|
|
784
|
+
method: 'POST',
|
|
785
|
+
headers: authHeaders(token)
|
|
786
|
+
});
|
|
787
|
+
case 7:
|
|
788
|
+
response = _context3.sent;
|
|
789
|
+
if (response.ok) {
|
|
790
|
+
_context3.next = 10;
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
return _context3.abrupt("return", {
|
|
794
|
+
ok: false,
|
|
795
|
+
error: {
|
|
796
|
+
status: response.status
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
case 10:
|
|
800
|
+
_context3.next = 12;
|
|
801
|
+
return parseTicketBody(response, options.logger);
|
|
802
|
+
case 12:
|
|
803
|
+
return _context3.abrupt("return", _context3.sent);
|
|
804
|
+
case 15:
|
|
805
|
+
_context3.prev = 15;
|
|
806
|
+
_context3.t0 = _context3["catch"](1);
|
|
807
|
+
if (!(_context3.t0 instanceof AuthTokenError)) {
|
|
808
|
+
_context3.next = 20;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
// 정지 결정은 SseClient 몫이지만, 로그는 원인 예외가 있는 여기서 한 번만 — 워커 경로에선 탭 스코프라 유일한 기록입니다
|
|
812
|
+
(_options$logger = options.logger) === null || _options$logger === void 0 || _options$logger.error('sse: getAuthToken이 예외를 던져 멈춥니다 — 콜백은 실패 시 null을 반환해야 합니다', _context3.t0.cause);
|
|
813
|
+
return _context3.abrupt("return", TICKET_AUTH_CALLBACK_FAILURE);
|
|
814
|
+
case 20:
|
|
815
|
+
return _context3.abrupt("return", TICKET_NETWORK_FAILURE);
|
|
816
|
+
case 21:
|
|
817
|
+
case "end":
|
|
818
|
+
return _context3.stop();
|
|
819
|
+
}
|
|
820
|
+
}, _callee3, null, [[1, 15]]);
|
|
821
|
+
}));
|
|
822
|
+
return function issueTicket(_x4) {
|
|
823
|
+
return _ref3.apply(this, arguments);
|
|
824
|
+
};
|
|
825
|
+
}();
|
|
826
|
+
/**
|
|
827
|
+
* @description 티켓 발급에 시간 제한을 겁니다 — 타임아웃 이후 도착한 응답은 race가 이미 완료돼 무시됩니다
|
|
828
|
+
*/
|
|
829
|
+
var issueTicketWithTimeout = /*#__PURE__*/function () {
|
|
830
|
+
var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4(options) {
|
|
831
|
+
var ticketTimer, ticketTimeout, result;
|
|
832
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
833
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
834
|
+
case 0:
|
|
835
|
+
ticketTimeout = new Promise(function (resolve) {
|
|
836
|
+
ticketTimer = setTimeout(function () {
|
|
837
|
+
return resolve(TICKET_NETWORK_FAILURE);
|
|
838
|
+
}, TICKET_TIMEOUT_MS);
|
|
839
|
+
});
|
|
840
|
+
_context4.next = 3;
|
|
841
|
+
return Promise.race([issueTicket(options), ticketTimeout]);
|
|
842
|
+
case 3:
|
|
843
|
+
result = _context4.sent;
|
|
844
|
+
clearTimeout(ticketTimer);
|
|
845
|
+
return _context4.abrupt("return", result);
|
|
846
|
+
case 6:
|
|
847
|
+
case "end":
|
|
848
|
+
return _context4.stop();
|
|
849
|
+
}
|
|
850
|
+
}, _callee4);
|
|
851
|
+
}));
|
|
852
|
+
return function issueTicketWithTimeout(_x5) {
|
|
853
|
+
return _ref4.apply(this, arguments);
|
|
854
|
+
};
|
|
855
|
+
}();
|
|
856
|
+
|
|
857
|
+
// 탭↔워커 postMessage 왕복 여유분 — 실측 아닌 임의값입니다
|
|
858
|
+
var PORT_ROUNDTRIP_SLACK_MS = 2000;
|
|
859
|
+
// 탭의 발급 제한보다 늦게 끝나야 합니다 — 탭이 스스로 실패를 알려줄 기회를 먼저 줍니다
|
|
860
|
+
var TICKET_RESPONSE_TIMEOUT_MS = TICKET_TIMEOUT_MS + PORT_ROUNDTRIP_SLACK_MS;
|
|
861
|
+
/**
|
|
862
|
+
* @description 티켓 발급을 탭에 위임합니다 — 토큰은 탭에 남고 워커는 발급 결과만 받습니다.
|
|
863
|
+
* 워커가 코어 연결 루프를 그대로 쓸 수 있도록, 요청·응답 왕복을 Promise 하나로 바꿉니다.
|
|
864
|
+
*/
|
|
865
|
+
var createTicketDelegate = function createTicketDelegate(requestFromTab) {
|
|
866
|
+
// 왕복은 한 번에 하나 — 겹친 요청은 탭에 다시 묻지 않고 이 결과를 함께 받습니다
|
|
867
|
+
var pending = null;
|
|
868
|
+
var finish = function finish(result) {
|
|
869
|
+
if (pending === null) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
var _pending = pending,
|
|
873
|
+
resolvers = _pending.resolvers,
|
|
874
|
+
timer = _pending.timer;
|
|
875
|
+
pending = null;
|
|
876
|
+
clearTimeout(timer);
|
|
877
|
+
resolvers.forEach(function (resolve) {
|
|
878
|
+
return resolve(result);
|
|
879
|
+
});
|
|
880
|
+
};
|
|
881
|
+
var startNewRequest = function startNewRequest(resolve) {
|
|
882
|
+
pending = {
|
|
883
|
+
resolvers: [resolve],
|
|
884
|
+
timer: setTimeout(function () {
|
|
885
|
+
return finish(TICKET_NETWORK_FAILURE);
|
|
886
|
+
}, TICKET_RESPONSE_TIMEOUT_MS)
|
|
887
|
+
};
|
|
888
|
+
try {
|
|
889
|
+
requestFromTab();
|
|
890
|
+
} catch (_unused) {
|
|
891
|
+
finish(TICKET_NETWORK_FAILURE);
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
var joinInFlight = function joinInFlight(resolve) {
|
|
895
|
+
var _pending2;
|
|
896
|
+
(_pending2 = pending) === null || _pending2 === void 0 || _pending2.resolvers.push(resolve);
|
|
897
|
+
};
|
|
898
|
+
return {
|
|
899
|
+
request: function request() {
|
|
900
|
+
return new Promise(function (resolve) {
|
|
901
|
+
if (pending === null) {
|
|
902
|
+
startNewRequest(resolve);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
joinInFlight(resolve);
|
|
906
|
+
});
|
|
907
|
+
},
|
|
908
|
+
/** @description 다음 연결이 이 왕복에 합류해 응답 없이 기다리지 않게 끊습니다 */
|
|
909
|
+
cancel: function cancel() {
|
|
910
|
+
return finish(TICKET_NETWORK_FAILURE);
|
|
911
|
+
},
|
|
912
|
+
/** @description 시간 제한 뒤 도착한 응답은 pending이 비어 있어 무시됩니다 */
|
|
913
|
+
settle: function settle(message) {
|
|
914
|
+
var result = message.ok ? {
|
|
915
|
+
ok: true,
|
|
916
|
+
ticket: message.ticket
|
|
917
|
+
} : {
|
|
918
|
+
ok: false,
|
|
919
|
+
error: message.error
|
|
920
|
+
};
|
|
921
|
+
finish(result);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
// 서버가 /v1/events 스트림으로 보내는 연결 수명주기 이벤트 — 도메인 이벤트와 달리 SDK가 소비합니다
|
|
927
|
+
var SERVER_CONNECTION_EVENTS = {
|
|
928
|
+
hello: 'hello',
|
|
929
|
+
goaway: 'goaway'
|
|
930
|
+
};
|
|
931
|
+
/**
|
|
932
|
+
* @description SDK가 스스로 발화하는 이벤트 — 재수립(resync)·멈춘 사유(fatal)·미연결 지속(stalled)을 SDK가 알아채 알립니다
|
|
933
|
+
*/
|
|
934
|
+
var SDK_EVENTS = {
|
|
935
|
+
resync: 'resync',
|
|
936
|
+
fatal: 'fatal',
|
|
937
|
+
stalled: 'stalled'
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* @description 소비처로 배달하는 이름들 — 출처(서버·SDK)와 무관하게 "소비처가 보는가"만 봅니다.
|
|
941
|
+
* 워커는 구독 장부와 별개로 이 목록을 중계해야 합니다: 장부만 보고 붙이면 소비처에 영영 도달하지 않습니다.
|
|
942
|
+
* SDK 이벤트는 배달이 곧 존재 이유라 전부 파생합니다 — 나열하면 이름을 늘릴 때 빠뜨릴 자리가 생깁니다.
|
|
943
|
+
* 서버 연결 이벤트는 SDK가 소비하는 게 기본이라 배달할 것만 골라 넣습니다:
|
|
944
|
+
* goaway는 SDK가 action으로 재연결을 판단하고, 소비처는 reason으로 안내를 정합니다.
|
|
945
|
+
*/
|
|
946
|
+
var FORWARDED_EVENTS = [].concat(_toConsumableArray(Object.values(SDK_EVENTS)), [SERVER_CONNECTION_EVENTS.goaway]);
|
|
947
|
+
// 멤버십 검사는 Set 파생으로 — Object.hasOwn(Safari 15.4+)이 배포 하한을 만들지 않게 합니다
|
|
948
|
+
var SERVER_CONNECTION_EVENT_NAMES = new Set(Object.values(SERVER_CONNECTION_EVENTS));
|
|
949
|
+
var FORWARDED_EVENT_NAMES = new Set(FORWARDED_EVENTS);
|
|
950
|
+
var isServerConnectionEvent = function isServerConnectionEvent(type) {
|
|
951
|
+
return SERVER_CONNECTION_EVENT_NAMES.has(type);
|
|
952
|
+
};
|
|
953
|
+
var isForwardedEvent = function isForwardedEvent(type) {
|
|
954
|
+
return FORWARDED_EVENT_NAMES.has(type);
|
|
955
|
+
};
|
|
956
|
+
/**
|
|
957
|
+
* @description 서버 스트림에 구독을 걸 이름인지 판정합니다 — 연결 이벤트와 중계 목록은 SDK가 소유합니다.
|
|
958
|
+
* 목록에서 판정을 파생해야 이름을 추가할 때 구독 제외가 따라옵니다: 빠지면 워커가 같은 이벤트를 두 번 배달합니다.
|
|
959
|
+
*/
|
|
960
|
+
var isSubscribableEvent = function isSubscribableEvent(type) {
|
|
961
|
+
return !isServerConnectionEvent(type) && !isForwardedEvent(type);
|
|
962
|
+
};
|
|
963
|
+
/**
|
|
964
|
+
* @description 구독해도 배달되지 않는 이름인지 판정합니다 — 조용한 무배달 구독을 경고로 알리는 데 씁니다.
|
|
965
|
+
* 중계 목록에 있으면 SDK가 소비하더라도 소비처에 배달되므로 경고 대상이 아닙니다.
|
|
966
|
+
*/
|
|
967
|
+
var isUndeliverableEvent = function isUndeliverableEvent(type) {
|
|
968
|
+
return isServerConnectionEvent(type) && !isForwardedEvent(type);
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
var DEFAULT_BACKOFF = {
|
|
972
|
+
initialDelayMs: 1000,
|
|
973
|
+
maxDelayMs: 30000,
|
|
974
|
+
multiplier: 2
|
|
975
|
+
};
|
|
976
|
+
/** @description 지수 백오프 + full jitter — 동시 재연결 폭주(thundering herd)를 분산시킵니다 */
|
|
977
|
+
var createBackoff = function createBackoff(options) {
|
|
978
|
+
var _options$initialDelay, _options$maxDelayMs, _options$multiplier;
|
|
979
|
+
var random = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Math.random;
|
|
980
|
+
var initialDelayMs = (_options$initialDelay = options === null || options === void 0 ? void 0 : options.initialDelayMs) !== null && _options$initialDelay !== void 0 ? _options$initialDelay : DEFAULT_BACKOFF.initialDelayMs;
|
|
981
|
+
var maxDelayMs = (_options$maxDelayMs = options === null || options === void 0 ? void 0 : options.maxDelayMs) !== null && _options$maxDelayMs !== void 0 ? _options$maxDelayMs : DEFAULT_BACKOFF.maxDelayMs;
|
|
982
|
+
var multiplier = (_options$multiplier = options === null || options === void 0 ? void 0 : options.multiplier) !== null && _options$multiplier !== void 0 ? _options$multiplier : DEFAULT_BACKOFF.multiplier;
|
|
983
|
+
var attempt = 0;
|
|
984
|
+
return {
|
|
985
|
+
nextDelayMs: function nextDelayMs() {
|
|
986
|
+
var capped = Math.min(maxDelayMs, initialDelayMs * Math.pow(multiplier, attempt));
|
|
987
|
+
attempt += 1;
|
|
988
|
+
return random() * capped;
|
|
989
|
+
},
|
|
990
|
+
reset: function reset() {
|
|
991
|
+
attempt = 0;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* @description SseClient·WorkerSseClient가 공유하는 구독·발행 코어.
|
|
998
|
+
* 리스너 예외를 격리합니다 — 하나가 던져도 나머지 리스너와 호출자의 정리 로직이 중단되지 않게.
|
|
999
|
+
*/
|
|
1000
|
+
var createEmitter = function createEmitter(logger) {
|
|
1001
|
+
var handlers = new Map();
|
|
1002
|
+
var statusListeners = new Set();
|
|
1003
|
+
var safeCall = function safeCall(listener, value) {
|
|
1004
|
+
try {
|
|
1005
|
+
listener(value);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
logger === null || logger === void 0 || logger.warn('sse: 리스너 예외를 격리했습니다', error);
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
return {
|
|
1011
|
+
on: function on(type, handler) {
|
|
1012
|
+
var _handlers$get;
|
|
1013
|
+
var set = (_handlers$get = handlers.get(type)) !== null && _handlers$get !== void 0 ? _handlers$get : new Set();
|
|
1014
|
+
set.add(handler);
|
|
1015
|
+
handlers.set(type, set);
|
|
1016
|
+
return function () {
|
|
1017
|
+
set["delete"](handler);
|
|
1018
|
+
};
|
|
1019
|
+
},
|
|
1020
|
+
onStatus: function onStatus(listener) {
|
|
1021
|
+
statusListeners.add(listener);
|
|
1022
|
+
return function () {
|
|
1023
|
+
statusListeners["delete"](listener);
|
|
1024
|
+
};
|
|
1025
|
+
},
|
|
1026
|
+
emit: function emit(type, payload) {
|
|
1027
|
+
var _handlers$get2;
|
|
1028
|
+
(_handlers$get2 = handlers.get(type)) === null || _handlers$get2 === void 0 || _handlers$get2.forEach(function (handler) {
|
|
1029
|
+
return safeCall(handler, payload);
|
|
1030
|
+
});
|
|
1031
|
+
},
|
|
1032
|
+
emitStatus: function emitStatus(status) {
|
|
1033
|
+
statusListeners.forEach(function (listener) {
|
|
1034
|
+
return safeCall(listener, status);
|
|
1035
|
+
});
|
|
1036
|
+
},
|
|
1037
|
+
handlerTypes: function handlerTypes() {
|
|
1038
|
+
return _toConsumableArray(handlers.keys());
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* @description 두 구현이 공유하는 구독 표면 — 구독 장부(emitter)를 소유하고 on·onStatus를 확정합니다.
|
|
1045
|
+
* 연결을 어떻게 다루는지는 모릅니다: 구현마다 다른 것만 추상 멤버로 남깁니다.
|
|
1046
|
+
* 같아야 할 것을 한 벌로 두기 위한 클래스입니다 — 두 구현이 각자 배선하다 갈린 이력이 있습니다.
|
|
1047
|
+
*/
|
|
1048
|
+
var BaseSseClient = /*#__PURE__*/function () {
|
|
1049
|
+
function BaseSseClient(logger) {
|
|
1050
|
+
_classCallCheck(this, BaseSseClient);
|
|
1051
|
+
_defineProperty(this, "logger", void 0);
|
|
1052
|
+
_defineProperty(this, "emitter", void 0);
|
|
1053
|
+
this.logger = logger;
|
|
1054
|
+
this.emitter = createEmitter(logger);
|
|
1055
|
+
}
|
|
1056
|
+
return _createClass(BaseSseClient, [{
|
|
1057
|
+
key: "on",
|
|
1058
|
+
value: function on(type, handler) {
|
|
1059
|
+
if (isUndeliverableEvent(type)) {
|
|
1060
|
+
var _this$logger;
|
|
1061
|
+
(_this$logger = this.logger) === null || _this$logger === void 0 || _this$logger.warn("sse: '".concat(type, "'\uB294 SDK\uAC00 \uC18C\uBE44\uD558\uB294 \uC608\uC57D \uC774\uBCA4\uD2B8\uB77C \uAD6C\uB3C5\uC774 \uBC30\uB2EC\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
|
|
1062
|
+
}
|
|
1063
|
+
this.registerSubscription(type);
|
|
1064
|
+
return this.emitter.on(type, handler);
|
|
1065
|
+
}
|
|
1066
|
+
}, {
|
|
1067
|
+
key: "onStatus",
|
|
1068
|
+
value: function onStatus(listener) {
|
|
1069
|
+
return this.emitter.onStatus(listener);
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* @description 탭이 돌아올 때 connect()를 다시 부릅니다 — 전원이 얼어붙어 접힌 연결(휴면)은
|
|
1073
|
+
* 깨어난 탭의 이 호출만이 다시 세울 수 있습니다. 연결이 살아 있으면 아무 일도 없습니다.
|
|
1074
|
+
* bfcache 복귀는 visibilitychange가 아니라 pageshow로만 알 수 있어 둘 다 봅니다.
|
|
1075
|
+
* SSR에는 붙일 곳이 없어 아무 일도 하지 않습니다.
|
|
1076
|
+
*
|
|
1077
|
+
* @returns 리스너를 떼는 함수 — 클라이언트를 버릴 때 함께 부르세요
|
|
1078
|
+
*/
|
|
1079
|
+
}, {
|
|
1080
|
+
key: "attachAutoResume",
|
|
1081
|
+
value: function attachAutoResume() {
|
|
1082
|
+
var _this = this;
|
|
1083
|
+
if (typeof document === 'undefined' || typeof window === 'undefined') {
|
|
1084
|
+
return function () {};
|
|
1085
|
+
}
|
|
1086
|
+
// 되살릴 대상은 idle입니다 — 전원 침묵으로 접힌 휴면이 이 상태입니다(workerConnection.close).
|
|
1087
|
+
// closed(소비처 disconnect·서버 goaway close)는 끊은 의도를 되살리지 않고,
|
|
1088
|
+
// fatal의 회복 계약은 disconnect 후 connect라 자동 재개가 대신할 수 없습니다
|
|
1089
|
+
var notifyAlive = function notifyAlive() {
|
|
1090
|
+
if (_this.status === 'closed' || _this.status === 'fatal') {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
_this.connect();
|
|
1094
|
+
};
|
|
1095
|
+
var onVisible = function onVisible() {
|
|
1096
|
+
if (document.visibilityState === 'visible') {
|
|
1097
|
+
notifyAlive();
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
// persisted = bfcache 복귀, 직결 경로용 — Chrome은 SharedWorker 페이지를 bfcache에 넣지 않고,
|
|
1101
|
+
// 넣는 브라우저라면 포트가 이미 죽어 이 connect가 닿지 않습니다(알려진 한계)
|
|
1102
|
+
var onPageShow = function onPageShow(event) {
|
|
1103
|
+
if (event.persisted) {
|
|
1104
|
+
notifyAlive();
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
1108
|
+
window.addEventListener('pageshow', onPageShow);
|
|
1109
|
+
return function () {
|
|
1110
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
1111
|
+
window.removeEventListener('pageshow', onPageShow);
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
}]);
|
|
1115
|
+
}();
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* @description 재시도 대기 Promise — 타이머 만료·abort 중 먼저 발생하는 쪽에서 resolve됩니다.
|
|
1119
|
+
* 어느 경로로 풀리든 타이머와 abort 리스너를 함께 정리합니다.
|
|
1120
|
+
*/
|
|
1121
|
+
var waitForRetry = function waitForRetry(delayMs, signal) {
|
|
1122
|
+
return new Promise(function (resolve) {
|
|
1123
|
+
var finish = function finish() {
|
|
1124
|
+
clearTimeout(timer);
|
|
1125
|
+
signal.removeEventListener('abort', finish);
|
|
1126
|
+
resolve();
|
|
1127
|
+
};
|
|
1128
|
+
var timer = setTimeout(finish, delayMs);
|
|
1129
|
+
signal.addEventListener('abort', finish);
|
|
1130
|
+
});
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* @description 상태 전이 규칙을 명시합니다
|
|
1135
|
+
*/
|
|
1136
|
+
var TRANSITIONS = {
|
|
1137
|
+
idle: {
|
|
1138
|
+
connect: 'connecting',
|
|
1139
|
+
disconnect: 'closed'
|
|
1140
|
+
},
|
|
1141
|
+
connecting: {
|
|
1142
|
+
hello: 'open',
|
|
1143
|
+
retryableFailure: 'reconnecting',
|
|
1144
|
+
fatalFailure: 'fatal',
|
|
1145
|
+
disconnect: 'closed'
|
|
1146
|
+
},
|
|
1147
|
+
open: {
|
|
1148
|
+
retryableFailure: 'reconnecting',
|
|
1149
|
+
fatalFailure: 'fatal',
|
|
1150
|
+
disconnect: 'closed'
|
|
1151
|
+
},
|
|
1152
|
+
reconnecting: {
|
|
1153
|
+
readyToRetry: 'connecting',
|
|
1154
|
+
disconnect: 'closed'
|
|
1155
|
+
},
|
|
1156
|
+
// closed 후 재연결을 열어둔 이유: React StrictMode 재마운트가 같은 인스턴스로 connect→disconnect→connect를 수행합니다
|
|
1157
|
+
closed: {
|
|
1158
|
+
connect: 'connecting'
|
|
1159
|
+
},
|
|
1160
|
+
fatal: {
|
|
1161
|
+
disconnect: 'closed'
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
/** @description 연결 상태머신 — 표에 선언된 전이만 수행하고, 전이가 일어날 때 onChange를 호출합니다 */
|
|
1165
|
+
var createStatusMachine = function createStatusMachine(onChange) {
|
|
1166
|
+
var status = 'idle';
|
|
1167
|
+
return {
|
|
1168
|
+
get status() {
|
|
1169
|
+
return status;
|
|
1170
|
+
},
|
|
1171
|
+
transition: function transition(event) {
|
|
1172
|
+
var transitions = TRANSITIONS[status];
|
|
1173
|
+
var next = transitions[event];
|
|
1174
|
+
if (next === undefined) {
|
|
1175
|
+
return false;
|
|
1176
|
+
}
|
|
1177
|
+
status = next;
|
|
1178
|
+
onChange(next);
|
|
1179
|
+
return true;
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
// 상태 이름의 단일 원천 — 타입과 런타임 멤버십 검사가 같은 목록을 봅니다
|
|
1185
|
+
// goaway 값의 단일 원천 — 타입과 런타임 검사가 같은 목록을 봅니다
|
|
1186
|
+
var GOAWAY_REASONS = ['deploy', 'user_logout', 'maintenance', 'manual'];
|
|
1187
|
+
var GOAWAY_ACTIONS = ['retry', 'close'];
|
|
1188
|
+
/**
|
|
1189
|
+
* @description 포트·스트림 너머의 값을 좁힙니다 — 미지 값은 없는 것으로 봅니다
|
|
1190
|
+
*/
|
|
1191
|
+
var isGoawayReason = function isGoawayReason(value) {
|
|
1192
|
+
return GOAWAY_REASONS.includes(value);
|
|
1193
|
+
};
|
|
1194
|
+
var isGoawayAction = function isGoawayAction(value) {
|
|
1195
|
+
return GOAWAY_ACTIONS.includes(value);
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
var parsePayload = function parsePayload(event) {
|
|
1199
|
+
if (typeof event.data !== 'string' || event.data === '') {
|
|
1200
|
+
return undefined;
|
|
1201
|
+
}
|
|
1202
|
+
try {
|
|
1203
|
+
return JSON.parse(event.data);
|
|
1204
|
+
} catch (_unused) {
|
|
1205
|
+
return event.data;
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
// 구버전 서버·잘린 프레임에서는 필드가 없거나 미지 값이 옵니다 — 아는 값만 남기고 나머지는 버립니다
|
|
1209
|
+
var toGoawayPayload = function toGoawayPayload(parsed) {
|
|
1210
|
+
if (_typeof(parsed) !== 'object' || parsed === null) {
|
|
1211
|
+
return {};
|
|
1212
|
+
}
|
|
1213
|
+
var reason = parsed.reason,
|
|
1214
|
+
action = parsed.action;
|
|
1215
|
+
return _objectSpread2(_objectSpread2({}, isGoawayReason(reason) && {
|
|
1216
|
+
reason: reason
|
|
1217
|
+
}), isGoawayAction(action) && {
|
|
1218
|
+
action: action
|
|
1219
|
+
});
|
|
1220
|
+
};
|
|
1221
|
+
/**
|
|
1222
|
+
* @description 도메인 이벤트 리스너를 소스에 1회 부착합니다 — SDK 예약 이벤트는 소비처로 흘리지 않습니다
|
|
1223
|
+
*/
|
|
1224
|
+
var attachDomainListener = function attachDomainListener(source, attachedEvents, type, deliver) {
|
|
1225
|
+
if (attachedEvents.has(type)) {
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
if (!isSubscribableEvent(type)) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
attachedEvents.add(type);
|
|
1232
|
+
source.addEventListener(type, function (event) {
|
|
1233
|
+
deliver(type, parsePayload(event));
|
|
1234
|
+
});
|
|
1235
|
+
};
|
|
1236
|
+
/**
|
|
1237
|
+
* @description 콜백 세계(EventSource 이벤트·타이머·abort)를 Promise 하나로 바꾸는 경계 어댑터.
|
|
1238
|
+
* 아래 결말 4개 중 먼저 발생하는 것으로 정확히 한 번 끝나고, hello는 결말이 아니라 수립 신호입니다.
|
|
1239
|
+
*/
|
|
1240
|
+
var streamUntilClosed = function streamUntilClosed(source, signal, hooks) {
|
|
1241
|
+
return new Promise(function (resolve) {
|
|
1242
|
+
var settled = false;
|
|
1243
|
+
var settle = function settle(closure, options) {
|
|
1244
|
+
if (settled) {
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
settled = true;
|
|
1248
|
+
clearTimeout(helloTimer);
|
|
1249
|
+
signal.removeEventListener('abort', onAbort);
|
|
1250
|
+
if (options.notifyClosed) {
|
|
1251
|
+
hooks.onClosed(closure);
|
|
1252
|
+
}
|
|
1253
|
+
resolve(closure.outcome);
|
|
1254
|
+
};
|
|
1255
|
+
var helloTimer = setTimeout(function () {
|
|
1256
|
+
return settle({
|
|
1257
|
+
outcome: 'error'
|
|
1258
|
+
}, {
|
|
1259
|
+
notifyClosed: true
|
|
1260
|
+
});
|
|
1261
|
+
}, hooks.helloTimeoutMs);
|
|
1262
|
+
source.addEventListener(SERVER_CONNECTION_EVENTS.goaway, function (event) {
|
|
1263
|
+
var goaway = toGoawayPayload(parsePayload(event));
|
|
1264
|
+
settle({
|
|
1265
|
+
outcome: goaway.action === 'close' ? 'goaway-close' : 'goaway',
|
|
1266
|
+
goaway: goaway
|
|
1267
|
+
}, {
|
|
1268
|
+
notifyClosed: true
|
|
1269
|
+
});
|
|
1270
|
+
});
|
|
1271
|
+
source.onerror = function () {
|
|
1272
|
+
return settle({
|
|
1273
|
+
outcome: 'error'
|
|
1274
|
+
}, {
|
|
1275
|
+
notifyClosed: true
|
|
1276
|
+
});
|
|
1277
|
+
};
|
|
1278
|
+
var onAbort = function onAbort() {
|
|
1279
|
+
return settle({
|
|
1280
|
+
outcome: 'error'
|
|
1281
|
+
}, {
|
|
1282
|
+
notifyClosed: false
|
|
1283
|
+
});
|
|
1284
|
+
};
|
|
1285
|
+
signal.addEventListener('abort', onAbort);
|
|
1286
|
+
source.addEventListener(SERVER_CONNECTION_EVENTS.hello, function () {
|
|
1287
|
+
if (settled) {
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
if (hooks.onHello()) {
|
|
1291
|
+
clearTimeout(helloTimer);
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
});
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
// 상태 코드 → 사유 매핑이 단일 원천입니다 — 멈출 코드 판정과 사유가 같은 표에서 나와 어긋날 수 없습니다
|
|
1298
|
+
var TICKET_FATAL_CAUSES = {
|
|
1299
|
+
401: 'auth',
|
|
1300
|
+
403: 'auth',
|
|
1301
|
+
429: 'rate_limit'
|
|
1302
|
+
};
|
|
1303
|
+
var isFatalTicketStatus = function isFatalTicketStatus(status) {
|
|
1304
|
+
return status in TICKET_FATAL_CAUSES;
|
|
1305
|
+
};
|
|
1306
|
+
var HELLO_TIMEOUT_MS = 10000;
|
|
1307
|
+
// 즉시 실패(CORS·오프라인)의 기본 백오프(full jitter) 기준 최대 15초, 보통 7~8초 안에 알립니다 — 순간 끊김에는 침묵합니다
|
|
1308
|
+
var STALLED_AFTER_ATTEMPTS = 5;
|
|
1309
|
+
/** @description SSR 환경에서도 안전하게 생성할 수 있습니다 — 연결은 connect()를 호출할 때 시작됩니다 */
|
|
1310
|
+
var _backoff = /*#__PURE__*/new WeakMap();
|
|
1311
|
+
var _machine = /*#__PURE__*/new WeakMap();
|
|
1312
|
+
var _currentStream = /*#__PURE__*/new WeakMap();
|
|
1313
|
+
var _abort = /*#__PURE__*/new WeakMap();
|
|
1314
|
+
var _hasOpened = /*#__PURE__*/new WeakMap();
|
|
1315
|
+
var _networkFailures = /*#__PURE__*/new WeakMap();
|
|
1316
|
+
var _SseClient_brand = /*#__PURE__*/new WeakSet();
|
|
1317
|
+
var SseClient = /*#__PURE__*/function (_BaseSseClient) {
|
|
1318
|
+
function SseClient(options) {
|
|
1319
|
+
var _internals$hasOpenedB;
|
|
1320
|
+
var _this;
|
|
1321
|
+
var internals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1322
|
+
_classCallCheck(this, SseClient);
|
|
1323
|
+
_this = _callSuper(this, SseClient, [options.logger]);
|
|
1324
|
+
// 한 연결 세션을 접습니다 — 다음 connect()가 첫 연결처럼 시작하도록 재수립 판단과 백오프도 함께 되돌립니다
|
|
1325
|
+
_classPrivateMethodInitSpec(_this, _SseClient_brand);
|
|
1326
|
+
_defineProperty(_this, "options", void 0);
|
|
1327
|
+
_defineProperty(_this, "internals", void 0);
|
|
1328
|
+
_classPrivateFieldInitSpec(_this, _backoff, void 0);
|
|
1329
|
+
_classPrivateFieldInitSpec(_this, _machine, void 0);
|
|
1330
|
+
_classPrivateFieldInitSpec(_this, _currentStream, null);
|
|
1331
|
+
_classPrivateFieldInitSpec(_this, _abort, null);
|
|
1332
|
+
_classPrivateFieldInitSpec(_this, _hasOpened, void 0);
|
|
1333
|
+
_classPrivateFieldInitSpec(_this, _networkFailures, 0);
|
|
1334
|
+
_this.options = options;
|
|
1335
|
+
_this.internals = internals;
|
|
1336
|
+
_classPrivateFieldSet2(_hasOpened, _this, (_internals$hasOpenedB = internals.hasOpenedBefore) !== null && _internals$hasOpenedB !== void 0 ? _internals$hasOpenedB : false);
|
|
1337
|
+
_classPrivateFieldSet2(_backoff, _this, createBackoff(options.backoff));
|
|
1338
|
+
_classPrivateFieldSet2(_machine, _this, createStatusMachine(function (status) {
|
|
1339
|
+
return _this.emitter.emitStatus(status);
|
|
1340
|
+
}));
|
|
1341
|
+
return _this;
|
|
1342
|
+
}
|
|
1343
|
+
_inherits(SseClient, _BaseSseClient);
|
|
1344
|
+
return _createClass(SseClient, [{
|
|
1345
|
+
key: "status",
|
|
1346
|
+
get: function get() {
|
|
1347
|
+
return _classPrivateFieldGet2(_machine, this).status;
|
|
1348
|
+
}
|
|
1349
|
+
}, {
|
|
1350
|
+
key: "connect",
|
|
1351
|
+
value: function connect() {
|
|
1352
|
+
var _this$options$eventSo,
|
|
1353
|
+
_this2 = this;
|
|
1354
|
+
if (((_this$options$eventSo = this.options.eventSource) !== null && _this$options$eventSo !== void 0 ? _this$options$eventSo : globalThis.EventSource) === undefined) {
|
|
1355
|
+
// SSR 대응 - no-op
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
var controller = new AbortController();
|
|
1359
|
+
var previous = _classPrivateFieldGet2(_abort, this);
|
|
1360
|
+
// 전이 통지 중 리스너가 disconnect()해도 이 controller가 abort되도록 전이보다 먼저 교체합니다
|
|
1361
|
+
_classPrivateFieldSet2(_abort, this, controller);
|
|
1362
|
+
if (!_classPrivateFieldGet2(_machine, this).transition('connect')) {
|
|
1363
|
+
_classPrivateFieldSet2(_abort, this, previous);
|
|
1364
|
+
if (_classPrivateFieldGet2(_machine, this).status === 'fatal') {
|
|
1365
|
+
var _this$options$logger;
|
|
1366
|
+
(_this$options$logger = this.options.logger) === null || _this$options$logger === void 0 || _this$options$logger.warn('sse: fatal 상태에서는 connect()가 무시됩니다 — disconnect()로 정리한 뒤 다시 connect() 하세요');
|
|
1367
|
+
}
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (controller.signal.aborted) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
// 루프가 예외로 죽으면 재시도가 영영 없습니다 — fatal로 떨어뜨려 회복 경로(정리 후 connect)에 맡깁니다
|
|
1374
|
+
void _assertClassBrand(_SseClient_brand, this, _runConnection).call(this, controller.signal)["catch"](function (error) {
|
|
1375
|
+
var _this2$options$logger;
|
|
1376
|
+
(_this2$options$logger = _this2.options.logger) === null || _this2$options$logger === void 0 || _this2$options$logger.error('sse: 연결 루프가 예외로 중단됐습니다', error);
|
|
1377
|
+
// 전이가 거부된 상태(reconnecting 등)에서 발화하면 "전이 후 발화" 계약이 깨집니다
|
|
1378
|
+
if (_classPrivateFieldGet2(_machine, _this2).transition('fatalFailure')) {
|
|
1379
|
+
_this2.emitter.emit(SDK_EVENTS.fatal, {
|
|
1380
|
+
cause: 'unknown'
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
}, {
|
|
1386
|
+
key: "disconnect",
|
|
1387
|
+
value: function disconnect() {
|
|
1388
|
+
var _classPrivateFieldGet2$1;
|
|
1389
|
+
// abort가 연결 루프를 중단합니다 — 진행 중이던 티켓 발급·대기·스트림 전부
|
|
1390
|
+
(_classPrivateFieldGet2$1 = _classPrivateFieldGet2(_abort, this)) === null || _classPrivateFieldGet2$1 === void 0 || _classPrivateFieldGet2$1.abort();
|
|
1391
|
+
_classPrivateFieldSet2(_abort, this, null);
|
|
1392
|
+
_assertClassBrand(_SseClient_brand, this, _endSession).call(this);
|
|
1393
|
+
_classPrivateFieldGet2(_machine, this).transition('disconnect');
|
|
1394
|
+
}
|
|
1395
|
+
}, {
|
|
1396
|
+
key: "registerSubscription",
|
|
1397
|
+
value:
|
|
1398
|
+
// 열린 스트림이 없으면 조용히 넘어갑니다 — 다음 #openStream이 구독 장부를 보고 한 번에 붙입니다
|
|
1399
|
+
function registerSubscription(type) {
|
|
1400
|
+
_assertClassBrand(_SseClient_brand, this, _attachSourceListener).call(this, type);
|
|
1401
|
+
}
|
|
1402
|
+
// 티켓 발급 → 스트림 유지 → 끊김 → 대기 → 재시도. disconnect(abort)만이 루프를 종료합니다
|
|
1403
|
+
}]);
|
|
1404
|
+
}(BaseSseClient);
|
|
1405
|
+
function _endSession() {
|
|
1406
|
+
_assertClassBrand(_SseClient_brand, this, _closeStream).call(this);
|
|
1407
|
+
_classPrivateFieldSet2(_hasOpened, this, false);
|
|
1408
|
+
_classPrivateFieldGet2(_backoff, this).reset();
|
|
1409
|
+
_classPrivateFieldSet2(_networkFailures, this, 0);
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* @description hello에 닿지 못한 네트워크성(응답 status 부재) 연속 실패를 셉니다 — 임계값 도달 순간에만
|
|
1413
|
+
* stalled를 발화해 한 구간에 한 번만 알립니다. 반드시 상태 전이 뒤에 부르세요.
|
|
1414
|
+
* 연속은 open 도달과 서버 응답(5xx)이 끊고, 스트림 단계 실패는 status가 없어 전부 여기 해당합니다.
|
|
1415
|
+
*/
|
|
1416
|
+
function _countNetworkFailure() {
|
|
1417
|
+
_classPrivateFieldSet2(_networkFailures, this, _classPrivateFieldGet2(_networkFailures, this) + 1);
|
|
1418
|
+
if (_classPrivateFieldGet2(_networkFailures, this) === STALLED_AFTER_ATTEMPTS) {
|
|
1419
|
+
this.emitter.emit(SDK_EVENTS.stalled, undefined);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function _runConnection(_x) {
|
|
1423
|
+
return _runConnection2.apply(this, arguments);
|
|
1424
|
+
}
|
|
1425
|
+
function _runConnection2() {
|
|
1426
|
+
_runConnection2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(signal) {
|
|
1427
|
+
var next;
|
|
1428
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
1429
|
+
while (1) switch (_context.prev = _context.next) {
|
|
1430
|
+
case 0:
|
|
1431
|
+
if (signal.aborted) {
|
|
1432
|
+
_context.next = 14;
|
|
1433
|
+
break;
|
|
1434
|
+
}
|
|
1435
|
+
_context.next = 3;
|
|
1436
|
+
return _assertClassBrand(_SseClient_brand, this, _attemptConnection).call(this, signal);
|
|
1437
|
+
case 3:
|
|
1438
|
+
next = _context.sent;
|
|
1439
|
+
if (!(signal.aborted || next === 'stop')) {
|
|
1440
|
+
_context.next = 6;
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1443
|
+
return _context.abrupt("return");
|
|
1444
|
+
case 6:
|
|
1445
|
+
if (!(next === 'retryWithDelay')) {
|
|
1446
|
+
_context.next = 11;
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
_context.next = 9;
|
|
1450
|
+
return waitForRetry(_classPrivateFieldGet2(_backoff, this).nextDelayMs(), signal);
|
|
1451
|
+
case 9:
|
|
1452
|
+
if (!signal.aborted) {
|
|
1453
|
+
_context.next = 11;
|
|
1454
|
+
break;
|
|
1455
|
+
}
|
|
1456
|
+
return _context.abrupt("return");
|
|
1457
|
+
case 11:
|
|
1458
|
+
_classPrivateFieldGet2(_machine, this).transition('readyToRetry');
|
|
1459
|
+
_context.next = 0;
|
|
1460
|
+
break;
|
|
1461
|
+
case 14:
|
|
1462
|
+
case "end":
|
|
1463
|
+
return _context.stop();
|
|
1464
|
+
}
|
|
1465
|
+
}, _callee, this);
|
|
1466
|
+
}));
|
|
1467
|
+
return _runConnection2.apply(this, arguments);
|
|
1468
|
+
}
|
|
1469
|
+
// 스트림이 끊길 때까지 대기하고, 루프의 다음 동작('stop'·'retryWithDelay'·'retryNow')을 반환합니다
|
|
1470
|
+
function _attemptConnection(_x2) {
|
|
1471
|
+
return _attemptConnection2.apply(this, arguments);
|
|
1472
|
+
}
|
|
1473
|
+
function _attemptConnection2() {
|
|
1474
|
+
_attemptConnection2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(signal) {
|
|
1475
|
+
var _this$internals$issue, _this$internals$issue2, _this$internals;
|
|
1476
|
+
var result, _result$error, status, cause, _this$options$logger2, outcome, _this$options$logger3;
|
|
1477
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
1478
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
1479
|
+
case 0:
|
|
1480
|
+
_context2.next = 2;
|
|
1481
|
+
return (_this$internals$issue = (_this$internals$issue2 = (_this$internals = this.internals).issueTicket) === null || _this$internals$issue2 === void 0 ? void 0 : _this$internals$issue2.call(_this$internals)) !== null && _this$internals$issue !== void 0 ? _this$internals$issue : issueTicketWithTimeout(this.options);
|
|
1482
|
+
case 2:
|
|
1483
|
+
result = _context2.sent;
|
|
1484
|
+
if (!signal.aborted) {
|
|
1485
|
+
_context2.next = 5;
|
|
1486
|
+
break;
|
|
1487
|
+
}
|
|
1488
|
+
return _context2.abrupt("return", 'stop');
|
|
1489
|
+
case 5:
|
|
1490
|
+
if (result.ok) {
|
|
1491
|
+
_context2.next = 19;
|
|
1492
|
+
break;
|
|
1493
|
+
}
|
|
1494
|
+
_result$error = result.error, status = _result$error.status, cause = _result$error.cause; // 로그는 원인 예외를 쥔 ticket.ts가 남깁니다 — 여기서 또 찍으면 직결 경로에서 같은 말이 두 줄 됩니다
|
|
1495
|
+
if (!(cause === 'auth_callback')) {
|
|
1496
|
+
_context2.next = 11;
|
|
1497
|
+
break;
|
|
1498
|
+
}
|
|
1499
|
+
_classPrivateFieldGet2(_machine, this).transition('fatalFailure');
|
|
1500
|
+
this.emitter.emit(SDK_EVENTS.fatal, {
|
|
1501
|
+
cause: 'auth_callback'
|
|
1502
|
+
});
|
|
1503
|
+
return _context2.abrupt("return", 'stop');
|
|
1504
|
+
case 11:
|
|
1505
|
+
if (!(status !== undefined && isFatalTicketStatus(status))) {
|
|
1506
|
+
_context2.next = 16;
|
|
1507
|
+
break;
|
|
1508
|
+
}
|
|
1509
|
+
(_this$options$logger2 = this.options.logger) === null || _this$options$logger2 === void 0 || _this$options$logger2.warn("sse: \uD2F0\uCF13 \uBC1C\uAE09\uC774 ".concat(status, "\uB85C \uAC70\uBD80\uB418\uC5B4 \uBA48\uCDA5\uB2C8\uB2E4 \u2014 disconnect()\uB85C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC connect() \uD558\uC138\uC694"));
|
|
1510
|
+
_classPrivateFieldGet2(_machine, this).transition('fatalFailure');
|
|
1511
|
+
// 전이를 마친 뒤 알립니다 — 핸들러가 그 자리에서 disconnect()·connect() 회복을 시작할 수 있게
|
|
1512
|
+
this.emitter.emit(SDK_EVENTS.fatal, {
|
|
1513
|
+
cause: TICKET_FATAL_CAUSES[status],
|
|
1514
|
+
status: status
|
|
1515
|
+
});
|
|
1516
|
+
return _context2.abrupt("return", 'stop');
|
|
1517
|
+
case 16:
|
|
1518
|
+
_classPrivateFieldGet2(_machine, this).transition('retryableFailure');
|
|
1519
|
+
// 서버가 응답한 거부(5xx 등)는 네트워크성이 아니라 세지 않습니다 — 서버 장애에 네트워크 안내가 나가는 오안내를 막습니다
|
|
1520
|
+
if (status === undefined) {
|
|
1521
|
+
_assertClassBrand(_SseClient_brand, this, _countNetworkFailure).call(this);
|
|
1522
|
+
} else {
|
|
1523
|
+
_classPrivateFieldSet2(_networkFailures, this, 0);
|
|
1524
|
+
}
|
|
1525
|
+
return _context2.abrupt("return", 'retryWithDelay');
|
|
1526
|
+
case 19:
|
|
1527
|
+
_context2.prev = 19;
|
|
1528
|
+
_context2.next = 22;
|
|
1529
|
+
return _assertClassBrand(_SseClient_brand, this, _openStream).call(this, result.ticket, signal);
|
|
1530
|
+
case 22:
|
|
1531
|
+
outcome = _context2.sent;
|
|
1532
|
+
if (!(outcome === 'goaway-close')) {
|
|
1533
|
+
_context2.next = 25;
|
|
1534
|
+
break;
|
|
1535
|
+
}
|
|
1536
|
+
return _context2.abrupt("return", 'stop');
|
|
1537
|
+
case 25:
|
|
1538
|
+
return _context2.abrupt("return", outcome === 'goaway' ? 'retryNow' : 'retryWithDelay');
|
|
1539
|
+
case 28:
|
|
1540
|
+
_context2.prev = 28;
|
|
1541
|
+
_context2.t0 = _context2["catch"](19);
|
|
1542
|
+
(_this$options$logger3 = this.options.logger) === null || _this$options$logger3 === void 0 || _this$options$logger3.error('sse: EventSource 생성 실패 — baseUrl을 확인하세요', _context2.t0);
|
|
1543
|
+
_classPrivateFieldGet2(_machine, this).transition('fatalFailure');
|
|
1544
|
+
// 생성의 동기 throw는 설정 실수입니다 — 네트워크성 실패는 onerror로 와서 reconnecting으로 갑니다
|
|
1545
|
+
this.emitter.emit(SDK_EVENTS.fatal, {
|
|
1546
|
+
cause: 'config'
|
|
1547
|
+
});
|
|
1548
|
+
return _context2.abrupt("return", 'stop');
|
|
1549
|
+
case 34:
|
|
1550
|
+
case "end":
|
|
1551
|
+
return _context2.stop();
|
|
1552
|
+
}
|
|
1553
|
+
}, _callee2, this, [[19, 28]]);
|
|
1554
|
+
}));
|
|
1555
|
+
return _attemptConnection2.apply(this, arguments);
|
|
1556
|
+
}
|
|
1557
|
+
// EventSource를 생성하고 스트림이 끊길 때까지 대기합니다 — 이벤트 연결은 stream.ts, 여기서는 상태 전이만 담당합니다
|
|
1558
|
+
function _openStream(ticket, signal) {
|
|
1559
|
+
var _this$options$eventSo2,
|
|
1560
|
+
_this3 = this;
|
|
1561
|
+
var EventSourceImpl = (_this$options$eventSo2 = this.options.eventSource) !== null && _this$options$eventSo2 !== void 0 ? _this$options$eventSo2 : globalThis.EventSource;
|
|
1562
|
+
var source = new EventSourceImpl("".concat(this.options.baseUrl, "/v1/events?ticket=").concat(encodeURIComponent(ticket)));
|
|
1563
|
+
_classPrivateFieldSet2(_currentStream, this, {
|
|
1564
|
+
source: source,
|
|
1565
|
+
attachedEvents: new Set()
|
|
1566
|
+
});
|
|
1567
|
+
this.emitter.handlerTypes().forEach(function (type) {
|
|
1568
|
+
return _assertClassBrand(_SseClient_brand, _this3, _attachSourceListener).call(_this3, type);
|
|
1569
|
+
});
|
|
1570
|
+
// hello 전 끊김(타임아웃 포함)만 미연결로 셉니다 — 수립 후 끊김은 다음 시도부터 다시 셉니다
|
|
1571
|
+
var opened = false;
|
|
1572
|
+
return streamUntilClosed(source, signal, {
|
|
1573
|
+
helloTimeoutMs: HELLO_TIMEOUT_MS,
|
|
1574
|
+
onHello: function onHello() {
|
|
1575
|
+
if (!_classPrivateFieldGet2(_machine, _this3).transition('hello')) {
|
|
1576
|
+
return false;
|
|
1577
|
+
}
|
|
1578
|
+
if (signal.aborted) {
|
|
1579
|
+
return false;
|
|
1580
|
+
}
|
|
1581
|
+
opened = true;
|
|
1582
|
+
_classPrivateFieldGet2(_backoff, _this3).reset();
|
|
1583
|
+
_classPrivateFieldSet2(_networkFailures, _this3, 0);
|
|
1584
|
+
// 갱신 전에 읽어야 이번 hello를 뺀 값입니다 — 두 번째부터가 재수립입니다
|
|
1585
|
+
var isReestablished = _classPrivateFieldGet2(_hasOpened, _this3);
|
|
1586
|
+
_classPrivateFieldSet2(_hasOpened, _this3, true);
|
|
1587
|
+
if (isReestablished) {
|
|
1588
|
+
_this3.emitter.emit(SDK_EVENTS.resync, undefined);
|
|
1589
|
+
}
|
|
1590
|
+
return true;
|
|
1591
|
+
},
|
|
1592
|
+
onClosed: function onClosed(closure) {
|
|
1593
|
+
if (closure.outcome === 'error') {
|
|
1594
|
+
_assertClassBrand(_SseClient_brand, _this3, _closeStream).call(_this3);
|
|
1595
|
+
_classPrivateFieldGet2(_machine, _this3).transition('retryableFailure');
|
|
1596
|
+
if (!opened) {
|
|
1597
|
+
_assertClassBrand(_SseClient_brand, _this3, _countNetworkFailure).call(_this3);
|
|
1598
|
+
}
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
// 서버가 재연결하지 말라고 했으면 reconnecting을 거치지 않습니다 — 소비처에 헛된 재연결 중을 보이지 않게
|
|
1602
|
+
if (closure.outcome === 'goaway-close') {
|
|
1603
|
+
_assertClassBrand(_SseClient_brand, _this3, _endSession).call(_this3);
|
|
1604
|
+
_classPrivateFieldGet2(_machine, _this3).transition('disconnect');
|
|
1605
|
+
} else {
|
|
1606
|
+
_assertClassBrand(_SseClient_brand, _this3, _closeStream).call(_this3);
|
|
1607
|
+
_classPrivateFieldGet2(_machine, _this3).transition('retryableFailure');
|
|
1608
|
+
}
|
|
1609
|
+
// 전이를 마친 뒤 알립니다 — 핸들러가 그 자리에서 connect()·disconnect()를 부를 수 있게
|
|
1610
|
+
_this3.emitter.emit(SERVER_CONNECTION_EVENTS.goaway, closure.goaway);
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
function _attachSourceListener(type) {
|
|
1615
|
+
var _this4 = this;
|
|
1616
|
+
if (_classPrivateFieldGet2(_currentStream, this) === null) {
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
var _classPrivateFieldGet3 = _classPrivateFieldGet2(_currentStream, this),
|
|
1620
|
+
source = _classPrivateFieldGet3.source,
|
|
1621
|
+
attachedEvents = _classPrivateFieldGet3.attachedEvents;
|
|
1622
|
+
attachDomainListener(source, attachedEvents, type, function (t, payload) {
|
|
1623
|
+
return _this4.emitter.emit(t, payload);
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
function _closeStream() {
|
|
1627
|
+
var _classPrivateFieldGet4;
|
|
1628
|
+
(_classPrivateFieldGet4 = _classPrivateFieldGet2(_currentStream, this)) === null || _classPrivateFieldGet4 === void 0 || _classPrivateFieldGet4.source.close();
|
|
1629
|
+
_classPrivateFieldSet2(_currentStream, this, null);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
var isDeadStatus = function isDeadStatus(status) {
|
|
1633
|
+
return status === 'fatal' || status === 'closed';
|
|
1634
|
+
};
|
|
1635
|
+
/**
|
|
1636
|
+
* @description 모든 탭이 함께 쓰는 워커의 연결 하나 — 수립·정리와 구독 부착을 맡습니다.
|
|
1637
|
+
* 탭 장부(hub)는 "탭이 남았는가"만 판단하고, 그 판단을 연결에 적용하는 방법은 여기서만 압니다.
|
|
1638
|
+
*/
|
|
1639
|
+
var createWorkerConnection = function createWorkerConnection(_ref) {
|
|
1640
|
+
var options = _ref.options,
|
|
1641
|
+
ticketDelegate = _ref.ticketDelegate,
|
|
1642
|
+
onStatus = _ref.onStatus,
|
|
1643
|
+
onEvent = _ref.onEvent;
|
|
1644
|
+
var subscriptions = new Set();
|
|
1645
|
+
// 하나로 묶어 셋이 따로 노는 상태를 표현 불가능하게 만듭니다
|
|
1646
|
+
var active = null;
|
|
1647
|
+
// 인스턴스보다 오래 삽니다 — 재수립 판정이 SseClient 수명에 묶이면 접힌 뒤 복구에서 resync가 사라집니다
|
|
1648
|
+
var hasOpenedBefore = false;
|
|
1649
|
+
var attach = function attach(client, event) {
|
|
1650
|
+
client.on(event, function (payload) {
|
|
1651
|
+
return onEvent(event, payload);
|
|
1652
|
+
});
|
|
1653
|
+
};
|
|
1654
|
+
var open = function open(baseUrl) {
|
|
1655
|
+
// 워커 스코프에는 토큰이 없습니다 — 티켓은 issueTicket으로 탭에 위임하므로 이 값은 쓰이지 않습니다
|
|
1656
|
+
var client = new SseClient(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
1657
|
+
baseUrl: baseUrl,
|
|
1658
|
+
getAuthToken: function getAuthToken() {
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
}), {
|
|
1662
|
+
issueTicket: ticketDelegate.request,
|
|
1663
|
+
hasOpenedBefore: hasOpenedBefore
|
|
1664
|
+
});
|
|
1665
|
+
var detachStatus = client.onStatus(function (next) {
|
|
1666
|
+
// open에 닿았다는 건 hello를 받았다는 뜻입니다 — 인스턴스가 바뀌어도 이 사실은 남아야 합니다
|
|
1667
|
+
if (next === 'open') {
|
|
1668
|
+
hasOpenedBefore = true;
|
|
1669
|
+
}
|
|
1670
|
+
// 이 리스너에 닿는 closed는 goaway(action: close)뿐입니다(자체 close는 먼저 detach) —
|
|
1671
|
+
// 서버가 세션을 끝냈으니 직결 경로(#endSession)와 같은 판정으로 다음 연결은 첫 연결입니다
|
|
1672
|
+
if (next === 'closed') {
|
|
1673
|
+
hasOpenedBefore = false;
|
|
1674
|
+
}
|
|
1675
|
+
onStatus(next);
|
|
1676
|
+
});
|
|
1677
|
+
active = {
|
|
1678
|
+
client: client,
|
|
1679
|
+
detachStatus: detachStatus,
|
|
1680
|
+
baseUrl: baseUrl
|
|
1681
|
+
};
|
|
1682
|
+
FORWARDED_EVENTS.forEach(function (event) {
|
|
1683
|
+
return attach(client, event);
|
|
1684
|
+
});
|
|
1685
|
+
subscriptions.forEach(function (event) {
|
|
1686
|
+
return attach(client, event);
|
|
1687
|
+
});
|
|
1688
|
+
client.connect();
|
|
1689
|
+
};
|
|
1690
|
+
/**
|
|
1691
|
+
* @description 연결을 접습니다. endSession이면 재수립 판정도 함께 되돌립니다.
|
|
1692
|
+
* 탭이 모두 스스로 떠났으면 세션이 끝난 것이고, 다음 탭에게는 첫 연결입니다 —
|
|
1693
|
+
* 이어서 두면 신규 탭이 놓친 구간도 없이 resync를 받습니다.
|
|
1694
|
+
*/
|
|
1695
|
+
var close = function close() {
|
|
1696
|
+
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
1697
|
+
_ref2$endSession = _ref2.endSession,
|
|
1698
|
+
endSession = _ref2$endSession === void 0 ? false : _ref2$endSession;
|
|
1699
|
+
if (endSession) {
|
|
1700
|
+
hasOpenedBefore = false;
|
|
1701
|
+
}
|
|
1702
|
+
if (active === null) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
// 정리 중 나오는 closed는 워커 내부 사정입니다 — 탭에 전하기 전에 통지를 끊습니다
|
|
1706
|
+
active.detachStatus();
|
|
1707
|
+
active.client.disconnect();
|
|
1708
|
+
active = null;
|
|
1709
|
+
ticketDelegate.cancel();
|
|
1710
|
+
// 연결이 없어졌다는 사실은 알려야 합니다 — 안 그러면 뒤에 합류한 탭이 낡은 open 스냅샷을 받습니다
|
|
1711
|
+
onStatus('idle');
|
|
1712
|
+
};
|
|
1713
|
+
return {
|
|
1714
|
+
ensureOpen: function ensureOpen(baseUrl) {
|
|
1715
|
+
if (active !== null && isDeadStatus(active.client.status)) {
|
|
1716
|
+
close();
|
|
1717
|
+
}
|
|
1718
|
+
if (active === null) {
|
|
1719
|
+
open(baseUrl);
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
// 선 연결을 그대로 씁니다 — 다른 baseUrl로 붙으려던 탭은 그 사실을 알 길이 없어 알립니다
|
|
1723
|
+
if (baseUrl !== active.baseUrl) {
|
|
1724
|
+
var _options$logger;
|
|
1725
|
+
(_options$logger = options.logger) === null || _options$logger === void 0 || _options$logger.warn("sse: \uC774\uBBF8 ".concat(active.baseUrl, "\uB85C \uC5F0\uACB0\uB3FC \uC788\uC5B4 baseUrl(").concat(baseUrl, ")\uC744 \uBB34\uC2DC\uD569\uB2C8\uB2E4"));
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
close: close,
|
|
1729
|
+
/** @description 선 연결에는 즉시, 이후 연결에는 수립 시점에 붙습니다 */
|
|
1730
|
+
subscribe: function subscribe(event) {
|
|
1731
|
+
if (subscriptions.has(event)) {
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
subscriptions.add(event);
|
|
1735
|
+
if (active !== null) {
|
|
1736
|
+
attach(active.client, event);
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
subscribedEvents: function subscribedEvents() {
|
|
1740
|
+
return _toConsumableArray(subscriptions);
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* @description SharedWorker 쪽 메시지 라우팅 — 탭이 보낸 것을 연결에 전하고, 연결이 낸 것을 탭에 전합니다.
|
|
1747
|
+
* 포트 장부(frozen 탭을 빼지 않는 이유 포함)는 portRegistry가, 연결 수립·정리는 workerConnection이 소유합니다.
|
|
1748
|
+
* 배선 현황은 protocol.ts의 지도를 보세요.
|
|
1749
|
+
*/
|
|
1750
|
+
var createWorkerHub = function createWorkerHub() {
|
|
1751
|
+
var hubOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1752
|
+
// 늦게 합류한 탭에 스냅샷으로 보냅니다 — 저장과 통지가 어긋나지 않게 setStatus로만 바꿉니다
|
|
1753
|
+
var status = 'idle';
|
|
1754
|
+
var registry = createPortRegistry();
|
|
1755
|
+
var broadcast = function broadcast(message) {
|
|
1756
|
+
registry.forEach(function (port) {
|
|
1757
|
+
return port.postMessage(message);
|
|
1758
|
+
});
|
|
1759
|
+
};
|
|
1760
|
+
var setStatus = function setStatus(next) {
|
|
1761
|
+
status = next;
|
|
1762
|
+
broadcast({
|
|
1763
|
+
kind: 'status',
|
|
1764
|
+
status: next
|
|
1765
|
+
});
|
|
1766
|
+
};
|
|
1767
|
+
var ticketDelegate = createTicketDelegate(function () {
|
|
1768
|
+
var target = registry.livelyPort();
|
|
1769
|
+
if (target !== undefined) {
|
|
1770
|
+
target.postMessage({
|
|
1771
|
+
kind: 'ticket-request'
|
|
1772
|
+
});
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
// 전원이 침묵하면 접습니다 — 세션 종료가 아니라 휴면이라, 재수립 판정은 그대로 둡니다.
|
|
1776
|
+
// microtask인 이유: 이 콜백은 연결 루프의 티켓 발급 스택 안이라, 동기로 접으면 그 루프를 재진입으로 끊습니다
|
|
1777
|
+
queueMicrotask(function () {
|
|
1778
|
+
return connection.close();
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
1781
|
+
var connection = createWorkerConnection({
|
|
1782
|
+
options: hubOptions,
|
|
1783
|
+
ticketDelegate: ticketDelegate,
|
|
1784
|
+
onStatus: setStatus,
|
|
1785
|
+
onEvent: function onEvent(event, payload) {
|
|
1786
|
+
return broadcast({
|
|
1787
|
+
kind: 'event',
|
|
1788
|
+
event: event,
|
|
1789
|
+
payload: payload
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
return {
|
|
1794
|
+
addPort: function addPort(port) {
|
|
1795
|
+
registry.add(port);
|
|
1796
|
+
port.postMessage({
|
|
1797
|
+
kind: 'status',
|
|
1798
|
+
status: status
|
|
1799
|
+
});
|
|
1800
|
+
},
|
|
1801
|
+
handleMessage: function handleMessage(port, data) {
|
|
1802
|
+
// close로 떠난 포트의 늦은 메시지만 거릅니다 — frozen 탭은 장부에 남아 그대로 지나갑니다
|
|
1803
|
+
if (!registry.has(port)) {
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
// 어떤 메시지든 생존 증거입니다 — pong만 세면 깨어나 init부터 보낸 탭이 침묵으로 남아 티켓을 못 받습니다
|
|
1807
|
+
registry.markAlive(port);
|
|
1808
|
+
var message = data;
|
|
1809
|
+
switch (message === null || message === void 0 ? void 0 : message.kind) {
|
|
1810
|
+
case 'init':
|
|
1811
|
+
// 휴면(전원 침묵)으로 접힌 연결은 깨어난 탭의 이 신호로 다시 섭니다
|
|
1812
|
+
if (typeof message.baseUrl === 'string') {
|
|
1813
|
+
connection.ensureOpen(message.baseUrl);
|
|
1814
|
+
}
|
|
1815
|
+
return;
|
|
1816
|
+
case 'subscribe':
|
|
1817
|
+
// 예약 이벤트·비문자열은 프록시가 걸러 보내지만, 구버전·타 구현 탭에 대비해 한 번 더 거릅니다
|
|
1818
|
+
if (typeof message.event === 'string' && isSubscribableEvent(message.event)) {
|
|
1819
|
+
connection.subscribe(message.event);
|
|
1820
|
+
}
|
|
1821
|
+
return;
|
|
1822
|
+
case 'ticket-response':
|
|
1823
|
+
// 여기서 거르지 않으면 잘못된 응답이 연결 루프를 예외로 죽여 워커가 고착됩니다
|
|
1824
|
+
if (isTicketResponse(message)) {
|
|
1825
|
+
ticketDelegate.settle(message);
|
|
1826
|
+
}
|
|
1827
|
+
return;
|
|
1828
|
+
case 'pong':
|
|
1829
|
+
// 생존 표시는 위에서 이미 끝났습니다 — pong은 그 증거만 나르는 메시지입니다
|
|
1830
|
+
return;
|
|
1831
|
+
case 'close':
|
|
1832
|
+
{
|
|
1833
|
+
var _registry$close = registry.close(port),
|
|
1834
|
+
wasLast = _registry$close.wasLast;
|
|
1835
|
+
// 모두 스스로 떠났으니 세션이 끝났습니다 — 다음 탭에게는 첫 연결입니다
|
|
1836
|
+
if (wasLast) {
|
|
1837
|
+
connection.close({
|
|
1838
|
+
endSession: true
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
default:
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
},
|
|
1847
|
+
broadcast: broadcast,
|
|
1848
|
+
tabCount: registry.size,
|
|
1849
|
+
subscribedEvents: connection.subscribedEvents
|
|
1850
|
+
};
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1853
|
+
var hub = createWorkerHub();
|
|
1854
|
+
self.onconnect = function (event) {
|
|
1855
|
+
var port = event.ports[0];
|
|
1856
|
+
if (port === undefined) {
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
hub.addPort(port);
|
|
1860
|
+
port.onmessage = function (message) {
|
|
1861
|
+
return hub.handleMessage(port, message.data);
|
|
1862
|
+
};
|
|
1863
|
+
port.start();
|
|
1864
|
+
};
|
|
1865
|
+
//# sourceMappingURL=sse.worker.js.map
|