@bstage-sdk/bridge 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -4
- package/dist/{chunk-KQMJY4ZH.js → chunk-CSAW3L7P.js} +204 -3
- package/dist/{chunk-6LV7HEN4.js → chunk-ND2HFYBP.js} +9 -3
- package/dist/{createSrcdoc-bOoxttPz.d.cts → createSrcdoc-DgQyvIjG.d.cts} +3 -0
- package/dist/{createSrcdoc-bOoxttPz.d.ts → createSrcdoc-DgQyvIjG.d.ts} +3 -0
- package/dist/frame.cjs +206 -4
- package/dist/frame.d.cts +52 -7
- package/dist/frame.d.ts +52 -7
- package/dist/frame.js +5 -3
- package/dist/host.cjs +249 -8
- package/dist/host.d.cts +25 -7
- package/dist/host.d.ts +25 -7
- package/dist/host.js +45 -7
- package/dist/index.cjs +209 -3
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +4 -2
- package/dist/{messages-9N0GFq5b.d.cts → messages-BjpZyiLy.d.cts} +12 -1
- package/dist/{messages-9N0GFq5b.d.ts → messages-BjpZyiLy.d.ts} +12 -1
- package/package.json +1 -1
package/dist/frame.cjs
CHANGED
|
@@ -21,7 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var frame_exports = {};
|
|
22
22
|
__export(frame_exports, {
|
|
23
23
|
createBridgeClient: () => createBridgeClient,
|
|
24
|
-
createBridgeFetch: () => createBridgeFetch
|
|
24
|
+
createBridgeFetch: () => createBridgeFetch,
|
|
25
|
+
installDirectFetch: () => installDirectFetch
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(frame_exports);
|
|
27
28
|
|
|
@@ -130,6 +131,20 @@ function bridgeRuntime(g, makeFetch, config) {
|
|
|
130
131
|
const session = config.session;
|
|
131
132
|
const hostOrigin = config.hostOrigin;
|
|
132
133
|
const parent = g.parent;
|
|
134
|
+
const sendToHost = parent ? parent.postMessage.bind(parent) : void 0;
|
|
135
|
+
const stopInterval = clearInterval;
|
|
136
|
+
const stopTimeout = clearTimeout;
|
|
137
|
+
let readPersisted;
|
|
138
|
+
const ownDescriptor = Object.getOwnPropertyDescriptor;
|
|
139
|
+
const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
|
|
140
|
+
try {
|
|
141
|
+
const pte = g.PageTransitionEvent;
|
|
142
|
+
const desc = pte ? Object.getOwnPropertyDescriptor(pte.prototype, "persisted") : void 0;
|
|
143
|
+
if (desc && typeof desc.get === "function")
|
|
144
|
+
readPersisted = Function.prototype.call.bind(desc.get);
|
|
145
|
+
} catch {
|
|
146
|
+
readPersisted = void 0;
|
|
147
|
+
}
|
|
133
148
|
const pending = /* @__PURE__ */ new Map();
|
|
134
149
|
const listeners = /* @__PURE__ */ new Map();
|
|
135
150
|
let init;
|
|
@@ -146,6 +161,17 @@ function bridgeRuntime(g, makeFetch, config) {
|
|
|
146
161
|
return out;
|
|
147
162
|
}
|
|
148
163
|
const idPrefix = randomPrefix();
|
|
164
|
+
function secureToken() {
|
|
165
|
+
if (!g.crypto || typeof g.crypto.getRandomValues !== "function") return "";
|
|
166
|
+
const bytes = new Uint8Array(16);
|
|
167
|
+
g.crypto.getRandomValues(bytes);
|
|
168
|
+
let out = "";
|
|
169
|
+
for (let i = 0; i < bytes.length; i += 1) out += (bytes[i] + 256).toString(16).slice(1);
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
const frameToken = secureToken();
|
|
173
|
+
let closed = frameToken === "";
|
|
174
|
+
let unloaded = false;
|
|
149
175
|
function nextId() {
|
|
150
176
|
seq += 1;
|
|
151
177
|
return idPrefix + "-" + seq;
|
|
@@ -157,9 +183,12 @@ function bridgeRuntime(g, makeFetch, config) {
|
|
|
157
183
|
return err;
|
|
158
184
|
}
|
|
159
185
|
function post(kind, type, id, payload) {
|
|
160
|
-
if (!
|
|
186
|
+
if (!sendToHost || closed) return;
|
|
161
187
|
const body = Object.assign({}, payload, { session });
|
|
162
|
-
|
|
188
|
+
sendToHost(
|
|
189
|
+
{ v: 1, ns: NS, id, kind, type, payload: body, frame: frameToken },
|
|
190
|
+
hostOrigin
|
|
191
|
+
);
|
|
163
192
|
}
|
|
164
193
|
function isEnvelope(m) {
|
|
165
194
|
if (typeof m !== "object" || m === null) return false;
|
|
@@ -232,6 +261,54 @@ function bridgeRuntime(g, makeFetch, config) {
|
|
|
232
261
|
}
|
|
233
262
|
}
|
|
234
263
|
g.addEventListener("message", onMessage);
|
|
264
|
+
function isPersisted(ev) {
|
|
265
|
+
try {
|
|
266
|
+
if (readPersisted) return readPersisted(ev) === true;
|
|
267
|
+
if (typeof ev !== "object" || ev === null) return false;
|
|
268
|
+
const desc = ownDescriptor(ev, "persisted");
|
|
269
|
+
return !!desc && hasOwn(desc, "value") && desc.value === true;
|
|
270
|
+
} catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function onPageHide(ev) {
|
|
275
|
+
if (unloaded || isPersisted(ev)) return;
|
|
276
|
+
unloaded = true;
|
|
277
|
+
closed = true;
|
|
278
|
+
if (sendToHost) {
|
|
279
|
+
try {
|
|
280
|
+
sendToHost(
|
|
281
|
+
{
|
|
282
|
+
v: 1,
|
|
283
|
+
ns: NS,
|
|
284
|
+
id: idPrefix + "-unload",
|
|
285
|
+
kind: "evt",
|
|
286
|
+
type: "frame-unload",
|
|
287
|
+
payload: { session },
|
|
288
|
+
frame: frameToken
|
|
289
|
+
},
|
|
290
|
+
hostOrigin
|
|
291
|
+
);
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
stopInterval(readyTimer);
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
pending.forEach(function(entry) {
|
|
301
|
+
try {
|
|
302
|
+
stopTimeout(entry.timer);
|
|
303
|
+
entry.reject(codeError("UNSUPPORTED", "bridge closed"));
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
pending.clear();
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
g.addEventListener("pagehide", onPageHide, true);
|
|
235
312
|
let readyAttempts = 0;
|
|
236
313
|
function sendReady() {
|
|
237
314
|
readyAttempts += 1;
|
|
@@ -245,6 +322,10 @@ function bridgeRuntime(g, makeFetch, config) {
|
|
|
245
322
|
sendReady();
|
|
246
323
|
function request(type, payload) {
|
|
247
324
|
return new Promise(function(resolve, reject) {
|
|
325
|
+
if (closed) {
|
|
326
|
+
reject(codeError("UNSUPPORTED", "bridge closed"));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
248
329
|
const id = nextId();
|
|
249
330
|
const timer = setTimeout(function() {
|
|
250
331
|
pending.delete(id);
|
|
@@ -299,8 +380,129 @@ function createBridgeClient(options = {}) {
|
|
|
299
380
|
}
|
|
300
381
|
return bridgeRuntime(g, createBridgeFetch, { session, hostOrigin });
|
|
301
382
|
}
|
|
383
|
+
|
|
384
|
+
// src/frame/directFetch.ts
|
|
385
|
+
function installDirectFetch(g, client, config) {
|
|
386
|
+
const hostOrigin = config.hostOrigin;
|
|
387
|
+
const callFn = Function.prototype.call;
|
|
388
|
+
const NativeURL = g.URL;
|
|
389
|
+
const rawFetch = g.fetch;
|
|
390
|
+
const nativeFetch = typeof rawFetch === "function" ? callFn.bind(rawFetch) : void 0;
|
|
391
|
+
const bridgeFetch = client.fetch;
|
|
392
|
+
const strSlice = callFn.bind(String.prototype.slice);
|
|
393
|
+
const promiseThen = callFn.bind(Promise.prototype.then);
|
|
394
|
+
const promiseReject = Promise.reject.bind(Promise);
|
|
395
|
+
const assign = Object.assign;
|
|
396
|
+
const TypeErr = TypeError;
|
|
397
|
+
function getter(ctor, name) {
|
|
398
|
+
if (typeof ctor !== "function") return void 0;
|
|
399
|
+
let proto = ctor.prototype;
|
|
400
|
+
while (proto) {
|
|
401
|
+
const desc = Object.getOwnPropertyDescriptor(proto, name);
|
|
402
|
+
if (desc && typeof desc.get === "function") {
|
|
403
|
+
return callFn.bind(desc.get);
|
|
404
|
+
}
|
|
405
|
+
proto = Object.getPrototypeOf(proto);
|
|
406
|
+
}
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
const getHref = getter(NativeURL, "href");
|
|
410
|
+
const getOrigin = getter(NativeURL, "origin");
|
|
411
|
+
const getProtocol = getter(NativeURL, "protocol");
|
|
412
|
+
const getPathname = getter(NativeURL, "pathname");
|
|
413
|
+
const getRequestUrl = getter(g.Request, "url");
|
|
414
|
+
let gatewayPrefixes = null;
|
|
415
|
+
let partnerOrigins = [];
|
|
416
|
+
let routesReady = false;
|
|
417
|
+
function copyStrings(value) {
|
|
418
|
+
if (!Array.isArray(value)) return null;
|
|
419
|
+
const out = [];
|
|
420
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
421
|
+
if (typeof value[i] === "string") out[out.length] = value[i];
|
|
422
|
+
}
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
function takeSnapshot(init) {
|
|
426
|
+
if (routesReady) return;
|
|
427
|
+
const raw = init;
|
|
428
|
+
gatewayPrefixes = copyStrings(raw.gatewayPrefixes);
|
|
429
|
+
partnerOrigins = copyStrings(raw.partnerOrigins) || [];
|
|
430
|
+
routesReady = true;
|
|
431
|
+
}
|
|
432
|
+
promiseThen(client.ready, takeSnapshot);
|
|
433
|
+
function matchesPrefix(pathname, prefix) {
|
|
434
|
+
const bare = strSlice(prefix, prefix.length - 1) === "/" ? strSlice(prefix, 0, -1) : prefix;
|
|
435
|
+
return pathname === bare || strSlice(pathname, 0, bare.length + 1) === bare + "/";
|
|
436
|
+
}
|
|
437
|
+
function routeOf(origin, protocol, pathname) {
|
|
438
|
+
if (origin === hostOrigin) {
|
|
439
|
+
if (gatewayPrefixes === null) return "bridge";
|
|
440
|
+
for (let i = 0; i < gatewayPrefixes.length; i += 1) {
|
|
441
|
+
if (matchesPrefix(pathname, gatewayPrefixes[i])) return "bridge";
|
|
442
|
+
}
|
|
443
|
+
return "reject";
|
|
444
|
+
}
|
|
445
|
+
for (let i = 0; i < partnerOrigins.length; i += 1) {
|
|
446
|
+
if (origin === partnerOrigins[i]) return "bridge";
|
|
447
|
+
}
|
|
448
|
+
return protocol === "https:" ? "direct" : "reject";
|
|
449
|
+
}
|
|
450
|
+
function requestUrlOf(input) {
|
|
451
|
+
if (!getRequestUrl || typeof input !== "object" || input === null) return void 0;
|
|
452
|
+
try {
|
|
453
|
+
return getRequestUrl(input);
|
|
454
|
+
} catch {
|
|
455
|
+
return void 0;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function dispatch(input, init) {
|
|
459
|
+
if (typeof NativeURL !== "function" || !getHref || !getOrigin || !getProtocol || !getPathname) {
|
|
460
|
+
return promiseReject(new TypeErr("bstage fetch: URL is unavailable"));
|
|
461
|
+
}
|
|
462
|
+
const requestUrl = requestUrlOf(input);
|
|
463
|
+
const request = requestUrl !== void 0;
|
|
464
|
+
let parsed;
|
|
465
|
+
try {
|
|
466
|
+
parsed = new NativeURL(request ? requestUrl : String(input), hostOrigin);
|
|
467
|
+
} catch {
|
|
468
|
+
return promiseReject(new TypeErr("bstage fetch: invalid url"));
|
|
469
|
+
}
|
|
470
|
+
const href = getHref(parsed);
|
|
471
|
+
const route = routeOf(getOrigin(parsed), getProtocol(parsed), getPathname(parsed));
|
|
472
|
+
const target = request ? input : href;
|
|
473
|
+
if (route === "bridge") return bridgeFetch(target, init);
|
|
474
|
+
if (route === "reject") {
|
|
475
|
+
return promiseReject(new TypeErr("bstage fetch: destination not allowed"));
|
|
476
|
+
}
|
|
477
|
+
if (!nativeFetch) return promiseReject(new TypeErr("bstage fetch: fetch is unavailable"));
|
|
478
|
+
return nativeFetch(
|
|
479
|
+
g,
|
|
480
|
+
target,
|
|
481
|
+
assign({}, init, { credentials: "omit", referrerPolicy: "no-referrer" })
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
function bstageFetch(input, init) {
|
|
485
|
+
try {
|
|
486
|
+
if (routesReady) return dispatch(input, init);
|
|
487
|
+
return promiseThen(client.ready, function(value) {
|
|
488
|
+
takeSnapshot(value);
|
|
489
|
+
return dispatch(input, init);
|
|
490
|
+
});
|
|
491
|
+
} catch {
|
|
492
|
+
return promiseReject(new TypeErr("bstage fetch: request failed"));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
Object.defineProperty(g, "__bstage_fetch__", {
|
|
496
|
+
value: bstageFetch,
|
|
497
|
+
writable: false,
|
|
498
|
+
configurable: false,
|
|
499
|
+
enumerable: false
|
|
500
|
+
});
|
|
501
|
+
return bstageFetch;
|
|
502
|
+
}
|
|
302
503
|
// Annotate the CommonJS export names for ESM import in node:
|
|
303
504
|
0 && (module.exports = {
|
|
304
505
|
createBridgeClient,
|
|
305
|
-
createBridgeFetch
|
|
506
|
+
createBridgeFetch,
|
|
507
|
+
installDirectFetch
|
|
306
508
|
});
|
package/dist/frame.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as BridgeInit, j as BridgeRequestType, i as BridgeRequestMap, e as BridgeEventMap } from './messages-
|
|
1
|
+
import { g as BridgeInit, j as BridgeRequestType, i as BridgeRequestMap, e as BridgeEventMap } from './messages-BjpZyiLy.cjs';
|
|
2
2
|
|
|
3
3
|
/** 프레임 런타임 설정 — srcdoc 부트스트랩이 상수로 넘긴다. */
|
|
4
4
|
interface BridgeClientConfig {
|
|
@@ -28,14 +28,18 @@ interface RuntimeGlobal {
|
|
|
28
28
|
parent: {
|
|
29
29
|
postMessage(message: unknown, targetOrigin: string): void;
|
|
30
30
|
} | null;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
/**
|
|
32
|
+
* `message`(호스트 응답·이벤트)와 `pagehide`(frame-unload 전송, capture)에 쓴다.
|
|
33
|
+
* 처리기 인자 모양은 type마다 다르다 — message는 `{ source, origin, data }`, pagehide는 `{ persisted }`.
|
|
34
|
+
*/
|
|
35
|
+
addEventListener(type: 'message' | 'pagehide', fn: (ev: never) => void, capture?: boolean): void;
|
|
36
36
|
crypto?: {
|
|
37
37
|
getRandomValues<T extends ArrayBufferView | null>(array: T): T;
|
|
38
38
|
};
|
|
39
|
+
/** pagehide 이벤트 생성자 — `persisted` 원본 접근자를 부트스트랩 때 잡는 데 쓴다(없으면 이벤트 속성을 읽는다). */
|
|
40
|
+
PageTransitionEvent?: {
|
|
41
|
+
readonly prototype: object;
|
|
42
|
+
};
|
|
39
43
|
__bstage_bridge__?: BridgeClient;
|
|
40
44
|
}
|
|
41
45
|
/** `createBridgeFetch`가 요구하는 클라이언트 부분. */
|
|
@@ -69,4 +73,45 @@ declare function createBridgeClient(options?: CreateBridgeClientOptions): Bridge
|
|
|
69
73
|
*/
|
|
70
74
|
declare function createBridgeFetch(client: BridgeRequester): typeof globalThis.fetch;
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
/** `installDirectFetch` 설정 — srcdoc 부트스트랩이 상수로 넘긴다. */
|
|
77
|
+
interface DirectFetchConfig {
|
|
78
|
+
/** 호스트 페이지 origin(srcdoc에 심긴 값). 상대 URL의 기준이자 "같은 origin" 판정 기준. */
|
|
79
|
+
readonly hostOrigin: string;
|
|
80
|
+
}
|
|
81
|
+
/** 프레임 fetch 라우터가 쓰는 전역의 최소 모양 — 테스트에서 가짜 창을 넘길 수 있게 한다. */
|
|
82
|
+
interface DirectFetchGlobal {
|
|
83
|
+
fetch?: typeof globalThis.fetch;
|
|
84
|
+
URL?: typeof URL;
|
|
85
|
+
Request?: typeof Request;
|
|
86
|
+
__bstage_fetch__?: typeof globalThis.fetch;
|
|
87
|
+
}
|
|
88
|
+
/** 갈래 판정 결과. */
|
|
89
|
+
type DirectRoute = 'bridge' | 'direct' | 'reject';
|
|
90
|
+
/**
|
|
91
|
+
* 격리 프레임의 fetch 라우터를(`createSrcdoc` 부트스트랩이 항상 설치한다, README "프레임 네트워크")
|
|
92
|
+
* `globalThis.__bstage_fetch__`에 설치한다. inline 모드용으로 쓰인 템플릿이 부르는 그 이름이다.
|
|
93
|
+
*
|
|
94
|
+
* 갈래(`new URL(input, hostOrigin)`으로 정규화한 뒤):
|
|
95
|
+
* - **브리지**(토큰이 필요한 목적지): hostOrigin과 같은 origin이고 경로가 init `gatewayPrefixes` 안, 또는
|
|
96
|
+
* origin이 init `partnerOrigins`와 정확히 일치. 기존 브리지 fetch(`client.fetch`)로 보내고 호스트가 다시 판정한다.
|
|
97
|
+
* - **직접**: 그 밖의 https. 부트스트랩 때 잡아 둔 프레임의 원래 `fetch`로 `credentials: 'omit'`·
|
|
98
|
+
* `referrerPolicy: 'no-referrer'`를 덮어 부른다. 인증 헤더는 붙이지 않는다(토큰은 프레임에 없다).
|
|
99
|
+
* - **거절**(`TypeError`): hostOrigin의 게이트웨이 밖 경로(혼동 방지 — 직접 보내지 않는다), https가 아닌 스킴.
|
|
100
|
+
*
|
|
101
|
+
* **자기완결 함수다** — `bridgeRuntime`과 함께 srcdoc에 `toString()`으로 심긴다(runtime.ts 주석 참고).
|
|
102
|
+
* 템플릿 코드보다 먼저 돌고, 판정·전송에 쓰는 전역(`fetch`·`URL`과 그 접근자·`String.prototype.slice`·
|
|
103
|
+
* `Promise.prototype.then`·`Object.assign`·`TypeError`)과 init의 라우팅 목록을 이때 잡아 둔다. 그래서 템플릿이
|
|
104
|
+
* 나중에 전역·프로토타입·init 객체를 바꿔도 갈래가 바뀌지 않는다. 갈래가 바뀌어도 토큰은 새지 않는다 —
|
|
105
|
+
* 브리지로 간 요청은 호스트가 따로 판정하고, 직접 간 요청에는 토큰이 없다. 이 고정은 혼동 방지다.
|
|
106
|
+
*
|
|
107
|
+
* init이 `gatewayPrefixes`를 싣지 않으면(옛 호스트) hostOrigin 요청은 경로와 무관하게 브리지로 보낸다
|
|
108
|
+
* (호스트가 판정하므로 안전한 쪽).
|
|
109
|
+
*
|
|
110
|
+
* @param g 프레임 전역(`globalThis`)
|
|
111
|
+
* @param client 프레임 브리지 클라이언트(`bridgeRuntime` 결과, frozen)
|
|
112
|
+
* @param config srcdoc에 심긴 hostOrigin
|
|
113
|
+
* @returns 설치한 fetch 라우터
|
|
114
|
+
*/
|
|
115
|
+
declare function installDirectFetch(g: DirectFetchGlobal, client: Pick<BridgeClient, 'fetch' | 'ready'>, config: DirectFetchConfig): typeof globalThis.fetch;
|
|
116
|
+
|
|
117
|
+
export { type BridgeClient, type BridgeClientConfig, type BridgeRequester, type CreateBridgeClientOptions, type DirectFetchConfig, type DirectFetchGlobal, type DirectRoute, type Unsubscribe, createBridgeClient, createBridgeFetch, installDirectFetch };
|
package/dist/frame.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as BridgeInit, j as BridgeRequestType, i as BridgeRequestMap, e as BridgeEventMap } from './messages-
|
|
1
|
+
import { g as BridgeInit, j as BridgeRequestType, i as BridgeRequestMap, e as BridgeEventMap } from './messages-BjpZyiLy.js';
|
|
2
2
|
|
|
3
3
|
/** 프레임 런타임 설정 — srcdoc 부트스트랩이 상수로 넘긴다. */
|
|
4
4
|
interface BridgeClientConfig {
|
|
@@ -28,14 +28,18 @@ interface RuntimeGlobal {
|
|
|
28
28
|
parent: {
|
|
29
29
|
postMessage(message: unknown, targetOrigin: string): void;
|
|
30
30
|
} | null;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
/**
|
|
32
|
+
* `message`(호스트 응답·이벤트)와 `pagehide`(frame-unload 전송, capture)에 쓴다.
|
|
33
|
+
* 처리기 인자 모양은 type마다 다르다 — message는 `{ source, origin, data }`, pagehide는 `{ persisted }`.
|
|
34
|
+
*/
|
|
35
|
+
addEventListener(type: 'message' | 'pagehide', fn: (ev: never) => void, capture?: boolean): void;
|
|
36
36
|
crypto?: {
|
|
37
37
|
getRandomValues<T extends ArrayBufferView | null>(array: T): T;
|
|
38
38
|
};
|
|
39
|
+
/** pagehide 이벤트 생성자 — `persisted` 원본 접근자를 부트스트랩 때 잡는 데 쓴다(없으면 이벤트 속성을 읽는다). */
|
|
40
|
+
PageTransitionEvent?: {
|
|
41
|
+
readonly prototype: object;
|
|
42
|
+
};
|
|
39
43
|
__bstage_bridge__?: BridgeClient;
|
|
40
44
|
}
|
|
41
45
|
/** `createBridgeFetch`가 요구하는 클라이언트 부분. */
|
|
@@ -69,4 +73,45 @@ declare function createBridgeClient(options?: CreateBridgeClientOptions): Bridge
|
|
|
69
73
|
*/
|
|
70
74
|
declare function createBridgeFetch(client: BridgeRequester): typeof globalThis.fetch;
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
/** `installDirectFetch` 설정 — srcdoc 부트스트랩이 상수로 넘긴다. */
|
|
77
|
+
interface DirectFetchConfig {
|
|
78
|
+
/** 호스트 페이지 origin(srcdoc에 심긴 값). 상대 URL의 기준이자 "같은 origin" 판정 기준. */
|
|
79
|
+
readonly hostOrigin: string;
|
|
80
|
+
}
|
|
81
|
+
/** 프레임 fetch 라우터가 쓰는 전역의 최소 모양 — 테스트에서 가짜 창을 넘길 수 있게 한다. */
|
|
82
|
+
interface DirectFetchGlobal {
|
|
83
|
+
fetch?: typeof globalThis.fetch;
|
|
84
|
+
URL?: typeof URL;
|
|
85
|
+
Request?: typeof Request;
|
|
86
|
+
__bstage_fetch__?: typeof globalThis.fetch;
|
|
87
|
+
}
|
|
88
|
+
/** 갈래 판정 결과. */
|
|
89
|
+
type DirectRoute = 'bridge' | 'direct' | 'reject';
|
|
90
|
+
/**
|
|
91
|
+
* 격리 프레임의 fetch 라우터를(`createSrcdoc` 부트스트랩이 항상 설치한다, README "프레임 네트워크")
|
|
92
|
+
* `globalThis.__bstage_fetch__`에 설치한다. inline 모드용으로 쓰인 템플릿이 부르는 그 이름이다.
|
|
93
|
+
*
|
|
94
|
+
* 갈래(`new URL(input, hostOrigin)`으로 정규화한 뒤):
|
|
95
|
+
* - **브리지**(토큰이 필요한 목적지): hostOrigin과 같은 origin이고 경로가 init `gatewayPrefixes` 안, 또는
|
|
96
|
+
* origin이 init `partnerOrigins`와 정확히 일치. 기존 브리지 fetch(`client.fetch`)로 보내고 호스트가 다시 판정한다.
|
|
97
|
+
* - **직접**: 그 밖의 https. 부트스트랩 때 잡아 둔 프레임의 원래 `fetch`로 `credentials: 'omit'`·
|
|
98
|
+
* `referrerPolicy: 'no-referrer'`를 덮어 부른다. 인증 헤더는 붙이지 않는다(토큰은 프레임에 없다).
|
|
99
|
+
* - **거절**(`TypeError`): hostOrigin의 게이트웨이 밖 경로(혼동 방지 — 직접 보내지 않는다), https가 아닌 스킴.
|
|
100
|
+
*
|
|
101
|
+
* **자기완결 함수다** — `bridgeRuntime`과 함께 srcdoc에 `toString()`으로 심긴다(runtime.ts 주석 참고).
|
|
102
|
+
* 템플릿 코드보다 먼저 돌고, 판정·전송에 쓰는 전역(`fetch`·`URL`과 그 접근자·`String.prototype.slice`·
|
|
103
|
+
* `Promise.prototype.then`·`Object.assign`·`TypeError`)과 init의 라우팅 목록을 이때 잡아 둔다. 그래서 템플릿이
|
|
104
|
+
* 나중에 전역·프로토타입·init 객체를 바꿔도 갈래가 바뀌지 않는다. 갈래가 바뀌어도 토큰은 새지 않는다 —
|
|
105
|
+
* 브리지로 간 요청은 호스트가 따로 판정하고, 직접 간 요청에는 토큰이 없다. 이 고정은 혼동 방지다.
|
|
106
|
+
*
|
|
107
|
+
* init이 `gatewayPrefixes`를 싣지 않으면(옛 호스트) hostOrigin 요청은 경로와 무관하게 브리지로 보낸다
|
|
108
|
+
* (호스트가 판정하므로 안전한 쪽).
|
|
109
|
+
*
|
|
110
|
+
* @param g 프레임 전역(`globalThis`)
|
|
111
|
+
* @param client 프레임 브리지 클라이언트(`bridgeRuntime` 결과, frozen)
|
|
112
|
+
* @param config srcdoc에 심긴 hostOrigin
|
|
113
|
+
* @returns 설치한 fetch 라우터
|
|
114
|
+
*/
|
|
115
|
+
declare function installDirectFetch(g: DirectFetchGlobal, client: Pick<BridgeClient, 'fetch' | 'ready'>, config: DirectFetchConfig): typeof globalThis.fetch;
|
|
116
|
+
|
|
117
|
+
export { type BridgeClient, type BridgeClientConfig, type BridgeRequester, type CreateBridgeClientOptions, type DirectFetchConfig, type DirectFetchGlobal, type DirectRoute, type Unsubscribe, createBridgeClient, createBridgeFetch, installDirectFetch };
|
package/dist/frame.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bridgeRuntime,
|
|
3
|
-
createBridgeFetch
|
|
4
|
-
|
|
3
|
+
createBridgeFetch,
|
|
4
|
+
installDirectFetch
|
|
5
|
+
} from "./chunk-CSAW3L7P.js";
|
|
5
6
|
|
|
6
7
|
// src/frame/createBridgeClient.ts
|
|
7
8
|
var BRIDGE_CONFIG_GLOBAL = "__bstage_bridge_config__";
|
|
@@ -17,5 +18,6 @@ function createBridgeClient(options = {}) {
|
|
|
17
18
|
}
|
|
18
19
|
export {
|
|
19
20
|
createBridgeClient,
|
|
20
|
-
createBridgeFetch
|
|
21
|
+
createBridgeFetch,
|
|
22
|
+
installDirectFetch
|
|
21
23
|
};
|