@bstage-sdk/bridge 0.10.0 → 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 +53 -4
- package/dist/{chunk-N3JID7KQ.js → chunk-CSAW3L7P.js} +122 -1
- package/dist/{chunk-JH3A3Z2A.js → chunk-ND2HFYBP.js} +7 -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 +124 -2
- package/dist/frame.d.cts +43 -2
- package/dist/frame.d.ts +43 -2
- package/dist/frame.js +5 -3
- package/dist/host.cjs +125 -1
- package/dist/host.d.cts +3 -3
- package/dist/host.d.ts +3 -3
- package/dist/host.js +3 -2
- package/dist/index.cjs +124 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{messages-CRGnyf-6.d.cts → messages-BjpZyiLy.d.cts} +6 -0
- package/dist/{messages-CRGnyf-6.d.ts → messages-BjpZyiLy.d.ts} +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -219,7 +219,7 @@ external에 인증을 붙이는 fetch를 주입하지 않는다. 외부 갈래
|
|
|
219
219
|
|
|
220
220
|
## 프레임 문서
|
|
221
221
|
|
|
222
|
-
`createSrcdoc`가 만드는 문서는 CSP로
|
|
222
|
+
`createSrcdoc`가 만드는 문서는 CSP로 프레임이 불러올 수 있는 것을 좁힌다. 스크립트는 nonce가 붙은
|
|
223
223
|
부트스트랩과 `assetOrigin`의 번들만 실행된다. 부트스트랩은 브리지 클라이언트를 설치하고 init을 받은 뒤
|
|
224
224
|
엔트리를 로드해 custom element를 붙이고, 문서 높이를 `resize`로 보고한다.
|
|
225
225
|
|
|
@@ -230,11 +230,12 @@ style-src 'unsafe-inline' {assetOrigin}
|
|
|
230
230
|
img-src {assetOrigin} {mediaOrigins…} data: blob:
|
|
231
231
|
font-src {assetOrigin} data:
|
|
232
232
|
media-src {assetOrigin} {mediaOrigins…} blob:
|
|
233
|
-
connect-src
|
|
233
|
+
connect-src https: wss:; frame-src 'none'; form-action 'none'; base-uri 'none'
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
-
- `
|
|
237
|
-
`
|
|
236
|
+
- `connect-src`는 항상 `https: wss:`다(아래 "프레임 네트워크"). 평문 `http:`·`ws:`는 열지 않는다. 토큰이 필요한 API는
|
|
237
|
+
부트스트랩의 `__bstage_fetch__`가 브리지로 보내고, 그 밖의 https API는 템플릿이 프레임 안에서 직접 부른다.
|
|
238
|
+
브리지 fetch(`__bstage_bridge__.fetch`)의 external 갈래(`allowExternal: true`)도 호환을 위해 그대로 남는다.
|
|
238
239
|
- img-src·media-src에 맨 `https:`는 두지 않는다 — 두면 `<img src="https://임의-origin/?d=…">`가 자유로운 GET
|
|
239
240
|
유출 통로가 된다. 외부 이미지·미디어가 필요하면 `mediaOrigins`에 **정확한 https origin**만 넘긴다
|
|
240
241
|
(경로·쿼리·userinfo·와일드카드·대문자 호스트 불가, 최대 20개, 하나라도 틀리면 `TypeError`). font-src는 넓히지 않는다.
|
|
@@ -243,6 +244,54 @@ connect-src 'none'; frame-src 'none'; form-action 'none'; base-uri 'none'
|
|
|
243
244
|
절대 들어가지 않는다**는 것이다 — 인증은 호스트의 `fetchImpl`이 붙이고, 토큰을 응답으로 돌려주는 인증 경로는
|
|
244
245
|
게이트웨이 거절 목록(`gatewayDenyPrefixes`·`gatewayDenySegments`)으로 막는다. 프레임에 넘기는 init·이벤트·응답에 민감 정보를 싣지 않는다.
|
|
245
246
|
|
|
247
|
+
### 프레임 네트워크 (항상 적용)
|
|
248
|
+
|
|
249
|
+
결정(2026-10-22): 격리 프레임에서 **토큰이 필요한 정해진 API만 브리지**로 보내고(호스트가 팬 토큰을 붙인다),
|
|
250
|
+
**그 밖의 API는 템플릿이 프레임 안에서 직접** 부른다. 호스트 옵션은 없다 — `createSrcdoc`로 만든 모든 프레임(포털 미리보기 포함)이
|
|
251
|
+
이 규칙으로 돈다.
|
|
252
|
+
|
|
253
|
+
동작:
|
|
254
|
+
|
|
255
|
+
- CSP `connect-src https: wss:`. `http:`·`ws:`는 열지 않는다. `img-src`·`media-src`·`font-src`는 위처럼 좁게 두고
|
|
256
|
+
`frame-src 'none'`·`form-action 'none'`·`base-uri 'none'`도 그대로다.
|
|
257
|
+
- 부트스트랩이 템플릿 코드보다 먼저 `globalThis.__bstage_fetch__`(쓰기 불가)를 설치한다. inline 모드용으로 쓰인 템플릿이
|
|
258
|
+
부르는 그 이름이다. `new URL(input, hostOrigin)`으로 정규화한 뒤 갈래를 나눈다.
|
|
259
|
+
|
|
260
|
+
| 목적지 | 갈래 |
|
|
261
|
+
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
262
|
+
| hostOrigin + 경로가 init `gatewayPrefixes` 안(web-base는 `['/gw/', '/svc/']`) | 브리지 fetch(기존 프로토콜, 호스트가 다시 판정하고 토큰을 붙인다) |
|
|
263
|
+
| origin이 init `partnerOrigins`와 정확히 일치 | 브리지 fetch(`partnerFetchImpl`) |
|
|
264
|
+
| 그 밖의 https | 프레임의 원래 `fetch`(부트스트랩 때 잡은 참조), `credentials: 'omit'`·`referrerPolicy: 'no-referrer'` |
|
|
265
|
+
| hostOrigin의 게이트웨이 밖 경로, https가 아닌 스킴(http·data·blob 등) | `TypeError`(혼동을 막으려고 직접 보내지 않는다) |
|
|
266
|
+
|
|
267
|
+
- init에 `gatewayPrefixes`(호스트 `policy.gatewayPrefixes`)가 실린다. 판정의 정본은 여전히 호스트 정책이고, 프레임 라우터는
|
|
268
|
+
보낼 길만 고른다. 필드가 없는 옛 호스트면 hostOrigin 요청은 경로와 무관하게 브리지로 보낸다.
|
|
269
|
+
- 라우터는 판정·전송에 쓰는 전역(`fetch`·`URL`과 그 접근자·`String.prototype.slice`·`Promise.prototype.then`·`Object.assign`)과
|
|
270
|
+
init의 라우팅 목록 사본을 부트스트랩 때 잡아 둔다. 템플릿이 나중에 전역·프로토타입·`getInit()` 객체를 바꿔도 갈래는 바뀌지 않는다(테스트로 고정).
|
|
271
|
+
갈래가 바뀌더라도 토큰은 새지 않는다 — 브리지로 간 요청은 호스트가 따로 판정하고, 직접 간 요청에는 토큰이 없다.
|
|
272
|
+
- 직접 호출에 인증 헤더를 **붙이지 않는다.** 템플릿이 스스로 넣은 헤더는 그대로 간다.
|
|
273
|
+
- `__bstage_bridge__.fetch`(브리지 fetch)와 호스트의 external 갈래(`allowExternal`)는 그대로 동작한다(호환).
|
|
274
|
+
- core `BstageClient`는 프레임 안에서 계속 브리지 fetch를 쓴다 — 기본 목적지가 게이트웨이(`gatewayBase`)라 토큰이 필요한 쪽이다.
|
|
275
|
+
`baseUrl`을 외부로 바꾼 `BstageClient`는 브리지의 external 갈래로 중계되므로 호스트가 `allowExternal: true`여야 한다.
|
|
276
|
+
외부를 직접 부르려면 `__bstage_fetch__`나 `fetch`를 쓴다(`config.fetch: globalThis.__bstage_fetch__`도 된다).
|
|
277
|
+
|
|
278
|
+
**보안상 맞바꾼 것(정직하게)** — 아래는 이 결정으로 열린 것이다. 끄는 스위치는 없으므로 프레임을 띄우는 모든 호스트가 아래 조건을 갖춘다.
|
|
279
|
+
|
|
280
|
+
- **직접 호출에는 브리지 정책이 적용되지 않는다.** 사설 IP·IP 리터럴·`localhost`·내부 이름 거절, 분당·동시 요청 상한,
|
|
281
|
+
요청·응답 본문 상한이 없다. 프레임이 가진 데이터는 임의 https로 나갈 수 있고(이전에도 `allowExternal`·자기 내비게이션으로
|
|
282
|
+
가능했다), 사설망 접근은 브라우저의 Local Network Access에 맡긴다.
|
|
283
|
+
- **요청은 `Origin: null`로 나간다**(sandbox opaque origin). 직접 부를 API는 CORS에서 `*` 또는 `null`을 허용해야 응답을 읽을 수 있다.
|
|
284
|
+
`null`을 credentials와 함께 허용하는 CORS는 다른 sandbox 문서에도 열리는 것이므로 API 쪽에서 피한다.
|
|
285
|
+
- **토큰은 여전히 프레임에 들어가지 않는다.** 팬 토큰·쿠키는 호스트 `fetchImpl`·`partnerFetchImpl`이 브리지 요청에만 붙인다.
|
|
286
|
+
- **같은 origin 게이트웨이·`/svc`는 브리지로만** 간다 — 단 이것은 `__bstage_fetch__` 라우터의 보장이다. 전역 `fetch`·`XMLHttpRequest`·
|
|
287
|
+
`navigator.sendBeacon`·`EventSource`·`WebSocket`은 그대로 남고 CSP `https:`는 hostOrigin도 포함하므로, 템플릿은 hostOrigin에
|
|
288
|
+
직접(`credentials: 'include'`, `mode: 'no-cors'`, `sendBeacon`은 항상 자격증명 포함) 요청을 보낼 수 있다. 이 요청은 브리지의
|
|
289
|
+
거절 규칙(`gatewayDenyPrefixes`·`gatewayDenySegments`)·상한을 거치지 않는다. opaque origin에서 나가 cross-site로 취급되므로
|
|
290
|
+
**`SameSite=None` 쿠키만** 실리고, 응답은 호스트 CORS가 `null`을 허용하지 않으면 읽히지 않는다(부작용은 일어날 수 있다).
|
|
291
|
+
그래서 프레임을 띄우는 호스트는 ① 호스트 origin 쿠키를 `SameSite=Lax`/`Strict`로 두고(`None` 쿠키는 이 직접 요청에 실린다 — 권장 후속 조치)
|
|
292
|
+
② CORS에 `null` origin을 허용하지 않으며 ③ 쿠키로 인증하는 상태 변경 엔드포인트에 CSRF 방어를 유지한다.
|
|
293
|
+
- 토큰을 응답 본문으로 돌려주는 경로(토큰 갱신 등)는 브리지의 게이트웨이 거절 규칙으로 계속 막힌다 — 직접 호출로는 토큰이 없으니 닿아도 얻을 것이 없다.
|
|
294
|
+
|
|
246
295
|
프레임 안 제약: 쿠키·`localStorage`·호스트 DOM에 접근할 수 없다(opaque origin). 번역 사전은 CDN이 아니라
|
|
247
296
|
호스트가 init·`locale` 이벤트의 `translations`로 넘긴다. 브리지 모드의 `fetchTranslations`는 **현재 locale 사전만**
|
|
248
297
|
돌려주고, 다른 locale(폴백 체인의 `en`·`ko` 등)은 빈 사전 `{}`이다. 같은 locale에 사전만 새로 와도 `useBstageTranslations`가 다시 읽는다.
|
|
@@ -90,6 +90,126 @@ function createBridgeFetch(client) {
|
|
|
90
90
|
return bridgeFetch;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// src/frame/directFetch.ts
|
|
94
|
+
function installDirectFetch(g, client, config) {
|
|
95
|
+
const hostOrigin = config.hostOrigin;
|
|
96
|
+
const callFn = Function.prototype.call;
|
|
97
|
+
const NativeURL = g.URL;
|
|
98
|
+
const rawFetch = g.fetch;
|
|
99
|
+
const nativeFetch = typeof rawFetch === "function" ? callFn.bind(rawFetch) : void 0;
|
|
100
|
+
const bridgeFetch = client.fetch;
|
|
101
|
+
const strSlice = callFn.bind(String.prototype.slice);
|
|
102
|
+
const promiseThen = callFn.bind(Promise.prototype.then);
|
|
103
|
+
const promiseReject = Promise.reject.bind(Promise);
|
|
104
|
+
const assign = Object.assign;
|
|
105
|
+
const TypeErr = TypeError;
|
|
106
|
+
function getter(ctor, name) {
|
|
107
|
+
if (typeof ctor !== "function") return void 0;
|
|
108
|
+
let proto = ctor.prototype;
|
|
109
|
+
while (proto) {
|
|
110
|
+
const desc = Object.getOwnPropertyDescriptor(proto, name);
|
|
111
|
+
if (desc && typeof desc.get === "function") {
|
|
112
|
+
return callFn.bind(desc.get);
|
|
113
|
+
}
|
|
114
|
+
proto = Object.getPrototypeOf(proto);
|
|
115
|
+
}
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
const getHref = getter(NativeURL, "href");
|
|
119
|
+
const getOrigin = getter(NativeURL, "origin");
|
|
120
|
+
const getProtocol = getter(NativeURL, "protocol");
|
|
121
|
+
const getPathname = getter(NativeURL, "pathname");
|
|
122
|
+
const getRequestUrl = getter(g.Request, "url");
|
|
123
|
+
let gatewayPrefixes = null;
|
|
124
|
+
let partnerOrigins = [];
|
|
125
|
+
let routesReady = false;
|
|
126
|
+
function copyStrings(value) {
|
|
127
|
+
if (!Array.isArray(value)) return null;
|
|
128
|
+
const out = [];
|
|
129
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
130
|
+
if (typeof value[i] === "string") out[out.length] = value[i];
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
function takeSnapshot(init) {
|
|
135
|
+
if (routesReady) return;
|
|
136
|
+
const raw = init;
|
|
137
|
+
gatewayPrefixes = copyStrings(raw.gatewayPrefixes);
|
|
138
|
+
partnerOrigins = copyStrings(raw.partnerOrigins) || [];
|
|
139
|
+
routesReady = true;
|
|
140
|
+
}
|
|
141
|
+
promiseThen(client.ready, takeSnapshot);
|
|
142
|
+
function matchesPrefix(pathname, prefix) {
|
|
143
|
+
const bare = strSlice(prefix, prefix.length - 1) === "/" ? strSlice(prefix, 0, -1) : prefix;
|
|
144
|
+
return pathname === bare || strSlice(pathname, 0, bare.length + 1) === bare + "/";
|
|
145
|
+
}
|
|
146
|
+
function routeOf(origin, protocol, pathname) {
|
|
147
|
+
if (origin === hostOrigin) {
|
|
148
|
+
if (gatewayPrefixes === null) return "bridge";
|
|
149
|
+
for (let i = 0; i < gatewayPrefixes.length; i += 1) {
|
|
150
|
+
if (matchesPrefix(pathname, gatewayPrefixes[i])) return "bridge";
|
|
151
|
+
}
|
|
152
|
+
return "reject";
|
|
153
|
+
}
|
|
154
|
+
for (let i = 0; i < partnerOrigins.length; i += 1) {
|
|
155
|
+
if (origin === partnerOrigins[i]) return "bridge";
|
|
156
|
+
}
|
|
157
|
+
return protocol === "https:" ? "direct" : "reject";
|
|
158
|
+
}
|
|
159
|
+
function requestUrlOf(input) {
|
|
160
|
+
if (!getRequestUrl || typeof input !== "object" || input === null) return void 0;
|
|
161
|
+
try {
|
|
162
|
+
return getRequestUrl(input);
|
|
163
|
+
} catch {
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function dispatch(input, init) {
|
|
168
|
+
if (typeof NativeURL !== "function" || !getHref || !getOrigin || !getProtocol || !getPathname) {
|
|
169
|
+
return promiseReject(new TypeErr("bstage fetch: URL is unavailable"));
|
|
170
|
+
}
|
|
171
|
+
const requestUrl = requestUrlOf(input);
|
|
172
|
+
const request = requestUrl !== void 0;
|
|
173
|
+
let parsed;
|
|
174
|
+
try {
|
|
175
|
+
parsed = new NativeURL(request ? requestUrl : String(input), hostOrigin);
|
|
176
|
+
} catch {
|
|
177
|
+
return promiseReject(new TypeErr("bstage fetch: invalid url"));
|
|
178
|
+
}
|
|
179
|
+
const href = getHref(parsed);
|
|
180
|
+
const route = routeOf(getOrigin(parsed), getProtocol(parsed), getPathname(parsed));
|
|
181
|
+
const target = request ? input : href;
|
|
182
|
+
if (route === "bridge") return bridgeFetch(target, init);
|
|
183
|
+
if (route === "reject") {
|
|
184
|
+
return promiseReject(new TypeErr("bstage fetch: destination not allowed"));
|
|
185
|
+
}
|
|
186
|
+
if (!nativeFetch) return promiseReject(new TypeErr("bstage fetch: fetch is unavailable"));
|
|
187
|
+
return nativeFetch(
|
|
188
|
+
g,
|
|
189
|
+
target,
|
|
190
|
+
assign({}, init, { credentials: "omit", referrerPolicy: "no-referrer" })
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function bstageFetch(input, init) {
|
|
194
|
+
try {
|
|
195
|
+
if (routesReady) return dispatch(input, init);
|
|
196
|
+
return promiseThen(client.ready, function(value) {
|
|
197
|
+
takeSnapshot(value);
|
|
198
|
+
return dispatch(input, init);
|
|
199
|
+
});
|
|
200
|
+
} catch {
|
|
201
|
+
return promiseReject(new TypeErr("bstage fetch: request failed"));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
Object.defineProperty(g, "__bstage_fetch__", {
|
|
205
|
+
value: bstageFetch,
|
|
206
|
+
writable: false,
|
|
207
|
+
configurable: false,
|
|
208
|
+
enumerable: false
|
|
209
|
+
});
|
|
210
|
+
return bstageFetch;
|
|
211
|
+
}
|
|
212
|
+
|
|
93
213
|
// src/frame/runtime.ts
|
|
94
214
|
function bridgeRuntime(g, makeFetch, config) {
|
|
95
215
|
const NS = "bstage-bridge";
|
|
@@ -342,5 +462,6 @@ function bridgeRuntime(g, makeFetch, config) {
|
|
|
342
462
|
|
|
343
463
|
export {
|
|
344
464
|
bridgeRuntime,
|
|
345
|
-
createBridgeFetch
|
|
465
|
+
createBridgeFetch,
|
|
466
|
+
installDirectFetch
|
|
346
467
|
};
|
|
@@ -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/protocol/envelope.ts
|
|
7
8
|
var BRIDGE_VERSION = 1;
|
|
@@ -449,7 +450,7 @@ function buildFrameCsp(nonce, assetOrigin, mediaOrigins = []) {
|
|
|
449
450
|
`img-src ${media} data: blob:`,
|
|
450
451
|
`font-src ${assetOrigin} data:`,
|
|
451
452
|
`media-src ${media} blob:`,
|
|
452
|
-
"connect-src
|
|
453
|
+
"connect-src https: wss:",
|
|
453
454
|
"frame-src 'none'",
|
|
454
455
|
"form-action 'none'",
|
|
455
456
|
"base-uri 'none'"
|
|
@@ -466,6 +467,9 @@ function bootstrapScript(o) {
|
|
|
466
467
|
`var bridgeRuntime = ${bridgeRuntime.toString()};`,
|
|
467
468
|
`var createBridgeFetch = ${createBridgeFetch.toString()};`,
|
|
468
469
|
"var client = bridgeRuntime(globalThis, createBridgeFetch, { session: SESSION, hostOrigin: HOST_ORIGIN });",
|
|
470
|
+
// 라우터는 항상 심는다. ready.then이 엔트리 로드(아래 client.ready.then)보다 먼저 등록돼야 템플릿보다 먼저 스냅샷을 잡는다.
|
|
471
|
+
`var installDirectFetch = ${installDirectFetch.toString()};`,
|
|
472
|
+
"installDirectFetch(globalThis, client, { hostOrigin: HOST_ORIGIN });",
|
|
469
473
|
"var lastHeight = -1;",
|
|
470
474
|
"var scheduled = false;",
|
|
471
475
|
"function reportHeight() {",
|
|
@@ -33,6 +33,9 @@ declare const MAX_ELEMENT_NAME_LENGTH = 100;
|
|
|
33
33
|
/**
|
|
34
34
|
* 브리지 프로토콜 CSP. 값은 이스케이프 전 원문(호출자가 속성에 넣을 때 이스케이프).
|
|
35
35
|
*
|
|
36
|
+
* connect-src는 항상 `https: wss:`다(프레임 네트워크 규칙, README "프레임 네트워크") — 토큰이 필요한 API는
|
|
37
|
+
* 부트스트랩의 `__bstage_fetch__`가 브리지로 보내고, 그 밖의 https API는 템플릿이 직접 부른다. 평문(`http:`·`ws:`)은 열지 않는다.
|
|
38
|
+
*
|
|
36
39
|
* img-src·media-src에 맨 `https:`를 두지 않는다 — 두면 템플릿이 `<img src="https://attacker/?d=...">`로
|
|
37
40
|
* 임의 origin에 GET을 보낼 수 있다. 외부 이미지·미디어는 `mediaOrigins`로 정확한 origin만 연다.
|
|
38
41
|
*
|
|
@@ -33,6 +33,9 @@ declare const MAX_ELEMENT_NAME_LENGTH = 100;
|
|
|
33
33
|
/**
|
|
34
34
|
* 브리지 프로토콜 CSP. 값은 이스케이프 전 원문(호출자가 속성에 넣을 때 이스케이프).
|
|
35
35
|
*
|
|
36
|
+
* connect-src는 항상 `https: wss:`다(프레임 네트워크 규칙, README "프레임 네트워크") — 토큰이 필요한 API는
|
|
37
|
+
* 부트스트랩의 `__bstage_fetch__`가 브리지로 보내고, 그 밖의 https API는 템플릿이 직접 부른다. 평문(`http:`·`ws:`)은 열지 않는다.
|
|
38
|
+
*
|
|
36
39
|
* img-src·media-src에 맨 `https:`를 두지 않는다 — 두면 템플릿이 `<img src="https://attacker/?d=...">`로
|
|
37
40
|
* 임의 origin에 GET을 보낼 수 있다. 외부 이미지·미디어는 `mediaOrigins`로 정확한 origin만 연다.
|
|
38
41
|
*
|
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
|
|
|
@@ -379,8 +380,129 @@ function createBridgeClient(options = {}) {
|
|
|
379
380
|
}
|
|
380
381
|
return bridgeRuntime(g, createBridgeFetch, { session, hostOrigin });
|
|
381
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
|
+
}
|
|
382
503
|
// Annotate the CommonJS export names for ESM import in node:
|
|
383
504
|
0 && (module.exports = {
|
|
384
505
|
createBridgeClient,
|
|
385
|
-
createBridgeFetch
|
|
506
|
+
createBridgeFetch,
|
|
507
|
+
installDirectFetch
|
|
386
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 {
|
|
@@ -73,4 +73,45 @@ declare function createBridgeClient(options?: CreateBridgeClientOptions): Bridge
|
|
|
73
73
|
*/
|
|
74
74
|
declare function createBridgeFetch(client: BridgeRequester): typeof globalThis.fetch;
|
|
75
75
|
|
|
76
|
-
|
|
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 {
|
|
@@ -73,4 +73,45 @@ declare function createBridgeClient(options?: CreateBridgeClientOptions): Bridge
|
|
|
73
73
|
*/
|
|
74
74
|
declare function createBridgeFetch(client: BridgeRequester): typeof globalThis.fetch;
|
|
75
75
|
|
|
76
|
-
|
|
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
|
};
|
package/dist/host.cjs
CHANGED
|
@@ -506,6 +506,7 @@ function buildInit(init, hostOrigin, partnerOrigins, policy) {
|
|
|
506
506
|
spaceId: String(init.spaceId),
|
|
507
507
|
hostOrigin,
|
|
508
508
|
gatewayBase: `${hostOrigin}/gw`,
|
|
509
|
+
gatewayPrefixes: policy.gatewayPrefixes,
|
|
509
510
|
locale: String(init.locale),
|
|
510
511
|
theme: sanitizeTheme(init.theme),
|
|
511
512
|
...slot ? { slot } : {},
|
|
@@ -1338,6 +1339,126 @@ function createBridgeFetch(client) {
|
|
|
1338
1339
|
return bridgeFetch;
|
|
1339
1340
|
}
|
|
1340
1341
|
|
|
1342
|
+
// src/frame/directFetch.ts
|
|
1343
|
+
function installDirectFetch(g, client, config) {
|
|
1344
|
+
const hostOrigin = config.hostOrigin;
|
|
1345
|
+
const callFn = Function.prototype.call;
|
|
1346
|
+
const NativeURL = g.URL;
|
|
1347
|
+
const rawFetch = g.fetch;
|
|
1348
|
+
const nativeFetch = typeof rawFetch === "function" ? callFn.bind(rawFetch) : void 0;
|
|
1349
|
+
const bridgeFetch = client.fetch;
|
|
1350
|
+
const strSlice = callFn.bind(String.prototype.slice);
|
|
1351
|
+
const promiseThen = callFn.bind(Promise.prototype.then);
|
|
1352
|
+
const promiseReject = Promise.reject.bind(Promise);
|
|
1353
|
+
const assign = Object.assign;
|
|
1354
|
+
const TypeErr = TypeError;
|
|
1355
|
+
function getter(ctor, name) {
|
|
1356
|
+
if (typeof ctor !== "function") return void 0;
|
|
1357
|
+
let proto = ctor.prototype;
|
|
1358
|
+
while (proto) {
|
|
1359
|
+
const desc = Object.getOwnPropertyDescriptor(proto, name);
|
|
1360
|
+
if (desc && typeof desc.get === "function") {
|
|
1361
|
+
return callFn.bind(desc.get);
|
|
1362
|
+
}
|
|
1363
|
+
proto = Object.getPrototypeOf(proto);
|
|
1364
|
+
}
|
|
1365
|
+
return void 0;
|
|
1366
|
+
}
|
|
1367
|
+
const getHref = getter(NativeURL, "href");
|
|
1368
|
+
const getOrigin = getter(NativeURL, "origin");
|
|
1369
|
+
const getProtocol = getter(NativeURL, "protocol");
|
|
1370
|
+
const getPathname = getter(NativeURL, "pathname");
|
|
1371
|
+
const getRequestUrl = getter(g.Request, "url");
|
|
1372
|
+
let gatewayPrefixes = null;
|
|
1373
|
+
let partnerOrigins = [];
|
|
1374
|
+
let routesReady = false;
|
|
1375
|
+
function copyStrings(value) {
|
|
1376
|
+
if (!Array.isArray(value)) return null;
|
|
1377
|
+
const out = [];
|
|
1378
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1379
|
+
if (typeof value[i] === "string") out[out.length] = value[i];
|
|
1380
|
+
}
|
|
1381
|
+
return out;
|
|
1382
|
+
}
|
|
1383
|
+
function takeSnapshot(init) {
|
|
1384
|
+
if (routesReady) return;
|
|
1385
|
+
const raw = init;
|
|
1386
|
+
gatewayPrefixes = copyStrings(raw.gatewayPrefixes);
|
|
1387
|
+
partnerOrigins = copyStrings(raw.partnerOrigins) || [];
|
|
1388
|
+
routesReady = true;
|
|
1389
|
+
}
|
|
1390
|
+
promiseThen(client.ready, takeSnapshot);
|
|
1391
|
+
function matchesPrefix2(pathname, prefix) {
|
|
1392
|
+
const bare = strSlice(prefix, prefix.length - 1) === "/" ? strSlice(prefix, 0, -1) : prefix;
|
|
1393
|
+
return pathname === bare || strSlice(pathname, 0, bare.length + 1) === bare + "/";
|
|
1394
|
+
}
|
|
1395
|
+
function routeOf(origin, protocol, pathname) {
|
|
1396
|
+
if (origin === hostOrigin) {
|
|
1397
|
+
if (gatewayPrefixes === null) return "bridge";
|
|
1398
|
+
for (let i = 0; i < gatewayPrefixes.length; i += 1) {
|
|
1399
|
+
if (matchesPrefix2(pathname, gatewayPrefixes[i])) return "bridge";
|
|
1400
|
+
}
|
|
1401
|
+
return "reject";
|
|
1402
|
+
}
|
|
1403
|
+
for (let i = 0; i < partnerOrigins.length; i += 1) {
|
|
1404
|
+
if (origin === partnerOrigins[i]) return "bridge";
|
|
1405
|
+
}
|
|
1406
|
+
return protocol === "https:" ? "direct" : "reject";
|
|
1407
|
+
}
|
|
1408
|
+
function requestUrlOf(input) {
|
|
1409
|
+
if (!getRequestUrl || typeof input !== "object" || input === null) return void 0;
|
|
1410
|
+
try {
|
|
1411
|
+
return getRequestUrl(input);
|
|
1412
|
+
} catch {
|
|
1413
|
+
return void 0;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
function dispatch2(input, init) {
|
|
1417
|
+
if (typeof NativeURL !== "function" || !getHref || !getOrigin || !getProtocol || !getPathname) {
|
|
1418
|
+
return promiseReject(new TypeErr("bstage fetch: URL is unavailable"));
|
|
1419
|
+
}
|
|
1420
|
+
const requestUrl = requestUrlOf(input);
|
|
1421
|
+
const request = requestUrl !== void 0;
|
|
1422
|
+
let parsed;
|
|
1423
|
+
try {
|
|
1424
|
+
parsed = new NativeURL(request ? requestUrl : String(input), hostOrigin);
|
|
1425
|
+
} catch {
|
|
1426
|
+
return promiseReject(new TypeErr("bstage fetch: invalid url"));
|
|
1427
|
+
}
|
|
1428
|
+
const href = getHref(parsed);
|
|
1429
|
+
const route = routeOf(getOrigin(parsed), getProtocol(parsed), getPathname(parsed));
|
|
1430
|
+
const target = request ? input : href;
|
|
1431
|
+
if (route === "bridge") return bridgeFetch(target, init);
|
|
1432
|
+
if (route === "reject") {
|
|
1433
|
+
return promiseReject(new TypeErr("bstage fetch: destination not allowed"));
|
|
1434
|
+
}
|
|
1435
|
+
if (!nativeFetch) return promiseReject(new TypeErr("bstage fetch: fetch is unavailable"));
|
|
1436
|
+
return nativeFetch(
|
|
1437
|
+
g,
|
|
1438
|
+
target,
|
|
1439
|
+
assign({}, init, { credentials: "omit", referrerPolicy: "no-referrer" })
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
function bstageFetch(input, init) {
|
|
1443
|
+
try {
|
|
1444
|
+
if (routesReady) return dispatch2(input, init);
|
|
1445
|
+
return promiseThen(client.ready, function(value) {
|
|
1446
|
+
takeSnapshot(value);
|
|
1447
|
+
return dispatch2(input, init);
|
|
1448
|
+
});
|
|
1449
|
+
} catch {
|
|
1450
|
+
return promiseReject(new TypeErr("bstage fetch: request failed"));
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
Object.defineProperty(g, "__bstage_fetch__", {
|
|
1454
|
+
value: bstageFetch,
|
|
1455
|
+
writable: false,
|
|
1456
|
+
configurable: false,
|
|
1457
|
+
enumerable: false
|
|
1458
|
+
});
|
|
1459
|
+
return bstageFetch;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1341
1462
|
// src/srcdoc/escape.ts
|
|
1342
1463
|
var HTML_ESCAPES = {
|
|
1343
1464
|
"&": "&",
|
|
@@ -1403,7 +1524,7 @@ function buildFrameCsp(nonce, assetOrigin, mediaOrigins = []) {
|
|
|
1403
1524
|
`img-src ${media} data: blob:`,
|
|
1404
1525
|
`font-src ${assetOrigin} data:`,
|
|
1405
1526
|
`media-src ${media} blob:`,
|
|
1406
|
-
"connect-src
|
|
1527
|
+
"connect-src https: wss:",
|
|
1407
1528
|
"frame-src 'none'",
|
|
1408
1529
|
"form-action 'none'",
|
|
1409
1530
|
"base-uri 'none'"
|
|
@@ -1420,6 +1541,9 @@ function bootstrapScript(o) {
|
|
|
1420
1541
|
`var bridgeRuntime = ${bridgeRuntime.toString()};`,
|
|
1421
1542
|
`var createBridgeFetch = ${createBridgeFetch.toString()};`,
|
|
1422
1543
|
"var client = bridgeRuntime(globalThis, createBridgeFetch, { session: SESSION, hostOrigin: HOST_ORIGIN });",
|
|
1544
|
+
// 라우터는 항상 심는다. ready.then이 엔트리 로드(아래 client.ready.then)보다 먼저 등록돼야 템플릿보다 먼저 스냅샷을 잡는다.
|
|
1545
|
+
`var installDirectFetch = ${installDirectFetch.toString()};`,
|
|
1546
|
+
"installDirectFetch(globalThis, client, { hostOrigin: HOST_ORIGIN });",
|
|
1423
1547
|
"var lastHeight = -1;",
|
|
1424
1548
|
"var scheduled = false;",
|
|
1425
1549
|
"function reportHeight() {",
|
package/dist/host.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { f as BridgeHostEventType, e as BridgeEventMap, t as ToastKind, L as LogLevel, l as BridgeSurface, T as ThemeState, S as SlotState, M as MemberState, m as BridgeTranslations } from './messages-
|
|
2
|
-
export { a as BRIDGE_SURFACES, I as IFRAME_ATTRIBUTES, w as isBridgeSurface } from './messages-
|
|
3
|
-
export { C as CreateSrcdocOptions, c as createNonce, d as createSession, e as createSrcdoc, f as isValidSession } from './createSrcdoc-
|
|
1
|
+
import { f as BridgeHostEventType, e as BridgeEventMap, t as ToastKind, L as LogLevel, l as BridgeSurface, T as ThemeState, S as SlotState, M as MemberState, m as BridgeTranslations } from './messages-BjpZyiLy.cjs';
|
|
2
|
+
export { a as BRIDGE_SURFACES, I as IFRAME_ATTRIBUTES, w as isBridgeSurface } from './messages-BjpZyiLy.cjs';
|
|
3
|
+
export { C as CreateSrcdocOptions, c as createNonce, d as createSession, e as createSrcdoc, f as isValidSession } from './createSrcdoc-DgQyvIjG.cjs';
|
|
4
4
|
|
|
5
5
|
/** 호스트 fetch 정책. */
|
|
6
6
|
interface BridgePolicy {
|
package/dist/host.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { f as BridgeHostEventType, e as BridgeEventMap, t as ToastKind, L as LogLevel, l as BridgeSurface, T as ThemeState, S as SlotState, M as MemberState, m as BridgeTranslations } from './messages-
|
|
2
|
-
export { a as BRIDGE_SURFACES, I as IFRAME_ATTRIBUTES, w as isBridgeSurface } from './messages-
|
|
3
|
-
export { C as CreateSrcdocOptions, c as createNonce, d as createSession, e as createSrcdoc, f as isValidSession } from './createSrcdoc-
|
|
1
|
+
import { f as BridgeHostEventType, e as BridgeEventMap, t as ToastKind, L as LogLevel, l as BridgeSurface, T as ThemeState, S as SlotState, M as MemberState, m as BridgeTranslations } from './messages-BjpZyiLy.js';
|
|
2
|
+
export { a as BRIDGE_SURFACES, I as IFRAME_ATTRIBUTES, w as isBridgeSurface } from './messages-BjpZyiLy.js';
|
|
3
|
+
export { C as CreateSrcdocOptions, c as createNonce, d as createSession, e as createSrcdoc, f as isValidSession } from './createSrcdoc-DgQyvIjG.js';
|
|
4
4
|
|
|
5
5
|
/** 호스트 fetch 정책. */
|
|
6
6
|
interface BridgePolicy {
|
package/dist/host.js
CHANGED
|
@@ -32,8 +32,8 @@ import {
|
|
|
32
32
|
prepareFetch,
|
|
33
33
|
resolveDestination,
|
|
34
34
|
resolvePolicy
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import "./chunk-
|
|
35
|
+
} from "./chunk-ND2HFYBP.js";
|
|
36
|
+
import "./chunk-CSAW3L7P.js";
|
|
37
37
|
|
|
38
38
|
// src/host/rateLimiter.ts
|
|
39
39
|
var DEFAULT_WINDOW_MS = 6e4;
|
|
@@ -115,6 +115,7 @@ function buildInit(init, hostOrigin, partnerOrigins, policy) {
|
|
|
115
115
|
spaceId: String(init.spaceId),
|
|
116
116
|
hostOrigin,
|
|
117
117
|
gatewayBase: `${hostOrigin}/gw`,
|
|
118
|
+
gatewayPrefixes: policy.gatewayPrefixes,
|
|
118
119
|
locale: String(init.locale),
|
|
119
120
|
theme: sanitizeTheme(init.theme),
|
|
120
121
|
...slot ? { slot } : {},
|
package/dist/index.cjs
CHANGED
|
@@ -496,6 +496,126 @@ function createBridgeFetch(client) {
|
|
|
496
496
|
return bridgeFetch;
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
+
// src/frame/directFetch.ts
|
|
500
|
+
function installDirectFetch(g, client, config) {
|
|
501
|
+
const hostOrigin = config.hostOrigin;
|
|
502
|
+
const callFn = Function.prototype.call;
|
|
503
|
+
const NativeURL = g.URL;
|
|
504
|
+
const rawFetch = g.fetch;
|
|
505
|
+
const nativeFetch = typeof rawFetch === "function" ? callFn.bind(rawFetch) : void 0;
|
|
506
|
+
const bridgeFetch = client.fetch;
|
|
507
|
+
const strSlice = callFn.bind(String.prototype.slice);
|
|
508
|
+
const promiseThen = callFn.bind(Promise.prototype.then);
|
|
509
|
+
const promiseReject = Promise.reject.bind(Promise);
|
|
510
|
+
const assign = Object.assign;
|
|
511
|
+
const TypeErr = TypeError;
|
|
512
|
+
function getter(ctor, name) {
|
|
513
|
+
if (typeof ctor !== "function") return void 0;
|
|
514
|
+
let proto = ctor.prototype;
|
|
515
|
+
while (proto) {
|
|
516
|
+
const desc = Object.getOwnPropertyDescriptor(proto, name);
|
|
517
|
+
if (desc && typeof desc.get === "function") {
|
|
518
|
+
return callFn.bind(desc.get);
|
|
519
|
+
}
|
|
520
|
+
proto = Object.getPrototypeOf(proto);
|
|
521
|
+
}
|
|
522
|
+
return void 0;
|
|
523
|
+
}
|
|
524
|
+
const getHref = getter(NativeURL, "href");
|
|
525
|
+
const getOrigin = getter(NativeURL, "origin");
|
|
526
|
+
const getProtocol = getter(NativeURL, "protocol");
|
|
527
|
+
const getPathname = getter(NativeURL, "pathname");
|
|
528
|
+
const getRequestUrl = getter(g.Request, "url");
|
|
529
|
+
let gatewayPrefixes = null;
|
|
530
|
+
let partnerOrigins = [];
|
|
531
|
+
let routesReady = false;
|
|
532
|
+
function copyStrings(value) {
|
|
533
|
+
if (!Array.isArray(value)) return null;
|
|
534
|
+
const out = [];
|
|
535
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
536
|
+
if (typeof value[i] === "string") out[out.length] = value[i];
|
|
537
|
+
}
|
|
538
|
+
return out;
|
|
539
|
+
}
|
|
540
|
+
function takeSnapshot(init) {
|
|
541
|
+
if (routesReady) return;
|
|
542
|
+
const raw = init;
|
|
543
|
+
gatewayPrefixes = copyStrings(raw.gatewayPrefixes);
|
|
544
|
+
partnerOrigins = copyStrings(raw.partnerOrigins) || [];
|
|
545
|
+
routesReady = true;
|
|
546
|
+
}
|
|
547
|
+
promiseThen(client.ready, takeSnapshot);
|
|
548
|
+
function matchesPrefix(pathname, prefix) {
|
|
549
|
+
const bare = strSlice(prefix, prefix.length - 1) === "/" ? strSlice(prefix, 0, -1) : prefix;
|
|
550
|
+
return pathname === bare || strSlice(pathname, 0, bare.length + 1) === bare + "/";
|
|
551
|
+
}
|
|
552
|
+
function routeOf(origin, protocol, pathname) {
|
|
553
|
+
if (origin === hostOrigin) {
|
|
554
|
+
if (gatewayPrefixes === null) return "bridge";
|
|
555
|
+
for (let i = 0; i < gatewayPrefixes.length; i += 1) {
|
|
556
|
+
if (matchesPrefix(pathname, gatewayPrefixes[i])) return "bridge";
|
|
557
|
+
}
|
|
558
|
+
return "reject";
|
|
559
|
+
}
|
|
560
|
+
for (let i = 0; i < partnerOrigins.length; i += 1) {
|
|
561
|
+
if (origin === partnerOrigins[i]) return "bridge";
|
|
562
|
+
}
|
|
563
|
+
return protocol === "https:" ? "direct" : "reject";
|
|
564
|
+
}
|
|
565
|
+
function requestUrlOf(input) {
|
|
566
|
+
if (!getRequestUrl || typeof input !== "object" || input === null) return void 0;
|
|
567
|
+
try {
|
|
568
|
+
return getRequestUrl(input);
|
|
569
|
+
} catch {
|
|
570
|
+
return void 0;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function dispatch(input, init) {
|
|
574
|
+
if (typeof NativeURL !== "function" || !getHref || !getOrigin || !getProtocol || !getPathname) {
|
|
575
|
+
return promiseReject(new TypeErr("bstage fetch: URL is unavailable"));
|
|
576
|
+
}
|
|
577
|
+
const requestUrl = requestUrlOf(input);
|
|
578
|
+
const request = requestUrl !== void 0;
|
|
579
|
+
let parsed;
|
|
580
|
+
try {
|
|
581
|
+
parsed = new NativeURL(request ? requestUrl : String(input), hostOrigin);
|
|
582
|
+
} catch {
|
|
583
|
+
return promiseReject(new TypeErr("bstage fetch: invalid url"));
|
|
584
|
+
}
|
|
585
|
+
const href = getHref(parsed);
|
|
586
|
+
const route = routeOf(getOrigin(parsed), getProtocol(parsed), getPathname(parsed));
|
|
587
|
+
const target = request ? input : href;
|
|
588
|
+
if (route === "bridge") return bridgeFetch(target, init);
|
|
589
|
+
if (route === "reject") {
|
|
590
|
+
return promiseReject(new TypeErr("bstage fetch: destination not allowed"));
|
|
591
|
+
}
|
|
592
|
+
if (!nativeFetch) return promiseReject(new TypeErr("bstage fetch: fetch is unavailable"));
|
|
593
|
+
return nativeFetch(
|
|
594
|
+
g,
|
|
595
|
+
target,
|
|
596
|
+
assign({}, init, { credentials: "omit", referrerPolicy: "no-referrer" })
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
function bstageFetch(input, init) {
|
|
600
|
+
try {
|
|
601
|
+
if (routesReady) return dispatch(input, init);
|
|
602
|
+
return promiseThen(client.ready, function(value) {
|
|
603
|
+
takeSnapshot(value);
|
|
604
|
+
return dispatch(input, init);
|
|
605
|
+
});
|
|
606
|
+
} catch {
|
|
607
|
+
return promiseReject(new TypeErr("bstage fetch: request failed"));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
Object.defineProperty(g, "__bstage_fetch__", {
|
|
611
|
+
value: bstageFetch,
|
|
612
|
+
writable: false,
|
|
613
|
+
configurable: false,
|
|
614
|
+
enumerable: false
|
|
615
|
+
});
|
|
616
|
+
return bstageFetch;
|
|
617
|
+
}
|
|
618
|
+
|
|
499
619
|
// src/srcdoc/escape.ts
|
|
500
620
|
var HTML_ESCAPES = {
|
|
501
621
|
"&": "&",
|
|
@@ -636,7 +756,7 @@ function buildFrameCsp(nonce, assetOrigin, mediaOrigins = []) {
|
|
|
636
756
|
`img-src ${media} data: blob:`,
|
|
637
757
|
`font-src ${assetOrigin} data:`,
|
|
638
758
|
`media-src ${media} blob:`,
|
|
639
|
-
"connect-src
|
|
759
|
+
"connect-src https: wss:",
|
|
640
760
|
"frame-src 'none'",
|
|
641
761
|
"form-action 'none'",
|
|
642
762
|
"base-uri 'none'"
|
|
@@ -653,6 +773,9 @@ function bootstrapScript(o) {
|
|
|
653
773
|
`var bridgeRuntime = ${bridgeRuntime.toString()};`,
|
|
654
774
|
`var createBridgeFetch = ${createBridgeFetch.toString()};`,
|
|
655
775
|
"var client = bridgeRuntime(globalThis, createBridgeFetch, { session: SESSION, hostOrigin: HOST_ORIGIN });",
|
|
776
|
+
// 라우터는 항상 심는다. ready.then이 엔트리 로드(아래 client.ready.then)보다 먼저 등록돼야 템플릿보다 먼저 스냅샷을 잡는다.
|
|
777
|
+
`var installDirectFetch = ${installDirectFetch.toString()};`,
|
|
778
|
+
"installDirectFetch(globalThis, client, { hostOrigin: HOST_ORIGIN });",
|
|
656
779
|
"var lastHeight = -1;",
|
|
657
780
|
"var scheduled = false;",
|
|
658
781
|
"function reportHeight() {",
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { B as BRIDGE_ERROR_CODES, a as BRIDGE_SURFACES, b as BridgeError, c as BridgeErrorCode, d as BridgeErrorLike, e as BridgeEventMap, f as BridgeHostEventType, g as BridgeInit, h as BridgeLimits, i as BridgeRequestMap, j as BridgeRequestType, k as BridgeResult, l as BridgeSurface, m as BridgeTranslations, F as FRAME_UNLOAD_EVENT, n as FetchRequest, o as FetchResult, H as HOST_EVENT_TYPES, I as IFRAME_ATTRIBUTES, p as INIT_RESPONSE, L as LogLevel, M as MemberState, R as READY_EVENT, q as REQUEST_TIMEOUT_MS, r as REQUEST_TYPES, S as SlotState, s as StorageOp, T as ThemeState, t as ToastKind, W as WithSession, u as isBridgeErrorCode, v as isBridgeErrorLike, w as isBridgeSurface, x as isHostEventType, y as isRequestType } from './messages-
|
|
2
|
-
export { C as CreateSrcdocOptions, M as MAX_ELEMENT_NAME_LENGTH, a as MAX_MEDIA_ORIGINS, b as buildFrameCsp, c as createNonce, d as createSession, e as createSrcdoc, i as isValidNonce, f as isValidSession } from './createSrcdoc-
|
|
1
|
+
export { B as BRIDGE_ERROR_CODES, a as BRIDGE_SURFACES, b as BridgeError, c as BridgeErrorCode, d as BridgeErrorLike, e as BridgeEventMap, f as BridgeHostEventType, g as BridgeInit, h as BridgeLimits, i as BridgeRequestMap, j as BridgeRequestType, k as BridgeResult, l as BridgeSurface, m as BridgeTranslations, F as FRAME_UNLOAD_EVENT, n as FetchRequest, o as FetchResult, H as HOST_EVENT_TYPES, I as IFRAME_ATTRIBUTES, p as INIT_RESPONSE, L as LogLevel, M as MemberState, R as READY_EVENT, q as REQUEST_TIMEOUT_MS, r as REQUEST_TYPES, S as SlotState, s as StorageOp, T as ThemeState, t as ToastKind, W as WithSession, u as isBridgeErrorCode, v as isBridgeErrorLike, w as isBridgeSurface, x as isHostEventType, y as isRequestType } from './messages-BjpZyiLy.cjs';
|
|
2
|
+
export { C as CreateSrcdocOptions, M as MAX_ELEMENT_NAME_LENGTH, a as MAX_MEDIA_ORIGINS, b as buildFrameCsp, c as createNonce, d as createSession, e as createSrcdoc, i as isValidNonce, f as isValidSession } from './createSrcdoc-DgQyvIjG.cjs';
|
|
3
3
|
|
|
4
4
|
/** 봉투 버전. 형식이 하위 호환되지 않게 바뀔 때만 올린다. */
|
|
5
5
|
declare const BRIDGE_VERSION = 1;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { B as BRIDGE_ERROR_CODES, a as BRIDGE_SURFACES, b as BridgeError, c as BridgeErrorCode, d as BridgeErrorLike, e as BridgeEventMap, f as BridgeHostEventType, g as BridgeInit, h as BridgeLimits, i as BridgeRequestMap, j as BridgeRequestType, k as BridgeResult, l as BridgeSurface, m as BridgeTranslations, F as FRAME_UNLOAD_EVENT, n as FetchRequest, o as FetchResult, H as HOST_EVENT_TYPES, I as IFRAME_ATTRIBUTES, p as INIT_RESPONSE, L as LogLevel, M as MemberState, R as READY_EVENT, q as REQUEST_TIMEOUT_MS, r as REQUEST_TYPES, S as SlotState, s as StorageOp, T as ThemeState, t as ToastKind, W as WithSession, u as isBridgeErrorCode, v as isBridgeErrorLike, w as isBridgeSurface, x as isHostEventType, y as isRequestType } from './messages-
|
|
2
|
-
export { C as CreateSrcdocOptions, M as MAX_ELEMENT_NAME_LENGTH, a as MAX_MEDIA_ORIGINS, b as buildFrameCsp, c as createNonce, d as createSession, e as createSrcdoc, i as isValidNonce, f as isValidSession } from './createSrcdoc-
|
|
1
|
+
export { B as BRIDGE_ERROR_CODES, a as BRIDGE_SURFACES, b as BridgeError, c as BridgeErrorCode, d as BridgeErrorLike, e as BridgeEventMap, f as BridgeHostEventType, g as BridgeInit, h as BridgeLimits, i as BridgeRequestMap, j as BridgeRequestType, k as BridgeResult, l as BridgeSurface, m as BridgeTranslations, F as FRAME_UNLOAD_EVENT, n as FetchRequest, o as FetchResult, H as HOST_EVENT_TYPES, I as IFRAME_ATTRIBUTES, p as INIT_RESPONSE, L as LogLevel, M as MemberState, R as READY_EVENT, q as REQUEST_TIMEOUT_MS, r as REQUEST_TYPES, S as SlotState, s as StorageOp, T as ThemeState, t as ToastKind, W as WithSession, u as isBridgeErrorCode, v as isBridgeErrorLike, w as isBridgeSurface, x as isHostEventType, y as isRequestType } from './messages-BjpZyiLy.js';
|
|
2
|
+
export { C as CreateSrcdocOptions, M as MAX_ELEMENT_NAME_LENGTH, a as MAX_MEDIA_ORIGINS, b as buildFrameCsp, c as createNonce, d as createSession, e as createSrcdoc, i as isValidNonce, f as isValidSession } from './createSrcdoc-DgQyvIjG.js';
|
|
3
3
|
|
|
4
4
|
/** 봉투 버전. 형식이 하위 호환되지 않게 바뀔 때만 올린다. */
|
|
5
5
|
declare const BRIDGE_VERSION = 1;
|
package/dist/index.js
CHANGED
|
@@ -70,6 +70,12 @@ interface BridgeInit {
|
|
|
70
70
|
readonly spaceId: string;
|
|
71
71
|
readonly hostOrigin: string;
|
|
72
72
|
readonly gatewayBase: string;
|
|
73
|
+
/**
|
|
74
|
+
* 호스트 정책의 같은 origin 게이트웨이 경로 접두사(`policy.gatewayPrefixes`). 프레임
|
|
75
|
+
* 라우터(`__bstage_fetch__`)가 "브리지로 보낼 같은 origin 경로"를 가르는 데 쓴다. 판정의 정본은 여전히 호스트다.
|
|
76
|
+
* 이 필드가 없는 옛 호스트면 라우터는 hostOrigin 요청을 모두 브리지로 보낸다.
|
|
77
|
+
*/
|
|
78
|
+
readonly gatewayPrefixes?: readonly string[];
|
|
73
79
|
readonly locale: string;
|
|
74
80
|
readonly theme: ThemeState;
|
|
75
81
|
readonly slot?: SlotState;
|
|
@@ -70,6 +70,12 @@ interface BridgeInit {
|
|
|
70
70
|
readonly spaceId: string;
|
|
71
71
|
readonly hostOrigin: string;
|
|
72
72
|
readonly gatewayBase: string;
|
|
73
|
+
/**
|
|
74
|
+
* 호스트 정책의 같은 origin 게이트웨이 경로 접두사(`policy.gatewayPrefixes`). 프레임
|
|
75
|
+
* 라우터(`__bstage_fetch__`)가 "브리지로 보낼 같은 origin 경로"를 가르는 데 쓴다. 판정의 정본은 여전히 호스트다.
|
|
76
|
+
* 이 필드가 없는 옛 호스트면 라우터는 hostOrigin 요청을 모두 브리지로 보낸다.
|
|
77
|
+
*/
|
|
78
|
+
readonly gatewayPrefixes?: readonly string[];
|
|
73
79
|
readonly locale: string;
|
|
74
80
|
readonly theme: ThemeState;
|
|
75
81
|
readonly slot?: SlotState;
|